Haskell 理解Data.Functor.Constant构造函数及其应用定律
我对Data.Functor.Constant的类型构造函数以及它如何与applicative一起工作感到困惑Haskell 理解Data.Functor.Constant构造函数及其应用定律,haskell,constants,functor,applicative,monoids,Haskell,Constants,Functor,Applicative,Monoids,我对Data.Functor.Constant的类型构造函数以及它如何与applicative一起工作感到困惑 首先,建造师: 当我检查常量::a->常量a b的类型时 我看到它接受一个a,但返回一个常数ab b从何而来,为什么存在 其次,我正在努力解决应用性问题: 我理解常量需要有一个幺半群才能成为一个应用实例 它必须遵守的法则是:纯id常数x=x 我认为这和:常量id常量x=x 但我想我错了,因为下面的代码清楚地显示了不同的纯行为 :t pure id <*> Constan
首先,建造师: 当我检查
常量::a->常量a b的类型时
我看到它接受一个a
,但返回一个常数ab
b
从何而来,为什么存在
其次,我正在努力解决应用性问题: 我理解常量需要有一个幺半群才能成为一个应用实例 它必须遵守的法则是:
纯id常数x=x
我认为这和:常量id常量x=x
但我想我错了,因为下面的代码清楚地显示了不同的纯行为
:t pure id <*> Constant "hello" // Constant [Char] b
:t Constant id <*> Constant "hello" // Couldn't match expected type `a0 -> a0' with actual type `[Char]'
:t pure id <*> Constant reverse // Constant ([a] -> [a]) b
:t Constant id <*> Constant reverse // Constant ([a] -> [a]) b
:t纯id常量“hello”//Constant[Char]b
:t常量id常量“hello”//无法将预期类型“a0->a0”与实际类型“[Char]”匹配
:t纯id常量反向//常量([a]->[a])b
:t常量id常量反向//常量([a]->[a])b
我发现只有当x
是同一个幺半群时它才起作用,除非我使用纯幺半群。所以我不知道为什么pure的工作方式不同。我怀疑这与b
有关,这就是为什么他们在同一个问题中
总结这两个问题:
b
在常量构造函数中做什么非常感谢 好的,你有这种类型的
data Const a b = Const { getConst :: a }
你的第一个问题是“这个b
从哪里来?”
答案是它不是来自任何地方。与您可以将可能b
视为一个容器,其中包含类型b
的0或1个值相同,a常量a b
是一个容器,它正好包含类型b
的0个值(但肯定包含类型a
的值)
你的第二个问题是“它为什么在那里?”
有时候,有一个函子说它可能包含b
类型的值,但实际上却包含其他的值,这是很有用的(例如,想想或者a b
函子——区别在于或者a b
可能包含b
类型的值,而常量a b
肯定不会)
然后您询问了代码片段pure id Const“hello”
和Const id Const“hello”
。你以为它们是一样的,但事实并非如此。原因是Const
的Applicative
实例
instance Monoid m => Applicative (Const m) where
-- pure :: a -> Const m a
pure _ = Const mempty
-- <*> :: Const m (a -> b) -> Const m a -> Const m b
Const m1 <*> Const m2 = Const (m1 <> m2)
data Identity a = Identity { getIdentity :: a }
get :: Lens b a -> b -> a
get l = getConst . l Const
另一方面,当您编写Const id Const“hello”
时,左侧参数的类型为Const(a->a)b
,右侧参数的类型为Const String b
,您会看到类型不匹配,这就是为什么会出现类型错误的原因
现在,这为什么有用呢?库中有一个应用程序,它允许您在纯函数设置中使用getter和setter(与命令式编程相似)。透镜的简单定义如下:
type Lens b a = forall f. Functor f => (a -> f a) -> (b -> f b)
i、 e.如果您给它一个转换a
类型值的函数,它将还给您一个转换b
类型值的函数。那有什么用?好吧,让我们为一个特定的函子f
选择一个类型为a->fa
的随机函数。如果我们选择Identity
函子
instance Monoid m => Applicative (Const m) where
-- pure :: a -> Const m a
pure _ = Const mempty
-- <*> :: Const m (a -> b) -> Const m a -> Const m b
Const m1 <*> Const m2 = Const (m1 <> m2)
data Identity a = Identity { getIdentity :: a }
get :: Lens b a -> b -> a
get l = getConst . l Const
如果l
是镜头,则定义
modify :: Lens b a -> (a -> a) -> (b -> b)
modify l f = runIdentity . l (Identity . f)
为您提供了一种方法,可以将转换a
s的函数转换为转换b
s的函数
我们可以传入的另一个类型为a->fa
的函数是Const::a->Const a a
(注意,我们已经专门化了第二个类型,因此第二个类型与第一个类型相同)。然后镜头l
的作用是将其转换为b->Const a b
类型的函数,这告诉我们它可能包含b
,但实际上它实际上暗中包含a
!一旦我们将它应用于b
类型的某个对象以获得常数ab
,我们就可以使用getConst::Const a b->a
将a
类型的值从帽子中取出。因此,这为我们提供了一种从ab
中提取a类型值的方法,即它是一个getter。定义看起来像
instance Monoid m => Applicative (Const m) where
-- pure :: a -> Const m a
pure _ = Const mempty
-- <*> :: Const m (a -> b) -> Const m a -> Const m b
Const m1 <*> Const m2 = Const (m1 <> m2)
data Identity a = Identity { getIdentity :: a }
get :: Lens b a -> b -> a
get l = getConst . l Const
作为镜头示例,您可以定义
first :: Lens (a,b) a
first f (a,b) = fmap (\x -> (x,b)) (f a)
这样您就可以打开GHCI会话并编写
>> get first (1,2)
1
>> modify first (*2) (3,4)
(6,4)
正如你所想象的,这在各种情况下都是有用的。答案不错,尽管我认为说“
Const a b
是谎称持有b
类型的值”是误导性的。也许“a b
可能包含类型为b
的值,而Const a b
肯定不包含该类型的值。”?@TomEllis这是一个很好的建议,已编辑以包含它。如果您能提供有关我对镜头的解释的反馈,我将不胜感激。这是我第一次尝试解释它。我只想链接到SPJ的视频来解释它。好吧,也许还可以链接到它,并说“这是另一个非常棒的解释。”太棒了!非常感谢。我不敢相信我没有意识到Const部分地应用于mempty。这很有道理。事实上,镜头是我首先考虑这个问题的动机。非常感谢。first
的类型应该是Lens(a,b)a