# 068: pairbind.hs で qname not found [↑up](bunny_notes) - issued: 2020-05-09 - 分類: A サンプルコードが fail - status: Closed (2020-05-14) ## 概要 divMod のデフォルト実装がエラーしたことで発覚。 $$
{
where (p, q) = (1, 2)
$$}
のような束縛がうまくいかない。
$${
where (,) p q = (1, 2)
$$}
でもだめ。それぞれにテストケースを用意した (pairbind2, pairbind)
## 調査ログ
### 2020-05-09
左辺がコンストラクタのパターンの場合(これに関する記述は、
Haskell 2010 Language Report のどこにあるんだろ
${thih の 11.6.3 にはこの変換に関する記載あり})には、
複数の 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 である場合を考慮していない。
およそ、以下のようなことをやっていくべき。
- 現状の renDecl, renFExp の動作を整理する
- InfixExp は fixity resolution をすませた上で、FunAppExp に変換して、統一的に処理するようにしたい
- その後、本件のケース(関数名が isConName だった場合)に対処する
また、関連 issue を2つたてた: ([069](bissue069), [070](bissue070))
### 2020-05-10
この変換は 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
$$}
### 2020-05-11
まずは As-pattern に対処してから、上述のように、パターンを as-pattern に変換する処理を追加したい。なので、まずは、明にかかれた As-pattern のケースを2つ追加した。
- pairbind3.hs
- pairbind4.hs
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) と通る。
### 2020-05-14
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](bissue073) とする。
### 調査継続
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 はクリア。
- pairbind : sample199
- pairbind2: sample200
- pairbind3: sample201
- pairbind4: sample202
- lsbind: sample203
- patbind2: samle204
本件ワークアラウンドへの正式対処は、別 issue [073](bissue073), [074](bissue074)
で継続することとして、本件はクローズ。