Lens,Getter,SetterからEquality,Iso,Prism,Reviewに関して

Haskellのlensの使い方 (基本)の続き

Equality

type Equality s t a b = forall p f. p a (f b) -> p s (f t)

A witness that (a ~ s, b ~ t).

  • 図の一番下にある。
  • as,btが等しいことを示す。同時に2つの等号を表すのはlensとして使うためであろう。
type Equality s t a b = forall p f.               p a (f b) -> p s (f t)
type Lens     s t a b = forall   f. Functor f => (a -> f b) -> s -> f t

のようにLensと比較すると、その(->)Functorの制約が取り除かれていることが分かる。型は、任意の二項型構築子pに対しa,bからs,tへはその上で変換できる、と言っている。このようなことが可能であるのはつまり、as,btが同一である時のみである。(あるいは型a,bを持つ値が存在しない時。)

  • 抽象的過ぎて制約が強く逆に分かりやすい
  • data Identicalは、その値が存在することとas,btが同一であることが同値であるようにGADTsで定義されている
    • 面白い
type Foo = Int
fooIsInt :: Equality' Foo Int
fooIsInt = id
fooIsDouble :: Equality' Foo Double
fooIsDouble = id -- Couldn't match type `Double' with `Int'

Iso

type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t)
iso :: (s -> a) -> (b -> t) -> Iso s t a b
from :: Iso s t a b -> Iso b a t s

Mon Dec 22 23:05:12 JST 2014 : Isoの型がLensのそれになっていたので修正 @minpou_氏に感謝します

Isomorphism families can be composed with another Lens using (.) and id.

  • EqualityLensの間にある。同型を表わす
    • 同型とはざっくり言うとこの場合その型を持つ値の数が等しいことである
  • 逆向きでも使えるLens
    • fromは向きをひっくり返す
    • ひっくり返してもなおIso
  • これも普通Iso s t a bでなくIso' s aを使うと思う
    • そもそもs = t, a = bでないとGetterのmethodが使えない
  • 関数isoは右向きと左向きの2つの関数を与え、Isoを作る
    • 合成してもidにならないものを与えることもできるが止めるべき
  • makeLensesは可能ならばIsoを作る
data Tribool = TTrue | TFalse | TUnkown
tribool :: Iso' (Maybe Bool) Tribool
tribool = iso f g where
    f (Just True)  = TTrue
    f (Just False) = TFalse
    f Nothing      = TUnkown
    g TTrue   = Just True
    g TFalse  = Just False
    g TUnkown = Nothing
>>> (Just True) ^. tribool
TTrue
>>> TUnkown ^. from tribool
Nothing

Lens

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b

A Lens s t a b is a purely functional reference.

  • 純粋関数的参照
  • GetterかつSetter

依存

lens類全般に言えることだが、単なる関数であるのでlens packageに依存していなくてもLensは作れる。

lens :: (s -> a) -> (s -> b -> t) -> (forall f. Functor f => (a -> f b) -> s -> f t)
lens sa sbt afb s = sbt s <$> afb (sa s)

なので、例えば

data Foo a = Foo { _bar :: Int, _baz :: Int, _quux :: a }
bar :: forall f. Functor f => (Int -> f Int) -> Foo a -> f (Foo a)
bar f s = (\ b -> s { _bar = b }) <$> f (_bar s)

のようにするとLensができる

Prism

type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
prism  :: (b -> t) -> (s -> Either t a) -> Prism s t a b
prism' :: (b -> s) -> (s -> Maybe a)    -> Prism s s a b

A Prism l is a Traversal that can also be turned around with re to obtain a Getter in the opposite direction.

  • (値の範囲的な意味での)包含
    • IntegetrNaturalとか
  • Lensの隣り
  • Lensと同じぐらい便利

作成:

import Numeric.Natural
nat :: Prism' Integer Natural
nat = prism toInteger $ \ i ->
    if i < 0
    then Left i
    else Right (fromInteger i)

左から右へは変換できないことがあるが、右から左へは必ず変換できる。

>>> (3 :: Integer) ^? nat :: Maybe Natural
Just 3
>>> (-3 :: Integer) ^? nat :: Maybe Natural
Nothing
>>> (3 :: Natural) ^. re nat :: Integer
3

無理矢理Maybeを剥がすこともできるし、添えることもできる。

>>> (3 :: Integer) ^?! nat :: Maybe Natural
3
>>> (-3 :: Integer) ^? nat :: Maybe Natural
*** Exception: (^?!): empty Fold
>>> (3 :: Natural) ^? re nat :: Maybe Integer
Just 3

reReviewの、(^?),(^?!)Foldのmethodである。またSetterでもあり、Naturalであるときだけ2倍するといった操作も可能。

>>> (-3 :: Integer) & nat %~ (* 2)
-3
>>> (3 :: Integer) & nat %~ (* 2)
6

template haskell

makeLensesの対応物makePrismsが存在する。

data ABC = A | B Int | C Double String
makePrisms ''ABC

すると

_A :: Prism' ABC ()
_B :: Prism' ABC Int
_C :: Prism' ABC (Double, String)

が定義される。便利。

Review

type Review t b = forall p f. (Choice p, Bifunctor p, Settable f) => Optic' p f t b
unto :: (b -> t) -> Review s t a b
re :: Review s a -> Getter a s
un :: Getter s a -> Review a s
review :: Review t b -> b -> t

This is a limited form of a Prism that can only be used for re operations.

  • Prismの上、親はいない
    • reだけできるPrism
  • reするとGetterになる
    • 逆にGetterunするとReviewになる
    • といってもReviewにしてもどうせ使うときはGetterに戻して使ってる
  • reviewも、reしてGetterにしてviewしてるだけ
>>> "hoge" ^. re (unto length)
4

ただし、

  re (unto length)
= (re . un . to) length
= (re . un) (to length)
= to length

である。

Getter

type Getter s a = forall f. Gettable f => (a -> f a) -> s -> f s
to :: (s -> a) -> Getter s a
view :: Getter s a -> s-> a
(^.) = flip view

Sun Dec 21 20:35:39 JST 2014 : view, (^.) :: s -> Getter s a -> aと表記していたので訂正

A Getter s a is just any function (s -> a), which …

  • つまるところただの関数
  • LensIsoGetter
  • Reviewと仲良し
  • view,(^.)で元の普通の関数に戻る
>>> False ^. to show
"False"

つまり

x ^. to f = f x

である。

State

Stateの上でも使える (正確にはMonadState s)

use :: Getter s a -> State s a

Setter

type Setter s t a b = forall f. Settable f => (a -> f b) -> s -> f t
sets :: ((a -> b) -> s -> t) -> Setter s t a b
mapped :: Functor f => Setter (f a) (f b) a b
set, (.~) :: Setter s t a b -> b -> s -> t
over, (%~) :: Setter s t a b -> (a -> b) -> s -> t

A Setter s t a b is a generalization of fmap from Functor.

  • 曰く関手
    • mappedの型を見ると分かりやすい
  • setは値を代入
  • overは値を更新
>>> [1,2,3,4] & sets (\ f (x : xs) -> f x : xs) .~ 10
[10,2,3,4]
>>> [1,2,3,4] & mapped %~ (+ 10)
[11,12,13,14]
>>> [1,2,3,4] & mapped .~ 10
[10,10,10,10]

mappedの例ではまさにmapしているのが分かる。

State

Getteruse同様、Stateの上でも使える

assign, (.=) :: Setter s s a b -> b -> State s ()
(%=) :: Setter s s a b -> (a -> b) -> m ()

まとめ

  • Getterは関数
  • Setterは関手
  • LensGetterかつSetter
  • Reviewは逆向きのGetter
  • PrismReviewかつSetter
  • Isoは同型
  • Equalityは同一性

This post is the No.15 article of Haskell Advent Calendar 2014


haskellのlensの使い方 (詳しめ)

  • Sun Jun 21 01:59:23 JST 2015
    • 次記事へのlinkを削除