首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在`Applicative`上定义`pure`函数?

如何在`Applicative`上定义`pure`函数?
EN

Stack Overflow用户
提问于 2018-10-26 15:19:30
回答 1查看 363关注 0票数 3

《Haskell Programming from First Principles》一书中有一个练习,要求我在Constant a上引用Applicative

我试过这个:

代码语言:javascript
复制
newtype Constant a b =
  Constant { getConstant :: a }
  deriving (Eq, Ord, Show)

instance Functor (Constant a) where
  fmap f (Constant x) = Constant x

instance Monoid a => Applicative (Constant a) where
  pure x = Constant { getConstant = x }
  Constant { getConstant = x } <*> Constant { getConstant = y } =
    Constant { getConstant = x <> y }

这不能编译。我偷看了Data.Functor.Constantsource code,它的定义如下:

代码语言:javascript
复制
pure _ = Constant mempty

我并不真正理解Applicative和它的pure函数,即使在读完本书中的应用一章之后也是如此。

我知道这可能是一个很大的问题,但是谁能解释一下pure的作用,以及为什么他们只是在Monoid上使用一些mempty来定义pure?为什么是这个:

代码语言:javascript
复制
pure x = Constant { getConstant = x }

不编译吗?我认为pure只是将a类型的一些值转换为f a类型的一些值,在本例中f就是Constant。为什么它与the pure of Identity a不同

EN

回答 1

Stack Overflow用户

发布于 2018-10-26 16:21:59

在这里,您的主要问题是种类。我将尝试向您解释为什么您的代码无法工作,但有关更多详细信息,我建议您阅读this excellent post on kinds

考虑一下在ghci中请求有关应用程序的信息时返回的第一行:

代码语言:javascript
复制
λ> :info Applicative
class Functor f => Applicative (f :: * -> *) where

看到那个f :: * -> *位了吗?它会告诉您预期的类型。对种类一无所知?我会尽可能给你最简单的解释。每次添加参数类型时,基本上都是在告诉您需要“另一个类型来构建该类型”。

例如,当你说Maybe a时,你会说“要有一个可能,我需要一个a”。或者,当您编写Either a b时,您会说“我的任何一种类型都取决于类型a和类型b”。您可以通过在ghci中使用:kind:k获取有关种类的信息。考虑一下:

代码语言:javascript
复制
λ> :kind Bool
Bool :: *

λ> :kind Maybe
Maybe :: * -> *

λ> :kind Either
Either :: * -> * -> *

每个"*“代表一种类型。Bool是一种简单类型。Maybe all本身需要另一种类型。Either all单独期待的是另一种类型。注意我键入时的不同之处:

代码语言:javascript
复制
λ> :kind Maybe Bool
Maybe Bool :: *

现在考虑一下:

代码语言:javascript
复制
λ> :kind Either Bool
Either Bool :: * -> *

看到这种* -> *类型了吗?这正是我们在被问到关于Applicative的信息时所看到的。(除了FunctorMonad之外,它也是相同的类型)。

这意味着类型类将只在最新的参数类型上运行。Either也是如此。如你所见:

代码语言:javascript
复制
λ> let fun = (++ " !")
λ> fun <$> Left "Oops"
Left "Oops"

这不做任何事情,因为EitherFunctor不是这两种类型的函数器:它只是最后一种类型(Either a bb )的函数器。使用简单的Functorfmap (或者这里的中缀版本<$>),我只能在Either a b的b上操作,这就是为什么这个可以工作:

代码语言:javascript
复制
λ> fun <$> Right "Oops"
Right "Oops !"

现在,回到你想要做的事情上。你有一个newtype Constant a b,所以Constant有一个友好的* -> * -> *。现在让我们看一下这次来自:info Applicative的第二行,它将为我们提供pure的签名

代码语言:javascript
复制
class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a

现在请始终注意:pure签名中的a不是Constant a ba。更糟糕的是,在本例中,pure中的aConstantb。因为如果你考虑到善意,如果你专门化这个签名,你会得到:

代码语言:javascript
复制
  pure :: b -> Constant a b

但这不是你要做的,不是吗?存储在新类型中的是类型a。并且您试图将其放入类型b中。因为ab可以是不同的,所以它不能工作。

至于“pure做什么”的“大问题”,这确实是一个相当大的问题,我将给你一个答案的开始。

pure是一种让朴素的值a进入Applicative f的方法。正如签名所说:a -> f a。这不管用吗?好的,把你的Applicative看作是“一个上下文”(我使用一个非常通用的词,因为应用词是一个非常通用的概念)。“上下文”可能是:我工作的世界可能会失败(也就是Maybe)。可能是:在我工作的世界里,一个问题有很多答案(那就是List[])。它可以是很多很多东西-在你的例子中,它是一个不计算任何东西的上下文,并且总是返回一个常量。问题是,在您的示例中,不可能“猜测”常量是什么。

正如我们已经看到的,常量(您的上下文)不是值。它不是purea。它是f的一部分。这就是实现使用Monoidmempty的原因:您需要一种获取默认上下文的方法,并且Monoid总是有一个默认值。

最后,Applicative很难。因此,不能立即理解它们是非常正常的。专注于读取类型,尝试理解编译器告诉你的内容,它就会变得更容易。慢慢地重读这一章,慢慢来。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53003469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档