[英]Why type of pure is a -> f a, not (a -> b) -> f (a -> b) on Applicative?
Pure is used to transform normal function into function in Applicative
container. Pure用于在
Applicative
容器中将普通的function转化为function。 With this, any multi-parameter operations become can be used on Applicative
.有了这个,任何多参数操作都可以在
Applicative
上使用。 In this context, pure is not desired to be a -> fa
type, it is just desired to be (a -> b) -> f (a -> b)
type.在这种情况下, pure 不希望成为
a -> fa
类型,它只希望成为(a -> b) -> f (a -> b)
类型。 But type of pure
is a -> fa
.但是
pure
的类型是a -> fa
。 Why should normal values can be transformed into Applicative
?为什么要将 normal values 转化为
Applicative
? Is there more purpose for use pure
more than transforming functions? use
pure
more 比转换函数有更多的目的吗?
I don't find the Applicative
interface of applying lifted functions (namely, (<*>)
) a good intuition.我没有发现应用提升函数(即
(<*>)
)的Applicative
接口是一个很好的直觉。 Functions are more complicated to conceptualize for various reasons.由于各种原因,函数的概念化更为复杂。
I prefer thinking of Applicative
as lifting an n -ary function我更喜欢将
Applicative
视为提升n元 function
liftA0 :: Applicative f => (a) -> (f a)
liftA :: Functor f => (a -> b) -> (f a -> f b)
liftA2 :: Applicative f => (a -> b -> c) -> (f a -> f b -> f c)
liftA3 :: Applicative f => (a -> b -> c -> d) -> (f a -> f b -> f c -> f d)
where liftA0 = pure
and liftA
already exists as fmap
defined in terms of Applicative
.其中
liftA0 = pure
和liftA
已经作为根据Applicative
定义的fmap
存在。
The thing is that 0-ary and 1-ary liftings问题是 0 元和 1 元提升
liftA0 @f @a :: Applicative f => a -> f a
liftA @f @a @b :: Applicative f => (a -> b) -> (f a -> f b)
can both take an a -> b
function if we instantiate liftA0 = pure
at a function type:如果我们在 function 类型中实例化
liftA0 = pure
,都可以采用a -> b
function 类型:
liftA0 @f @(a->b) :: Applicative f => (a -> b) -> f (a->b)
liftA @f @a @b :: Applicative f => (a -> b) -> (f a -> f b)
So pure @f @(a->b)
already has that type.所以
pure @f @(a->b)
已经有那个类型了。
And pure
has plenty of purposes, theoretical which turn out to be practical in Haskell, it is the unit if Applicative
is viewed as a Monoid
in the category of natural transformations, with ( Notions of Computation as Monoids ) with Day
pure
有很多目的,理论上在 Haskell 中被证明是实用的,如果Applicative
被视为自然变换类别中的Monoid
,它是单位,与(计算概念作为 Monoids )和Day
type Mempty :: Type -> Type
type Mempty = Identity
type Mappend :: (Type -> Type) -> (Type -> Type) -> (Type -> Type)
type Mappend = Day
mempty :: Applicative f => Mempty ~> f
mempty (Identity a) = pure a
mappend :: Mappend f f ~> f
mappend (LiftA2 (·) as bs) = liftA2 (·) as bs
I just released a library that works with Applicative
homomorphisms, that are polymorphic functions that respect the applicative structure.我刚刚发布了一个适用于
Applicative
同态的库,应用同态是尊重应用结构的多态函数。 It defines a type class for such structures它为此类结构定义了类型 class
type Idiom :: k -> (Type -> Type) -> (Type -> Type) -> Constraint
class (Applicative f, Applicative g) => Idiom tag f g where
idiom :: f ~> g
where pure
is the initial applicative morphism .其中
pure
是初始应用态射。
-- https://chrisdone.com/posts/haskell-constraint-trick/
instance (Identity ~ id, Applicative f) => Idiom Initial id f where
idiom :: Identity ~> f
idiom (Identity a) = pure a
pure
is then frequently used, as a unit for a computation.然后经常使用
pure
作为计算单位。 It is the driving force in Traversable
one of the success stories of Haskell它是 Haskell 成功案例之一的
Traversable
的推动力
instance Traversable [] where
traverse :: Applicative f => (a -> f b) -> ([a] -> f [b])
traverse f [] = pure []
traverse f (x:xs) = ...
we require pure
because our only argument that produces an Applicative
-action is fx
but with an empty list we don't have an x:: a
to feed it.我们需要
pure
是因为我们唯一产生Applicative
-action 的参数是fx
但对于一个空列表,我们没有x:: a
来提供它。 Thus, we need 0-ary lifting.因此,我们需要 0 元提升。
You can define pure:: a -> fa
in terms of lift:: (a -> b) -> f (a -> b)
and <*>
:您可以根据
lift:: (a -> b) -> f (a -> b)
和<*>
定义pure:: a -> fa
:
pure x = lift (const x) <*> lift (const ())
So it's equivalent either way, and usually simpler to write pure
.所以这两种方式都是等价的,而且通常写成
pure
更简单。
(This is in addition to Iceland_jack's excellent summary of the design reasons it should be this way.) (这是 Iceland_jack 对设计原因的出色总结的补充,它应该是这样的。)
Sometimes it's worth approaching questions from the opposite direction.有时从相反的方向来探讨问题是值得的。 What would you gain if the type of
pure
was (a -> b) -> f (a -> b)
?如果
pure
的类型是(a -> b) -> f (a -> b)
你会得到什么?
As someone calling pure
, that's strictly a downgrade.作为调用
pure
的人,这完全是降级。 As mentioned, (a -> b) -> f (a -> b)
is an instantiation of the current type of pure
already.如前所述,
(a -> b) -> f (a -> b)
已经是当前pure
类型的实例化。 So on the calling side, you only lose options.所以在跟注方,你只会失去选择权。
The implementation side is the dual here, though.不过,实现方面是双重的。 The more concrete a type is, the more options an implementation has.
类型越具体,实现的选项就越多。 Requiring the argument to be a function means the implementation can take advantage of that to do function-specific things.
要求参数为 function 意味着实现可以利用它来做特定于功能的事情。 Like... calling it.
就像...称呼它。 That's the only special thing you get to do with functions in Haskell. So to call it, you just need to provide it with a value of some type
a
that the caller of pure
gets to choose.这是您可以使用 Haskell 中的函数做的唯一特殊事情。因此,要调用它,您只需要为其提供某种类型的值
a
, pure
的调用者可以选择该类型。 You can get one of those by.. uh.. you can't get one of those.你可以通过.. 呃.. 你不能得到其中之一。 The only option is using
undefined
or some other bottom value that has a universally-quantified type.唯一的选择是使用
undefined
或其他一些具有通用量化类型的底值。 What are you going to do?你会怎样做?
pure f = let x = f undefined in...
? pure f = let x = f undefined in...
? How can that be helpful for implementing pure?这对实施 pure 有何帮助?
To return to the initial question, then: what would you gain if the type of pure
was (a -> b) -> f (a -> b)
?回到最初的问题,那么:如果
pure
的类型是(a -> b) -> f (a -> b)
你会得到什么? As a caller, it's strictly less useful.作为调用者,它的用处不大。 As an implementer, it provides additional power, but that additional power doesn't help you do anything useful.
作为一个实施者,它提供了额外的力量,但这种额外的力量并不能帮助你做任何有用的事情。 Where are the upsides to a more concrete type?
更具体的类型的好处在哪里?
Yes, there is more purpose for pure
than transforming functions.是的,
pure
函数比转换函数有更多的用途。 It is very common for do
blocks to end with a call to pure
. do
块以调用pure
结束是很常见的。 It's also handy for giving fallback values by combining its use with <|>
.通过将它的使用与
<|>
结合使用,它也可以方便地提供回退值。
Also, it meshes well with the underlying category theory;此外,它与基础范畴论非常吻合; but I don't really consider that a motivating reason.
但我真的不认为这是一个激励原因。 Rather it's first useful and then discovered to coincide with a previously-known category theory concept.
相反,它首先有用,然后被发现与先前已知的范畴论概念相吻合。 (Actually, historically I think it went "define something useful; realize it's the concept of monad; discover the related concept of applicative functors; realize they're useful. So it is in fact a mix of "useful first" and "theory first". But I would never defend its existence because it's there in theory -- only get excited that the theory was insightful.)
(实际上,从历史上看,我认为它是“定义一些有用的东西;意识到它是 monad 的概念;发现应用函子的相关概念;意识到它们是有用的。所以它实际上是“有用第一”和“理论第一”的混合体”。但我永远不会为它的存在辩护,因为它在理论上存在——只会为这个理论很有见地而兴奋。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.