実を言うと、私は通常、これらの単純な関数のコードを読むのではなく、型を読んで関数を自分で書く方が簡単だと思っています。パズルだと思ってください。あなたはこれを構築しようとしています:
mapFree :: Functor f => (a -> b) -> Free f a -> Free f b
では、どうすればよいのでしょうか。Pure
さて、最初にコンストラクターを取りましょう:
mapFree f (Pure a) = ...
-- I like to write comments like these while using Haskell, then usually delete
-- them by the end:
--
-- f :: a -> b
-- a :: a
そこに 2 つの型のコメントがあり、 の型がわかれPure
ば、すぐに解決策がわかるはずです。
mapFree f (Pure a) = Pure (f a)
2番目のケース:
mapFree f (Free fa) = ...
-- f :: a -> b
-- fa :: Functor f => f (Free f a)
f
は であるため、Functor
実際に を使用して の内部コンポーネントにmapFree
適用できます。したがって、次のようになります。mapFree f
f (Free f a)
mapFree f (Free fa) = Free (fmap (mapFree f) fa)
この定義をFunctor f => Functor (Free f)
インスタンスとして使用すると、次のようになります。
instance Functor f => Functor (Free f) where
fmap f (Pure a) = Pure (f a)
fmap f (Free fa) = Free (fmap (fmap f) fa)
ちょっとした作業で、ここでたどり着いた定義が、あなたが困惑しているものと同じものであることを確認できます。(他の人が述べたように、(<$>)
(defined in Control.Applicative
) は の同義語にすぎfmap
ません。) まだ理解していないかもしれませんが、なんとか記述できました。これは、これらのような抽象的な型の場合、非常に多くの場合十分です。
しかし、それを理解するのに役立つのは、次のことです。モナドを、 asとasFree
を使用した一種のリストのような構造と考えてください。型の定義から、これがわかるはずです:は基本ケースであり、再帰ケースです。インスタンスが行っているのは、マップされた関数をこの構造の一番下、つまり が存在する場所に「プッシュ」することです。 Pure
[]
Free
(:)
Pure
Free
fmap
Pure