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.