《Haskell Programming from First Principles》一书中有一个练习,要求我在Constant a上引用Applicative。
我试过这个:
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.Constant的source code,它的定义如下:
pure _ = Constant mempty我并不真正理解Applicative和它的pure函数,即使在读完本书中的应用一章之后也是如此。
我知道这可能是一个很大的问题,但是谁能解释一下pure的作用,以及为什么他们只是在Monoid上使用一些mempty来定义pure?为什么是这个:
pure x = Constant { getConstant = x }不编译吗?我认为pure只是将a类型的一些值转换为f a类型的一些值,在本例中f就是Constant。为什么它与the pure of Identity a不同
发布于 2018-10-26 16:21:59
在这里,您的主要问题是种类。我将尝试向您解释为什么您的代码无法工作,但有关更多详细信息,我建议您阅读this excellent post on kinds。
考虑一下在ghci中请求有关应用程序的信息时返回的第一行:
λ> :info Applicative
class Functor f => Applicative (f :: * -> *) where看到那个f :: * -> *位了吗?它会告诉您预期的类型。对种类一无所知?我会尽可能给你最简单的解释。每次添加参数类型时,基本上都是在告诉您需要“另一个类型来构建该类型”。
例如,当你说Maybe a时,你会说“要有一个可能,我需要一个a”。或者,当您编写Either a b时,您会说“我的任何一种类型都取决于类型a和类型b”。您可以通过在ghci中使用:kind或:k获取有关种类的信息。考虑一下:
λ> :kind Bool
Bool :: *
λ> :kind Maybe
Maybe :: * -> *
λ> :kind Either
Either :: * -> * -> *每个"*“代表一种类型。Bool是一种简单类型。Maybe all本身需要另一种类型。Either all单独期待的是另一种类型。注意我键入时的不同之处:
λ> :kind Maybe Bool
Maybe Bool :: *现在考虑一下:
λ> :kind Either Bool
Either Bool :: * -> *看到这种* -> *类型了吗?这正是我们在被问到关于Applicative的信息时所看到的。(除了Functor和Monad之外,它也是相同的类型)。
这意味着类型类将只在最新的参数类型上运行。Either也是如此。如你所见:
λ> let fun = (++ " !")
λ> fun <$> Left "Oops"
Left "Oops"这不做任何事情,因为Either的Functor不是这两种类型的函数器:它只是最后一种类型(Either a b的b )的函数器。使用简单的Functor和fmap (或者这里的中缀版本<$>),我只能在Either a b的b上操作,这就是为什么这个可以工作:
λ> fun <$> Right "Oops"
Right "Oops !"现在,回到你想要做的事情上。你有一个newtype Constant a b,所以Constant有一个友好的* -> * -> *。现在让我们看一下这次来自:info Applicative的第二行,它将为我们提供pure的签名
class Functor f => Applicative (f :: * -> *) where
pure :: a -> f a现在请始终注意:pure签名中的a不是Constant a b的a。更糟糕的是,在本例中,pure中的a是Constant的b。因为如果你考虑到善意,如果你专门化这个签名,你会得到:
pure :: b -> Constant a b但这不是你要做的,不是吗?存储在新类型中的是类型a。并且您试图将其放入类型b中。因为a和b可以是不同的,所以它不能工作。
至于“pure做什么”的“大问题”,这确实是一个相当大的问题,我将给你一个答案的开始。
pure是一种让朴素的值a进入Applicative f的方法。正如签名所说:a -> f a。这不管用吗?好的,把你的Applicative看作是“一个上下文”(我使用一个非常通用的词,因为应用词是一个非常通用的概念)。“上下文”可能是:我工作的世界可能会失败(也就是Maybe)。可能是:在我工作的世界里,一个问题有很多答案(那就是List或[])。它可以是很多很多东西-在你的例子中,它是一个不计算任何东西的上下文,并且总是返回一个常量。问题是,在您的示例中,不可能“猜测”常量是什么。
正如我们已经看到的,常量(您的上下文)不是值。它不是pure的a。它是f的一部分。这就是实现使用Monoid和mempty的原因:您需要一种获取默认上下文的方法,并且Monoid总是有一个默认值。
最后,Applicative很难。因此,不能立即理解它们是非常正常的。专注于读取类型,尝试理解编译器告诉你的内容,它就会变得更容易。慢慢地重读这一章,慢慢来。
https://stackoverflow.com/questions/53003469
复制相似问题