跳轉到內容

Haskell/Solutions/透鏡和函式式引用

來自華夏公益教科書,開放書籍,開放世界

← 返回透鏡和函式式引用

透鏡的風景路線

[編輯 | 編輯原始碼]

1.

extremityCoordinates :: Applicative f
                     => (Double -> f Double) -> Segment -> f Segment
extremityCoordinates g (Segment start end) =
    Segment <$> pointCoordinates g start <*> pointCoordinates g end

設定器

[編輯 | 編輯原始碼]

1.

scaleSegment :: Double -> Segment -> Segment
scaleSegment x = over extremityCoordinates (x *)

2.

mapped :: Functor f => (a -> Identity b) -> f a -> Identity (f b)
mapped f = Identity . fmap (runIdentity . f)

最終的透鏡

[編輯 | 編輯原始碼]

1.

positionX :: Functor f => (Double -> f Double) -> Point -> f Point
positionX k p = (\x -> p { _positionX = x }) <$> k (_positionX p)

positionY :: Functor f => (Double -> f Double) -> Point -> f Point
positionY k p = (\y -> p { _positionY = y }) <$> k (_positionY p)

segmentStart :: Functor f => (Point -> f Point) -> Segment -> f Segment
segmentStart k s = (\p -> s { _segmentStart = p }) <$> k (_segmentStart s)

segmentEnd :: Functor f => (Point -> f Point) -> Segment -> f Segment
segmentEnd k s = (\p -> s { _segmentEnd = p }) <$> k (_segmentEnd s)

2.

lens :: Functor f => (s -> a) -> (s -> b -> t)
     -> (a -> f b) -> s -> f t
lens getter setter = \k x -> setter x <$> k (getter x)

瑞士軍刀

[編輯 | 編輯原始碼]

1a.

挑戰在於解碼outside的型別

outside :: Prism s t a b
        -> Lens (t -> r) (s -> r) (b -> r) (a -> r)

如果我們將它專門化為不改變型別的稜鏡,則更容易理解正在發生的事情。

Prism' s a -> Lens' (s -> r) (a -> r)

給定一個針對s內的a型別可能的目標的Prism'outside會給我們一個針對s -> r函式內部a -> r函式的Lens'。如果我們回到原始型別,它允許型別改變的稜鏡,我們注意到s/ta/b對被交換了。這是因為稜鏡的型別變量出現在透鏡處理的函式的引數中。我們可能會說outside是逆變的,與我們在本章前面討論逆變函子時使用的意義類似。

我們在剛才說的話中有一些非常奇怪的東西需要進一步解釋。說一個函式在另一個函式內部是什麼意思?為了回答這個問題,讓我們看看either的型別

either :: (a -> c) -> (b -> c) -> Either a b -> c
either f _ (Left x)     =  f x
either _ g (Right y)    =  g y

either從一個Either a b值中提取一個值。為此,它依賴於兩個函式:一個處理Left包裝的值的a -> c函式,以及一個處理Rightb -> c函式。either f g然後是一個Either a b -> c,可以看作是由兩個元件fg組成的,每個元件處理Either的一個建構函式。這些函式元件是outside修改的物件。從這個意義上說,outside將稜鏡用作“一等模式”。任何Either a b -> c函式必然正在進行模式匹配,解構一個Either a b以生成其c結果。outside _Leftoutside _Right允許我們修改這種函式處理每個模式的方式。

1b.

maybe :: b -> (a -> b) -> Maybe a -> b
maybe xNothing fJust
    = outside _Just .~ fJust
    $ const xNothing -- A default Maybe a -> b function.

either :: (a -> c) -> (b -> c) -> Either a b -> c
either fLeft fRight
    = outside _Left .~ fLeft
    $ outside _Right .~ fRight
    $ error "Impossible" -- There is no sensible default here.
華夏公益教科書