Functorってなに?
Functorとは「ある関数で値を写して新しい値を作る」型クラスらしい、数学的には「関手」とか言うらしいが、まぁ「写像」程度の理解度でいいのか。ApplicativeやらMonadやらでつまずくので、Functorは理解したい、なので...
Functor調べる
とりあえず、Functorクラスの情報を調べてみる、Functorとは型クラスでfmapって言う関数が定義されているPrelude> :i Functor
class Functor f where
fmap :: (a -> b) -> f a -> f b
(GHC.Base.<$) :: a -> f b -> f a
-- Defined in `GHC.Base'
instance Functor Maybe -- Defined in `Data.Maybe'
instance Functor [] -- Defined in `GHC.Base'
instance Functor IO -- Defined in `GHC.Base'
Prelude>
そしての「Maybe」とか「[]」がFunctorのインスタンスになっているのがわかる
fmapの使い方
実際に使ってみるPrelude> fmap (+10) [1,2,3,4,5] [11,12,13,14,15] Prelude> fmap (+10) (Just 20) Just 30 Prelude> fmap (+10) Nothing Nothing Prelude>
簡単な使い方はこんな感じ、fmapの動作は
- [] の場合、配列のそれぞれの要素に(+10)を適用して新しい配列[11,12,13,14,15]を返してくる
- Maybeの場合、Just (20 +(10))、要するにfmapがJustの中身に(+10)を適用して返してくる
「型aから型bに変換する関数に、型aのファンクタ値を適用して、型bのファンクタ値を生成する」
ってことか
自分の型をFunctorクラスのインスタンスにしてみる
実際に自分の型を作成してFunctorクラスのインスタンスにしてみる。ThisTypeデータ型を宣言してGHCiに読み込ませてみるFuncSample.hs
data ThisType a = ThisType a deriving(Eq, Show)
GHCiを起動してThisType型の動作を確認する
Prelude> :l FuncSample
[1 of 1] Compiling Main ( FuncSample.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i ThisType
data ThisType a = ThisType a -- Defined at FuncSample.hs:1:6
instance Eq a => Eq (ThisType a) -- Defined at FuncSample.hs:1:37
instance Show a => Show (ThisType a)
-- Defined at FuncSample.hs:1:41
*Main> :t ThisType
ThisType :: a -> ThisType a
*Main> ThisType 20
ThisType 20
*Main> ThisType "ABC"
ThisType "ABC"
*Main>
Functorとして使ってみると、まずThisType型がFunctor型クラスのインスタンスとして宣言されていないエラーと怒られる、なるほど、ではFunctorのインスタンスにしてみる
*Main> fmap (+10) (ThisType 20) <interactive>:19:1: No instance for (Functor ThisType) arising from a use of `fmap' Possible fix: add an instance declaration for (Functor ThisType) In the expression: fmap (+ 10) (ThisType 20) In an equation for `it': it = fmap (+ 10) (ThisType 20) *Main>
FuncSample.hsにinstanceを追加
data ThisType a = ThisType { getThisType :: a } deriving(Eq, Show) instance Functor ThisType where fmap f (ThisType x) = ThisType (f x) main = do let val1 = getThisType $ fmap (+10) (ThisType 20) val2 = getThisType $ fmap ("#A君"++) (ThisType "メリーゴーランドに乗る") putStrLn $ show(val1) putStrLn $ val2
もう一度同じ計算をやらせてみると
~ $ runhaskell FuncSample.hs 30 #A君メリーゴーランドに乗る ~ $
#A君もメリーゴーランドに乗れる
ThisTypeは型コンストラクタ
:k でThisTypeを調べてみると、ThisTypeは型引数を1つ取る型コンストラクタでMaybeとかと同じ事が分かります。Functor型クラスのインスタンスにしたい場合は型引数を1つ取る型コンストラクタでなければならないということ。*Main> *Main> :k ThisType ThisType :: * -> * *Main> :k Maybe Maybe :: * -> * *Main> :k [] [] :: * -> * *Main>
ThisTypeはFunctor則を満たしてるのか?
これが正確かどうか分からないがThisType型がFunctor則を満たしているか確認する以下のFunctor則の二つ
- 第1法則 fmap id = id
- 第2法則 fmap (f . g) x = fmap f (fmap g x)
第1法則 fmap id = id
fmap id = idを満たしているか確認する、多分これはカリー化されているので書き直すとこんな感じか
fmap id (f a) = id (f a) これは
*Main> fmap id (ThisType 20) == id (ThisType 20) True *Main>とりあえず満たしている
第2法則 fmap (f . g) x = fmap f (fmap g x)これは、関数fと関数gの合成関数とファンクター値にfmapを適用したものと、最初に関数gとfmapを適用し生成したファンクター値をさらに関数fでfmapしたファンクター値が等しくなればいいということ
f = (+10)、g = (+50)として試してみる
*Main> let f=(+10) *Main> let g=(+50) *Main> fmap (f . g) (ThisType 20) == fmap f (fmap g (ThisType 20)) True *Main>
これも良さそう、これでThisTypeはFunctorでしょう(微妙か...)
Functorの定義
ちなみにこの書き方でもできましたFuncSample.h
{-# LANGUAGE DeriveFunctor #-} data ThisType a = ThisType { getThisType :: a } deriving(Eq, Show, Functor)
- ファイルの最初に{-# LANGUAGE DeriveFunctor #-}を書く
- derivingにFunctorを追加する
実行してみると
*Main> fmap id (ThisType 20) == id (ThisType 20) True *Main> fmap ((+10) . (+50)) (ThisType 20) == fmap (+10) (fmap (+50) (ThisType 20)) True *Main>
同じ結果となる、こういう理解でいいと思うが間違いがあったら指摘してください、ヨロピク
0 件のコメント:
コメントを投稿