divMod のデフォルト実装がエラーしたことで発覚。
where (p, q) = (1, 2)
のような束縛がうまくいかない。
where (,) p q = (1, 2)
でもだめ。それぞれにテストケースを用意した (pairbind2, pairbind)
左辺がコンストラクタのパターンの場合(これに関する記述は、 Haskell 2010 Language Report のどこにあるんだろ *1)には、 複数の binding に変換してやればいいように思われる:
C a b c ... = rhs ⇒ a = let e = rhs in case e of C x y z ... -> x b = let e = rhs in case e of C x y z ... -> y c = let e = rhs in case e of C x y z ... -> z
(この方針で patbind.hs を手で変換したのが patbind2.hs)
現状の実装では、renDecl や、そこから呼ばれている renFExp が、 関数名が isConName である場合を考慮していない。
およそ、以下のようなことをやっていくべき。
この変換は as-pattern にいったん直してから、as-pattern として扱った方がいいような気がしてきた。
つまり、C a b c ...= rhs は、いったん e@(C a b c ...) = rhs に変換してから、以下の変換を施す:
e@(C a b c ...) = rhs ⇒ e = rhs a = case e of C x y z ... -> x b = case e of C x y z ... -> y c = case e of C x y z ... -> z
まずは As-pattern に対処してから、上述のように、パターンを as-pattern に変換する処理を追加したい。なので、まずは、明にかかれた As-pattern のケースを2つ追加した。
3, 4 の順に対処していけると思う。
scanValueDecl2 で、AsPat に対する限定的な対処をいれて、pairbind3, pairbind4 のケースに対しては As pattern から上述のような変換をするようにした。
ところが、その後でエラー。もともとは、TupleExp も FunAppExp に変換して統一的に扱うつもりだったが、調査のため、現状はわけて処理している。
pairbind3.hs の方は、findCMs failed: "Prelude.(,)" , pairbind4.hs の方は、dictionary not found: ("Prelude.Show",Tyvar "v1865" Star,[]) となる。
pairbind4.hs と同様の変換をあらかじめ手でかいておいた、pairbind4b.hs でも同じく dictionary not found: ("Prelude.Show",Tyvar "v1865" Star,[]) となるため、今回追加した変換に問題があるのではなく、辞書渡し変換の潜在的な障害だと思われる。
なお、pairbind4b.hs で、x に型注釈をつける (pairbind4b.hs) と通る。
pairbind4.hs に関して、この現象が再現する最小コードがどうなるか考えてみた。 以下の形にまで変形しても、エラーは再現した:
main = do print p where x = (1, 2) p = case x of (a, b) -> a
つまり、Rename に追加した変換に問題があるのではなく、期待通り変換しても型変換に失敗しているということ。
なお、case 式に x ではなく、(1, 2) を直接与えるようにしたら通る:
main = do print p where p = case (1, 2) of (a, b) -> a
それぞれの core0 を示す。まず、うまくいかない方(case に x を与えた方):
---- ddumpCore ---- (Main.main :: (Prelude.IO ())) = let (Main.l1.l0.p :: v1873) = let (Main.l1.l0.l1.l0.F :: (((Prelude.(,) t6) t7) -> t6)) = \(_Main.l1.l0.l1.l0.F.U1 :: ((Prelude.(,) t0) t1)) -> case (_Main.l1.l0.l1.l0.F.U1 :: ((Prelude.(,) t0) t1)) (_Main.l1.l0.l1.l0.F.U1b :: ((Prelude.(,) t0) t1)) of Prelude.(,) (_Main.l1.l0.l1.l0.F.U2 :: t2) (_Main.l1.l0.l1.l0.F.U3 :: t3) :: (t2 -> (t3 -> ((Prelude.(,) t2) t3))) -> (_Main.l1.l0.l1.l0.F.U2 :: t2) in ((Main.l1.l0.l1.l0.F :: (((Prelude.(,) t8) t9) -> t8)) (Main.l1.l0.x :: ([Prelude.Num v1860,Prelude.Num v1861] :=> ((Prelude.(,) v1860) v1861)))) (Main.l1.l0.x :: ([Prelude.Num v1860,Prelude.Num v1861] :=> ((Prelude.(,) v1860) v1861))) = (((Prelude.(,) :: (t14 -> (t15 -> ((Prelude.(,) t14) t15)))) ((Prelude.fromInteger :: ([Prelude.Num t16] :=> (Prelude.Integer -> t16))) (1 :: Prelude.Integer))) ((Prelude.fromInteger :: ([Prelude.Num t17] :=> (Prelude.Integer -> t17))) (2 :: Prelude.Integer))) in ((Prelude.print :: ([Prelude.Show t20] :=> (t20 -> (Prelude.IO ())))) (Main.l1.l0.p :: v1873))
つぎに、うまくいった方(case に (1, 2) を直接書いた方):
---- ddumpCore ---- (Main.main :: (Prelude.IO ())) = let (Main.l1.l0.p :: ([Prelude.Num v1866] :=> v1866)) = let (Main.l1.l0.l0.l0.F :: (((Prelude.(,) t7) t8) -> t7)) = \(_Main.l1.l0.l0.l0.F.U1 :: ((Prelude.(,) t1) t2)) -> case (_Main.l1.l0.l0.l0.F.U1 :: ((Prelude.(,) t1) t2)) (_Main.l1.l0.l0.l0.F.U1b :: ((Prelude.(,) t1) t2)) of Prelude.(,) (_Main.l1.l0.l0.l0.F.U2 :: t3) (_Main.l1.l0.l0.l0.F.U3 :: t4) :: (t3 -> (t4 -> ((Prelude.(,) t3) t4))) -> (_Main.l1.l0.l0.l0.F.U2 :: t3) in ((Main.l1.l0.l0.l0.F :: (((Prelude.(,) t9) t10) -> t9)) (((Prelude.(,) :: (t11 -> (t12 -> ((Prelude.(,) t11) t12)))) ((Prelude.fromInteger :: ([Prelude.Num t13] :=> (Prelude.Integer -> t13))) (1 :: Prelude.Integer))) ((Prelude.fromInteger :: ([Prelude.Num t14] :=> (Prelude.Integer -> t14))) (2 :: Prelude.Integer)))) in ((Prelude.print :: ([Prelude.Show t16] :=> (t16 -> (Prelude.IO ())))) (Main.l1.l0.p :: ([Prelude.Num v1866] :=> v1866)))
Main.l1.l0.p の型がちがうなぁ。 これは、Assump (Typing の出力)時点ですでに異なっているのだが、 Thih 自体が間違っているとは思えない(これまでそんなことは一度もなかった)。 そういえば、case 式を Typing に与えるとき、自前でなんかしてたかなぁ…。
そのあたりが怪しい?
ひとまず、case 式に直接右辺の式をあたえる (p = case (1, 2) of ... にする) ことで問題を回避し、as-pattern の実装はすすめることにしたい。 根本解決になっていないし、 右辺が複雑な場合(ガードがあったり、where 節があったり)には、対応できないのだが。
この問題は切り出して、別 issue とする。
pairbind3.hs でエラーする (findCMs failed) 原因は、Predefined における漏れだった。 以下のように primConsMem の宣言に pairCfun を追加することで解決:
--- a/compiler/src/PreDefined.hs +++ b/compiler/src/PreDefined.hs @@ -199,7 +199,7 @@ overloadedCfun = "#overloaded#" :>: Forall [Star, Star] ([] :=> (TGen 0 `fn` tString `fn` TGen 1)) primConsMems :: [Assump] -primConsMems = [ unitCfun, nilCfun, consCfun +primConsMems = [ unitCfun, nilCfun, consCfun, pairCfun
上記の対処にくわえて、コンストラクタ適用式を As-pattern に変換する処理を加えることで、 本 issue で想定していた testcases はクリア。