Swiftを書いていらっしゃる方なら、型推論が弱くて困る経験をされたことがあるかもしれません。
Expression was too complex to be solved in reasonable time
~~最近↑に愛着湧いてきました。~~嘘です。
気になったので、Swiftに関数型の型推論を少しずつ食わせていってどこで潰れるか少しだけ試してみることにします。
実験道具
さっきわたしが拾ってきたものを使う。
- App StoreにあったXcode7.2(7C68)
- Argo v2.2.0
Argoはjsonのパースをするものである。結果が enumの Decoded<T>
であり、functor( <^>
)とapplicative( <*>
)と、それから map
/ flatMap
を使うと楽しい。
詳しい話は色々な方がすでにされているので省略。
題材
次のようなものをつくってビルドが通るか試す。
struct Hoge {
let args0: String
let args1: String
//略
let argsN: String
static func create(args0: String)(args1: String)/*以下略*/(argsN: String) -> Hoge {
return Hoge(args0: args0, args1: args1, /*以下略*/, argsN: argsN)
}
}
extension Hoge: Decodable {
static func decode(json: JSON) -> Decoded<Hoge> {
return create
<^> json <| "args0" // Functor
<*> json <| "args1" // Applicative
// 以下ずっとApplicative
<*> json <| "argsN"
}
}
実験開始
https://github.com/S-Shimotori/SwiftTypeInference
紙面の都合上省略して掲載します
ひたすらApplicative
↑に let argsN: String
を増やすだけ。
引数14個までOK。15個目で力尽きた。
mapとApplicative
↑の json <| "argsN"
を全部 (json <|? "argsN").map { $0 ?? "default" }
に変えて、やはり増やす。
引数13個までOK。Applicativeのみの時と比べると1個減っただけだが、ビルドにものすごく時間がかかる。
AlternativeとApplicative
map
ではなくAlternative( <|>
)でやってみる。
json <| "argsN" <|> json <| "argsN_1"
<|>
の右が pure("")
であるぶんには14個まで行けるが、 <|
だと7個まで。
数が半減した割には map
より処理も諦めも早い。
入れ子状態
次のようなものを増やす。
struct Hoge {
struct Nested {
let args0: String
let args1: String
static func create(args0: String)(args1: String) -> Nested {
return Nested(args0: args0, args1: args1)
}
}
}
でもって引数の型を Hoge.Nested
にし、 json <| "argsN"
を
(Hoge.Nested.create <^> json <| ["nestedN", "args0"] <*> json <| ["nestedN", "args1"])
に変えてみる。
6個までOK。
まとめ
まあこんなもんですよね。
thoughtbot, inc.はCurryも出してるけど、到底アレは使い切れない。
個人的な感想
今年はついにオープンソース化されたし、来年は色々カンファレンスも控えてワクワクしますね。
わたしはいままで初心者本レベルのプログラミングしかしてなかったし、それもCやJavaといった既に安定した言語だったので、__発表→β版→仕様変更の嵐__という時代の流れを楽しみながらやってます。Swiftは安定してないからとまだ様子見の方も多いでしょうが、そういう楽しみ方も人生に1回くらい……なんて。