Haskell镜头:如何通过遍历使视图很好地播放?

Aad*_*hah 12 haskell lenses traversable

我试图通过在Haskell中实现它来了解镜头.我已经实现了view如下组合器:

{-# LANGUAGE RankNTypes #-}

import Control.Applicative
import Data.Traversable

type Lens s a = Functor f => (a -> f a) -> s -> f s

view :: Lens s a -> s -> a
view lens = getConst . lens Const
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试与它一起使用时,traverse我收到以下错误消息:

Prelude> :load Lens.hs
[1 of 1] Compiling Main             ( Lens.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t view traverse

<interactive>:1:6:
    Could not deduce (Applicative f) arising from a use of ‘traverse’
    from the context (Traversable t)
      bound by the inferred type of it :: Traversable t => t a -> a
      at Top level
    or from (Functor f)
      bound by a type expected by the context:
                 Functor f => (a -> f a) -> t a -> f (t a)
      at <interactive>:1:1-13
    Possible fix:
      add (Applicative f) to the context of
        a type expected by the context:
          Functor f => (a -> f a) -> t a -> f (t a)
        or the inferred type of it :: Traversable t => t a -> a
    In the first argument of ‘view’, namely ‘traverse’
    In the expression: view traverse
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不明白这个错误信息.请解释它的含义以及如何解决它.

ben*_*ofs 10

至于其他的答案已经解释的问题是,view预期的东西,对于任何工作Functor f,但traverse只有工作,如果f是还Applicative(有仿函数不属于应用性).

lens,通过使viewRank2参数的类型解决问题(事实上​​,镜头中的大多数功能不使用镜头类型的同义词,它们总是使用较弱的东西).对于您的功能,请注意view只能使用f ~ Const.这就是您可以将类型签名更改为:

view :: ((a -> Const a a) -> s -> Const a s) -> s -> a
Run Code Online (Sandbox Code Playgroud)

实现可以保持不变,但现在view也适用于traverse:

view traverse :: (Traversable t, Monoid a) => t a -> a
Run Code Online (Sandbox Code Playgroud)

注意额外的Monoid约束.这种约束会出现,因为如果你设置f ~ Const atraverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a),你需要一个实例Applicative (Const a).但是那个例子有一个Monoid限制a.这也是有道理的,因为遍历可能是空的或包含多个元素,所以你需要memptymappend.