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.