跳转到内容

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.
华夏公益教科书