再帰型に関連した変態は、機械的に導き出すことができます。
あなたが再帰的に定義された型を持ち、複数のコンストラクタを持ち、それぞれが独自のアリティを持つとします。私はOPの例を借ります。
data X a b f = A Int b
| B
| C (f a) (X a b f)
| D a
その後、我々はすべてをuncurrying、一つで各アリティを強制することにより、同じタイプを書き換えることができます。ユニットタイプ()
を追加すると、Arity zero(B
)が1になります。
data X a b f = A (Int, b)
| B()
| C (f a, X a b f)
| D a
その後、我々は代わりに複数のコンストラクタのEither
を利用し、1にコンストラクタの数を減らすことができます。以下では、簡略化のために、Either
の代わりに+
というインフィクスを書きます。用語レベルで
data X a b f = X ((Int, b) +() + (f a, X a b f) + a)
、我々は、明示的な固定小数点式x = f x
を書いて、フォームx = f x where f w = ...
として任意の再帰的定義 を書き換えることができます知っています。タイプレベルでは、refectorの再帰型に対して同じメソッド を使用できます。
data X a b f = X (F (X a b f)) -- fixed point equation
data F a b f w = F ((Int, b) +() + (f a, w) + a)
ここで、ファンクタインスタンスを自動作成できることに注意してください。
deriving instance Functor (F a b f)
オリジナルタイプに各再帰参照のみ正位置で発生したため、これが可能です。これが成り立たなければ、F a b f
をファンクタではなく、私たちは異型性を持つことができません。
cata :: (F a b f w -> w) -> X a b f -> w
これはOPのxCata
タイプです:次のように
最後に、我々はcata
の種類を書き込むことができますか?そうです。我々は、いくつかの型同型写像を適用すればよい。 1
としてユニット()
、我々は製品a*b
として(a,b)
を書くならば、それはこれらの同型写像を覚えておくのは簡単です、ちなみに
1) (a,b) -> c ~= a -> b -> c (currying)
2) (a+b) -> c ~= (a -> c, b -> c)
3)() -> c ~= c
、および電源b^a
などa->b
:私たちは、次の代数法則を使用しています。実際には1) c^(a*b) = (c^a)^b , 2) c^(a+b) = c^a*c^b, 3) c^1 = c
になります。とにかく
、唯一
F a b f w -> w
=~ (def F)
((Int, b) +() + (f a, w) + a) -> w
=~ (2)
((Int, b) -> w,() -> w, (f a, w) -> w, a -> w)
=~ (3)
((Int, b) -> w, w, (f a, w) -> w, a -> w)
=~ (1)
(Int -> b -> w, w, f a -> w -> w, a -> w)
さんは今、完全な形を考えてみましょう、のは、F a b f w -> w
一部を書き換えるために始めましょう:(w=r
の名前を変更する)確かにある
cata :: (F a b f w -> w) -> X a b f -> w
~= (above)
(Int -> b -> w, w, f a -> w -> w, a -> w) -> X a b f -> w
~= (1)
(Int -> b -> w)
-> w
-> (f a -> w -> w)
-> (a -> w)
-> X a b f
-> w
希望タイプ
xCata :: (Int -> b -> r)
-> r
-> (f a -> r -> r)
-> (a -> r)
-> X a b f
-> r
の「標準」実装は
cata g = wrap . fmap (cata g) . unwrap
where unwrap (X y) = y
wrap y = X y
であることは、その一般に理解するためにいくつかの努力がかかりますが、これは確かに意図したものです。
自動化について:はい、これは少なくとも部分的には自動化できます。 1は
type X a b f = Fix (F a f b)
data F a b f w = ... -- you can use the actual constructors here
deriving Functor
-- use cata here
のようなものを書くことができますhackageのパッケージrecursion-schemes
があります。例:
import Data.Functor.Foldable hiding (Nil, Cons)
data ListF a k = NilF | ConsF a k deriving Functor
type List a = Fix (ListF a)
-- helper patterns, so that we can avoid to match the Fix
-- newtype constructor explicitly
pattern Nil = Fix NilF
pattern Cons a as = Fix (ConsF a as)
-- normal recursion
sumList1 :: Num a => List a -> a
sumList1 Nil = 0
sumList1 (Cons a as) = a + sumList1 as
-- with cata
sumList2 :: forall a. Num a => List a -> a
sumList2 = cata h
where
h :: ListF a a -> a
h NilF = 0
h (ConsF a s) = a + s
-- with LambdaCase
sumList3 :: Num a => List a -> a
sumList3 = cata $ \case
NilF -> 0
ConsF a s -> a + s
はあなたのタイプの表現の作品を選び、B FORALL同型 '〜を適用します。 (a - > b) - > b '、voilà。 –
私はテンプレートHaskellの変態のためのジェネレータを書いています:https://github.com/KommuSoft/template-fun。すべての型宣言を網羅するわけではありませんが、それは基本的な問題ではありません。だからアフアイアの変態は自動的に得られる。 –
@BenjaminHodgson私はそれが私にとってはあまりにも軽すぎるのではないかと心配しています。私はその提案を全く理解していないと思う。 – amalloy