連結に関する素晴らしい真の事実は、方程式の2つの変数がわかっている場合です。
a ++ b = c
次に、3番目を知っています。
このアイデアを自分の連結に取り込んで、機能的な依存関係を使用したいと思います。
{-# Language DataKinds, GADTs, FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, PolyKinds, TypeOperators, UndecidableInstances #-}
import Data.Kind (Type)
class Concatable
(m :: k -> Type)
(as :: k)
(bs :: k)
(cs :: k)
| as bs -> cs
, as cs -> bs
, bs cs -> as
where
concat' :: m as -> m bs -> m cs
今私はそう異種のリストを想起させます:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
しかし、私Concatable
は問題があるのでこれらを宣言しようとすると
instance Concatable HList '[] bs bs where
concat' HEmpty bs = bs
instance
( Concatable HList as bs cs
)
=> Concatable HList (a ': as) bs (a ': cs)
where
concat' (HCons head tail) bs = HCons head (concat' tail bs)
3つ目の機能依存関係を満たしていません。むしろ、コンパイラは私たちがそうではないと信じています。これは、コンパイラが2番目のインスタンスではそうなる可能性があると考えているためですbs ~ (a ': cs)
。そして、それはそうである可能性がありConcatable as (a ': cs) cs
ます。
3つの依存関係がすべて満たされるようにインスタンスを調整するにはどうすればよいですか?
ルキが言ったことを拡大するために、私たちが知っている
—
カイ
bs
とを想像してください。確定的な方法でこれを行うには、単一のインスタンスにコミットし、そのレシピに従うことができると期待しています。具体的には、と仮定します。どのインスタンスを選択しますか?このようなことがあるでは、から来ている(と空です)。代わりに(空ではない)から来ている可能性もあり、後で再び表示されます。私たちは知るためにさらに深く掘り下げる必要があり、GHCはそれをしません。cs
as
bs = (Int ': bs2)
cs = (Int ': cs2)
Int
cs
bs
as
as
Int
cs
cs
非常に大まかに言えば、GHCはインスタンスからの誘導の単純な形式を使用して証明できるFundepsを受け入れます。ここで、そのうちの1つは、証明するために一種の二重帰納法を必要とする(またはそのように思われる)ため、コンパイラーはそれほど遠くに行きません。
—
カイ
bs cs -> as
。なぜなら、私たちはローカルではない情報を必要bs
とcs
しas
、それが短所かゼロかを決定する必要があるためです。この情報を表す方法を見つける必要があります。直接推論できない場合に保証するために、型シグネチャにどのコンテキストを追加しますか?