12

我正在尝试通过在 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

但是,当我尝试将它与我一起使用时,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

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

4

3 回答 3

10

正如其他答案已经解释的那样,问题是view期望一些适用于 any的东西Functor f,但traverse只有在fis时才有效Applicative(并且有些函子不适用)。

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

view :: ((a -> Const a a) -> s -> Const a s) -> s -> a

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

view traverse :: (Traversable t, Monoid a) => t a -> a

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

于 2014-09-10T20:35:17.470 回答
3

tl;dr - 根据您对 的定义Lens,atraverse不能是 a Lens,因为traverse不适用于所有Functors。


让我们看看你的类型:

λ :set -XRankNTypes 
λ :m +Control.Applicative Data.Traversable 
λ type Lens s a = Functor f => (a -> f a) -> s -> f s
λ :t traverse
traverse
  :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)

现在在这一点上,我们可以看到traverse,在某种程度上,它比我们的Lens类型稍微更通用 - 它可以从a -> f b我们的镜头只能从a -> f a.

把它限制在那种情况下是没有问题的,所以我们可以说

λ :t traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)
traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)
  :: (Applicative f, Traversable t) => (a -> f a) -> t a -> f (t a)

所以现在很明显 if traverseis 是 a Lens,它必须是 a Lens (t a) a,因为这是使类型变量对齐的唯一方法。

所以让我们试试看。

λ :t traverse :: Lens (t a) a

<interactive>:1:1:
    Could not deduce (Traversable t1) arising from a use of `traverse'
    from the context (Functor f)
      bound by the inferred type of
               it :: Functor f => (a -> f a) -> t a -> f (t a)
      at Top level
    or from (Functor f1)
      bound by an expression type signature:
                 Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
      at <interactive>:1:1-24
    Possible fix:
      add (Traversable t1) to the context of
        an expression type signature:
          Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
        or the inferred type of
           it :: Functor f => (a -> f a) -> t a -> f (t a)
    In the expression: traverse :: Lens (t a) a

哎呀,它不喜欢那样。哦,等等,为了使用traverse我们的类型thas to be Traversable,所以让我们添加这个限制。(就像“可能的修复”)建议:

λ :t traverse :: Traversable t => Lens (t a) a

<interactive>:1:1:
    Could not deduce (Applicative f1) arising from a use of `traverse'
    from the context (Functor f, Traversable t)
      bound by the inferred type of
               it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a)
      at Top level
    or from (Traversable t1, Functor f1)
      bound by an expression type signature:
                 (Traversable t1, Functor f1) =>
                 (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
      at <interactive>:1:1-41
    Possible fix:
      add (Applicative f1) to the context of
        an expression type signature:
          (Traversable t1, Functor f1) =>
          (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
        or the inferred type of
           it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a)
    In the expression: traverse :: Traversable t => Lens (t a) a

好的,所以现在的问题是它不能推断那fApplicative(也需要使用traverse),只是它是一个Functor(它从 的定义中得到Lens)。

但是我们不能添加Applicative f到上下文中 -f是隐藏的。当我们说 时type Lens s a = Functor f => (a -> f a) -> s -> f s,我们说的是Lens必须为所有 Functors 工作。

traverse仅适用于也是Functors的子集Applicative。因此,通过这种方式,类型的类型比 es 允许的traverse类型更具体Lens

于 2014-09-10T19:53:41.110 回答
3

traverse有这种类型:

traverse :: (Applicative f, Traversable t) => (x -> f y) -> t x -> f (t y)

如果我们f在explicit的类型定义中做free变量Lens,它的定义其实就是

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

所以view想要一个可以对any Functor进行操作的函数,而traverse只能对 an 进行操作Applicative

您可以简单地通过更改 的定义来修复错误FunctorApplicativeLens我不确定这是否正是您想要在这里实现的。

于 2014-09-10T19:49:33.317 回答