LoginSignup
1
0

More than 1 year has passed since last update.

Functor 整理

Last updated at Posted at 2022-12-31

基本用語

data宣言 -- 値コンストラクタ

-- data 型名 = 値コンストラクタ
data Bool = False | True

型名は型の名称を、値コンストラクタはデータ型が取りうる値を表す。

-- 値コンストラクタは名称をつけることができる
data Shape = Circle Float Float Float | 
                Rect Float Float Float Float 

CircleはFloat型引数を三つとる値(コンストラクタ)である。
※「Circleというデータ型を定義している」のではなく「Circleという名称の値を宣言している」というイメージ。
Shape型の取りうる値はCircleとRectの2種類。

ghci> :t Circle  // 宣言前
<interactive>:1:1: error: Data constructor not in scope: Circle
ghci> data Shape = Circle Float Float Float // 宣言
ghci> :t Circle // ghciに認識される
Circle :: Float -> Float -> Float -> Shape

Circleのその実は、ただの関数であるともいえる。

data Shape = Circle { x ::Float, y:: Float, r:: Float } | 
Rect {left::Float , top::Float, right::Float, bottom::Float}

レコード構文という上記書き方で引数にラベル付が可能

-- 面積を求める関数
area :: Shape -> Float
area (Circle _ _ r) = pi * r^2
area (Rext x1 y1 x2 y2) = (abs $ x2-x1) * (abs $ y2-y1)

---
ghci> area $ Rect 0 0 10 10
100

-- Circleは値なので以下の書き方はできない
area :: Circle -> Float
area (Circle _ _ r) = pi * r^2

関数を作成

data宣言 -- 型コンストラクタ

-- data 型コンストラクタ 型引数 = 値コンストラクタ
data Maybe a = Nothing | Just a

型コンストラクタは型を引数にとって新しい型を生み出す。
Maybeは型コンストラクタといい、aという型(引数)から新しい型を生み出す。

ghci> :t Nothing
Nothing :: Maybe a
ghci> :t Just "hoge"
Just "hoge" :: Maybe String -- Maybe String 型
ghci> :t Just 1
Just 1 :: Num a => Maybe a -- 型推論

Maybeは型を生み出す型コンストラクタであるため、単独では値になれない。

型引数がない型(IntやBool)、型引数があるが全て埋まっている型(Maybe String)型 を具体型という。

-- この関数はNothingを引数に取れる
fn :: Maybe Int -> Int

Nothing :: Maybe a は多相的、つまりa型がなんであれ、Maybe型コンストラクタを持つすべての型にはNothingを入れられる。

この仕組みを使って、Maybeという値を持つ(Just a) か持たないか(Nothing)を表す値といった抽象な概念を表現している。

-- ベクトル加算を表現
data Vector a = Vector a a a deriving (Show)

vplus:: (Num a) => Vector a -> Vector a -> Vector a
vplus (Vector x y z) (Vector i j k) = Vector (x+i) (y+j) (z+k)

main = do
    print $ vplus (Vector 3 5 8) (Vector 9 2 8)

型クラス

class Foo a where
    foo :: a -> String
instance Foo Bool where
    foo True :i = "True"
    foo False = "False"
instance Foo Int where
    foo x = show x
main = do
    print $ foo True -- "True"
    print $ foo False -- "False"
    print $ foo (2 ::Int) -- "2"

classは特定の振る舞いを表す。同じ振る舞いをする型たちに共通のインターフェースを提供する。
新たな型クラスを定義するのがclass、型を型クラスのインスタンスにするのがinstance

種類

ghci> :i Int
type Int :: *
ghci> :i Maybe
type Maybe :: * -> *
data Maybe a = Nothing | Just a

型にはラベルがある。型の型なんて言ったりもする。
「*」が具体型を表している。
上記Maybeは「一つの具体型を受け取って具体型となる」と読み、型コンストラクタであることを示している。

Fanctor

ghci> :i Functor 
type Functor :: (* -> *) -> Constraint
class Functor f where
  fmap :: (a -> b) -> f a -> f b

type より 型コンストラクタから型制約ありの型(※1)を返すことがわかるので、fはなんらかの型コンストラクタである読める。
fmapは 「a->b の関数」と「型コンストラクタfに型引数aを適応した型」を受け取り、「型コンストラクタfに型引数bを適応した型」を返すと読む。
混同するかもだが、f関数にaを適応すると言った意味ではなく、「f a」 で一つの具体型を表す。

instance Functor Maybe where
    fmap _ Nothing = Nothing
    fmap f (Just a) = Just (f a)

型コンストラクタを伴う具象型はFunctorになり得る(*2)。Maybe型コンストラクタはFunctorのインスタンスである。
Maybeの中身がNothingならNothingを返し、なんらかの値をもったMaybeであれば,第一引数のfをaに適応する。
同じ単語がでてくるのでややこいが、ここでは
fmap f (Just a) = Just (f a) のf は (a -> b)の関数のことであり、aは(Just a)の中身の値aである。 Functor型クラスのf,aとは異なるので注意。

ghci> fmap (*3) (Just 11) -- 値があれば関数を適応する
Just 33
ghci> fmap (*3) Nothing -- なにもなければ何もないを返す
Nothing

Just 11のような「型コンストラクタで意味付けされたなんらかの値」の中身11に関数*3を適応して、「同じく型コンストラクタで意味付されたなんらかの値」 Just 33 を返す。
「型コンストラクタで意味付けされたなんらかの値」のことを文脈といったりする。fmapは文脈を変えずに中身の値だけ変える道具」といったイメージ。

instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor Solo -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((,,,) a b c) -- Defined in ‘GHC.Base’
instance Functor ((,,) a b) -- Defined in ‘GHC.Base’
instance Functor ((,) a) -- Defined in ‘GHC.Base’

Functorのinstanceは上記、一般的には「全体を写せるもの」は全部Functor なんていったりする。
Maybeの例でも,Maybeの文脈はそこねず、aをbに写したものと考えることができる。


Functorの説明表現

  • なんらかの値を写せるもの
  • 値を入れるなんらかのコンテナのような構造を持つ型
  • なんらかの型をラッピングした(包み込んだ)型を

※1

ghci> :t (==)
(==) :: Eq a => a -> a -> Bool

この記事ではあまり気にしなくて良いが、Haskellには型制約という機能がある。(==>)がそれで、型引数aはEq(等値性比較クラス)のサブクラスである必要があるといった制約を設けている。

※2

fmap id = id
fmap (f . g) == fmap f . fmap g

Fanctorである条件は上記Functor即を満たすものである必要がる。

1
0
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
1
0