最初に、私はいくつかの典型的なタイプレベルの自然数のものから始めました。
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
data Nat = Z | S Nat
type family Plus (n :: Nat) (m :: Nat) :: Nat
type instance Plus Z m = m
type instance Plus (S n) m = S (Plus n m)
そこで、n次元グリッドを表すデータ型を作成したいと思いました。(セルオートマトンの評価で見られるものの一般化はcomonadicです。)
data U (n :: Nat) x where
Point :: x -> U Z x
Dimension :: [U n x] -> U n x -> [U n x] -> U (S n) x
タイプは、グリッド内の特定のポイントに「焦点を合わせた」sの次元グリッドのU num x
タイプであるという考え方です。num
x
だから私はこれをコモナドにしたかったのですが、私が作ることができるこの潜在的に有用な関数があることに気づきました:
ufold :: (x -> U m r) -> U n x -> U (Plus n m) r
ufold f (Point x) = f x
ufold f (Dimension ls mid rs) =
Dimension (map (ufold f) ls) (ufold f mid) (map (ufold f) rs)
これで、このコンビネータに関して、m次元グリッドのn次元グリッドを(n + m)次元グリッドに変換する「次元結合」を実装できます。cojoin
これは、グリッドのグリッドを生成する結果を処理するときに便利です。
dimJoin :: U n (U m x) -> U (Plus n m) x
dimJoin = ufold id
ここまでは順調ですね。Functor
また、インスタンスは。で記述できることにも気づきましたufold
。
instance Functor (U n) where
fmap f = ufold (\x -> Point (f x))
ただし、これによりタイプエラーが発生します。
Couldn't match type `n' with `Plus n 'Z'
しかし、コピーパスタを作ると、タイプエラーはなくなります。
instance Functor (U n) where
fmap f (Point x) = Point (f x)
fmap f (Dimension ls mid rs) =
Dimension (map (fmap f) ls) (fmap f mid) (map (fmap f) rs)
コピーパスタの味が嫌いなので、これが私の質問です。に等しい型システムをどのように見分けることができPlus n Z
n
ますか?dimJoin
そして、キャッチはこれです:同様のタイプエラーを生成する原因となるタイプファミリーインスタンスに変更を加えることはできません。