Bri*_*ian 15 haskell constants functor applicative monoids
我对Data.Functor.Constant的类型构造函数以及它如何与applicative一起使用感到困惑.
首先是构造函数:
当我检查的类型 Constant :: a -> Constant a b
我看到它需要一个a
,但返回一个Constant a b
它b
来自哪里,为什么存在?
其次,我正在努力应用:
我理解Constant需要将Monoid作为Applicative实例.
它必须遵守的法律是: pure id <*> Constant x = x
我以为那是一样的: Constant id <*> Constant 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
Run Code Online (Sandbox Code Playgroud)
我看到它只有x
在相同的monoid 时才有效,除非我使用纯粹的.所以我不确定为什么纯粹的工作方式不同.我怀疑这与b
他们在同一个问题中的原因有关.
总结这两个问题:
b
在Constant构造函数中做什么?
即使幺半群内部不同,为什么纯粹的工作呢?
非常感谢!
Chr*_*lor 27
好的,所以你有这种类型
data Const a b = Const { getConst :: a }
Run Code Online (Sandbox Code Playgroud)
你的第一个问题是"它b
来自哪里?"
答案是它不是来自任何地方.以您可以将其Maybe b
视为容纳0或1类型的容器的方式相同的方式b
,a Const a b
是一个容器,它只包含0个类型的值b
(但绝对保存类型的值a
).
你的第二个问题是"它为什么存在?"
好吧,有时候让一个仿函数说它可能包含类型的值b
,但实际上包含其他东西(例如,考虑Either a b
仿函数 - 区别在于它Either a b
可能包含类型的值b
,而Const a b
绝对不是).
然后你问了代码片段pure id <*> Const "hello"
和Const id <*> Const "hello"
.你认为这些是相同的,但它们不是.原因是该Applicative
实例Const
看起来像
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)
Run Code Online (Sandbox Code Playgroud)
由于实际上没有任何值具有第二个参数的类型,我们只需要处理那些具有第一个参数类型的值,我们知道它是一个幺半群.这就是为什么我们可以创建Const
一个实例Applicative
- 我们需要m
从某个地方提取类型的值,并且Monoid
实例为我们提供了一种从无处(使用mempty
)创建一个的方法.
那你的例子会发生什么?从那以后你pure id <*> Const "hello"
必须有类型.在这种情况下,幺半群是.我们有一个,和.所以你最终得到了Const String a
id :: a -> a
String
mempty = ""
String
(<>) = (++)
pure id <*> Const "hello" = Const "" <*> Const "hello"
= Const ("" <> "hello")
= Const ("" ++ "hello")
= Const "hello"
Run Code Online (Sandbox Code Playgroud)
另一方面,当你写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)
Run Code Online (Sandbox Code Playgroud)
即如果你给它一个转换类型值的函数a
,它将返回一个转换类型值的函数b
.那有用的是什么?好吧,让我们a -> f a
为特定的仿函数选择一个随机函数f
.如果我们选择Identity
看起来像的仿函数
data Identity a = Identity { getIdentity :: a }
Run Code Online (Sandbox Code Playgroud)
那么如果l
是镜头,定义
modify :: Lens b a -> (a -> a) -> (b -> b)
modify l f = runIdentity . l (Identity . f)
Run Code Online (Sandbox Code Playgroud)
为您提供了一种方法来获取转换a
s的函数并将它们转换为转换b
s的函数.
a -> f a
我们可以传入的另一种类型的函数是Const :: a -> Const a a
(注意我们已经专门化,因此第二种类型与第一种类型相同).然后镜头的作用l
是把它变成类型的函数b -> Const a b
,它告诉我们,它可能包含b
,但实际上悄悄它确实包含一个a
!一旦我们将它应用于某种类型b
以获得a Const a b
,我们就可以用它来获取getConst :: Const a b -> a
类型的值a
.因此,这为我们提供了一种a
从a中提取类型值的方法b
- 即它是一个吸气剂.定义看起来像
get :: Lens b a -> b -> a
get l = getConst . l Const
Run Code Online (Sandbox Code Playgroud)
作为镜头的一个例子,你可以定义
first :: Lens (a,b) a
first f (a,b) = fmap (\x -> (x,b)) (f a)
Run Code Online (Sandbox Code Playgroud)
这样你就可以打开一个GHCI会话并写下来
>> get first (1,2)
1
>> modify first (*2) (3,4)
(6,4)
Run Code Online (Sandbox Code Playgroud)
正如您可能想象的那样,它在各种情况下都很有用.
归档时间: |
|
查看次数: |
1009 次 |
最近记录: |