我试图通过在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
不幸的是,我不明白这个错误信息。请解释它的含义以及如何解决它。
答案 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
约束。这也是有道理的,因为遍历可能是空的或包含多个元素,因此您需要a
和mempty
。
答案 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)
所以现在很明显,如果traverse
是Lens
,它必须是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
好的,现在的问题是它无法推断f
是Applicative
(这也是使用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所允许的类型