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/t和a/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函式,以及一個處理Right的b -> c函式。either f g然後是一個Either a b -> c,可以看作是由兩個元件f和g組成的,每個元件處理Either的一個建構函式。這些函式元件是outside修改的物件。從這個意義上說,outside將稜鏡用作“一等模式”。任何Either a b -> c函式必然正在進行模式匹配,解構一個Either a b以生成其c結果。outside _Left和outside _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.