12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ファンクターについて勉強したことの整理

Last updated at Posted at 2015-03-05

学習の素材は、
すごいHaskellたのしく学ぼう!

この表記は、私見・感想です。

Functor 型クラス

ファンクターとは、関数で写せる(写像、map over) できるもののことをいう。例えば、リスト、Maybe、木がファンクターとなる。つまり、ある型aから別の型bへの関数ある型aに適用されたファンクター値 を引数にとり、 別の型bに適用されたファンクター値 を返すもののことをファンクターという。

また、ファンクターは文脈を持った値だ とみなすこともできる。Maybe 値は「計算が失敗したかもしれない」という文脈、リストは「複数の値を同時に取るかもしれない」という文脈。この時、fmap はこの文脈を維持したまま、内部の値に対して、関数を適用してくれる(後述)。

Functor型クラス定義
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b

ある型を Functor のインスタンスにしたければ、fmap ただひとつ実装すれば良い(追記)のではなく、ファンクター則を守る必要がある。ファンクター則とは、基本的な振る舞いが他のファンクターと一致することを保証するためにプログラマが遵守すべきルール。

ファンクターのインスタンス

Maybe や、リスト、Either a などがそれに当たる。

Maybeのインスタンス定義
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
リストのインスタンス定義
instance Functor [] where
fmap = map
Either/aのインスタンス定義
instance Functor (Either a) where
fmap f (Left a) = Left a
fmap f (Right a) = Right (f a)
木のインスタンス定義
instance Functor Tree where
fmap f EmptyTree = EmptyTree
fmap f (Node a left right) = 
        Node (f a) (fmap f left) (fmap f right)

-- 値が再帰的な二分木構造になるかもしれない文脈
data Tree a = EmptyTree
            | Node {value::a,  left::(Tree a), right::(Tree a)} deriving (Show)

自作の木( 型引数が String )について、fmapを適用してみると次のように出力される。

*Main> fmap ("this is " ++) (Node "test" EmptyTree EmptyTree)
Node {value = "this is test", left = EmptyTree, right = EmptyTree}

fmap の役割

ファンクターは、文脈の持った値であり、それは箱(に入った値)のようなものと表現することもできる。fmap は、この文脈(≒ 箱)を保ったまま、引数に取った関数を中の値に適用することができる。

「複数の値を同時に取るかもしれない」文脈を保ったまま、中の値に関数が適用される。
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b

Prelude> fmap (++ " world!") ["Hello", "Good-by"]
["Hello world!","Good-by world!"]

fmap と関数合成

ある1引数関数の型を表す際、アロー演算子を用いてa -> b のように書く。この演算子は中置演算子だが、(->) a b と表すこともできる。この時、(-> a) もファンクターとなる。ここでの文脈は、 a の値を適用すると、値が返ってくる となる。
fmap :: Functor f => (a -> b) -> f a -> f bf(-> r) で置き換えてみると、

fmap :: (a -> b) -> ((->)r ) a -> ((->) r b)

(->) を中置演算子として書き直すと

fmap :: (a -> b) -> (r -> a) -> (r -> b)

となる。fmap の実装を見てみると、

fmap f g = (.)
-- or
fmap f g = (\x = f (g x))

であり、これは関数合成そのものとなる。

関数合成は、文脈を保って中の値に関数を適用するファンクターの一種、ということが言える。

fmap のまとめ

fmap とは、2つの観点で説明することができる。

  1. 関数とファンクター値を取って、その関数でファンクター値を写してから返す(fmap :: (a->b) -> f a -> f b)
  2. 値から値への関数を取って、ファンクター値からファンクター値への関数を返す(fmap :: (a->b) -> (f a -> f b))

特に、2番目の観点でファンクターを捉える時、fmap することを 関数の持ち上げ(lifting) と呼ぶ。

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?