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

时间:2014-09-10 19:22:22

标签: 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

但是,当我尝试将其与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

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

3 个答案:

答案 0 :(得分:10)

正如其他答案已经解释的那样,问题是view期望某些内容适用于任何Functor f,但traverse只有在f也是{{1}时才有效(并且有一些不适用的仿函数)。

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 a,则需要一个实例traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)。该实例虽然对Applicative (Const a)Monoid约束。这也是有道理的,因为遍历可能是空的或包含多个元素,因此您需要amempty

答案 1 :(得分:3)

traverse有这种类型:

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

如果我们在f的类型定义中明确地设置了自由变量Lens,那么它的定义实际上是

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

所以view想要一个可以在任何 Functor上运行的函数,而traverse只能在Applicative上运行。

您可以通过在Functor的定义中将Applicative更改为Lens来修复错误,但我不确定这是否与您完全相同想在这里实现。

答案 2 :(得分:3)

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


让我们来看看你的类型:

λ :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)

所以现在很明显,如果traverseLens,它必须是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

Oof,它不喜欢那样。哦,等等,为了使用traverse我们的类型t必须是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所必需的),只是它是{{1} (它来自Functor的定义。)

我们无法将Lens添加到上下文中 - 隐藏Applicative f。当我们说f时,我们说type Lens s a = Functor f => (a -> f a) -> s -> f s必须为所有 Lens 工作

Functor仅适用于同时为traverse的{​​{1}}子集。因此,Functor的类型更具体Applicative es所允许的类型