実を言うと、私は通常、これらの単純な関数のコードを読むのではなく、型を読んで関数を自分で書く方が簡単だと思っています。パズルだと思ってください。あなたはこれを構築しようとしています:
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 ff (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(:)PureFreefmapPure