完全に依存する連結を作成する


10

連結に関する素晴らしい真の事実は、方程式の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つの依存関係がすべて満たされるようにインスタンスを調整するにはどうすればよいですか?


1
重要な問題はのようですbs cs -> as。なぜなら、私たちはローカルではない情報を必要bscsas、それが短所かゼロかを決定する必要があるためです。この情報を表す方法を見つける必要があります。直接推論できない場合に保証するために、型シグネチャにどのコンテキストを追加しますか?
ルキ

3
ルキが言ったことを拡大するために、私たちが知っているbsとを想像してください。確定的な方法でこれを行うには、単一のインスタンスにコミットし、そのレシピに従うことができると期待しています。具体的には、と仮定します。どのインスタンスを選択しますか?このようなことがあるでは、から来ている(と空です)。代わりに(空ではない)から来ている可能性もあり、後で再び表示されます。私たちは知るためにさらに深く掘り下げる必要があり、GHCはそれをしません。csasbs = (Int ': bs2)cs = (Int ': cs2)IntcsbsasasIntcscs
カイ

1
非常に大まかに言えば、GHCはインスタンスからの誘導の単純な形式を使用して証明できるFundepsを受け入れます。ここで、そのうちの1つは、証明するために一種の二重帰納法を必要とする(またはそのように思われる)ため、コンパイラーはそれほど遠くに行きません。
カイ

回答:


10

したがって、コメントが示唆するように、GHCはそれ自体でそれを理解するつもりはありませんが、少しタイプレベルのプログラミングでそれを助けることができます。いくつか紹介しましょうTypeFamilies。これらの関数はすべて、リスト操作を型レベルに引き上げたかなり単純な翻訳です。

-- | This will produce the suffix of `cs` without `as`
type family DropPrefix (as :: [Type]) (cs :: [Type]) where
  DropPrefix '[] cs = cs
  DropPrefix (a ': as) (a ': cs) = DropPrefix as cs

-- Similar to the logic in the question itself: list concatenation. 
type family Concat (as :: [Type]) (bs :: [Type]) where
  Concat '[] bs = bs
  Concat (head ': tail) bs = head ': Concat tail bs

-- | Naive list reversal with help of concatenation.
type family Reverse (xs :: [Type]) where
  Reverse '[] = '[]
  Reverse (x ': xs) = Concat (Reverse xs) '[x]

-- | This will produce the prefix of `cs` without `bs`
type family DropSuffix (bs :: [Type]) (cs :: [Type]) where
  DropSuffix bs cs = Reverse (DropPrefix (Reverse bs) (Reverse cs))

-- | Same definition of `HList` as in the question
data HList (as :: [Type]) where
  HEmpty :: HList '[]
  HCons :: a -> HList as -> HList (a ': as)

-- | Definition of concatenation at the value level
concatHList :: (cs ~ Concat as bs) => HList as -> HList bs -> HList cs
concatHList HEmpty bs = bs
concatHList (HCons head tail) bs = HCons head (concatHList tail bs)

このツールを自由に使用すると、実際には1時間の目標を達成できますが、最初に、目的のプロパティを持つ関数を定義しましょう。

  • 推論する能力csからasしてbs
  • 推論する能力asからbsしてcs
  • 推論する能力bsからasしてcs

出来上がり:

concatH ::
     (cs ~ Concat as bs, bs ~ DropPrefix as cs, as ~ DropSuffix bs cs)
  => HList as
  -> HList bs
  -> HList cs
concatH = concatHList

それをテストしましょう:

foo :: HList '[Char, Bool]
foo = HCons 'a' (HCons True HEmpty)

bar :: HList '[Int]
bar = HCons (1 :: Int) HEmpty
λ> :t concatH foo bar
concatH foo bar :: HList '[Char, Bool, Int]
λ> :t concatH bar foo
concatH bar foo :: HList '[Int, Char, Bool]

そして最後に最終目標:

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

instance (cs ~ Concat as bs, bs ~ DropPrefix as cs, as ~ DropSuffix bs cs) =>
         Concatable HList as bs cs where
  concat' = concatH
λ> :t concat' HEmpty bar
concat' HEmpty bar :: HList '[Int]
λ> :t concat' foo bar
concat' foo bar :: HList '[Char, Bool, Int]
λ> :t concat' bar foo
concat' bar foo :: HList '[Int, Char, Bool]

3
よくやった!これは不可能かもしれないとさえ思ったが、あなたはそれを透明かつエレガントに解決した。
ルキ

ありがとう、@ luqui
lehins
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.