困惑(fmap length Just)[1,1,1,1] vs. fmap length $ Just [1,1,1,1]

时间:2018-03-05 04:11:11

标签: haskell functional-programming functor

我知道parens强制执行不同的操作顺序,但我不太了解第一个结果:

>> (fmap length Just) [1, 2, 3]
1

以下内容非常有意义 - 我们正在Just结构上提升长度函数,因此我们应该得到" Just [list of list]":

>> fmap length $ Just [1, 2, 3]
Just 3

第一种情况发生了什么?

2 个答案:

答案 0 :(得分:10)

在第一种情况下,您将获得Functor的函数实例,其fmap = (.)为:{/ p>

fmap length Just [1,2,3]
=
(length . Just) [1,2,3]
=
length (Just [1,2,3])

Foldable的{​​{1}}个实例表示Maybe的长度为Nothing,而0的长度为Just - - 如果您认为1有点像Maybe a的集合,其中最多只有一个a,那么这是非常明智的。

答案 1 :(得分:0)


TL; DR
  1. fmap只能应用于两个参数,因此首先让我们主要关注fmap length Just
  2. 函数上的fmap实例是(.) 即函数组合,因此表达式基本上是(length . Just) [1,2,3]
  3. 上面的表达式等效于length (Just [1,2,3])
  4. 请注意,我们将length应用于Maybe值,这取决于Maybe的Foldable实例。 Foldable是类型构造函数上的类型类,而不是类型(就像Functor一样),因此实例根据类型构造函数所采用的参数的类型(即{{1} a中的})。列表Maybe a只是一个干扰,因为长度只能查询最外层,因此表达式与[1, 2, 3]相同。
  5. length (Just ())值只能包含0或1个值,而Maybe只能包含1。因此答案是Just

除了丹尼尔所说的话,我想详细解释一下我们是如何遇到的。

给定的表达式为1,可以进一步写为fmap length Just [1,2,3]

让我们看看我们拥有的所有三个函数的类型定义

((fmap length) Just) [1,2,3]

现在让我们将类型替换为> :t fmap fmap :: Functor f => (a -> b) -> f a -> f b > :t length length :: Foldable t => t a -> Int > :t Just Just :: a -> Maybe a ,然后看看我们得到了什么。

fmap

用ghci确认

fmap :: (a    ->  b) ->  f a      -> f b
         t a     Int ->  f (t a)  -> f Int

可以肯定地说> :t fmap length fmap length :: (Functor f, Foldable t) => f (t a) -> f Int 也是Functions稍后会详细介绍)。

这意味着我们可以说上面的Functorsf,而 -> a(t a),这是可以的,因为Maybe a有一个可折叠的实例定义。即 Maybe可以写为 f (t a),也可以写成 (-> a) (Maybe a)的类型, a -> Maybe a

类似Just的{​​{1}}可以写为f Int

因此 (-> a) Int的类型定义为

 a -> Int

但这并不能完全消除我的困惑,现在我知道我们得到了一个fmap lenght Just,但是为什么?令我担心的事。看一下上面的类型,唯一可行的方法是,如果将Just的值以某种方式输入到长度中。

Okieee!将数据输入另一个函数让我想起了什么。啊!类似于> :t fmap length Just fmap length Just :: a -> Int ,我的意思是如果我们有类似的东西:

Int

这也许可以解释这个。但是我们为什么要在这里组成这些功能。这里有些不合要求的东西。

看起来两个函数的fmap等同于它们的组成。那有可能吗?


我们继续进行下去,消除这个疑问。我们需要问自己两个问题:

  1. Function可以是Functor的实例吗?
  2. 如果函数是Functor的实例,它们的fmap实现是什么?

  1. Function可以是Functor的实例吗?

稍作挖掘后,我发现是的-函数可以是函子的实例。任何函数都可以写为Function composition,也可以写成fmap length Just [1,2,3] == (length . Just) [1,2,3] == (length (Just [1,2,3]) ,因为 a -> b只是带有两个参数的类型构造函数。

现在我们知道 (->) a b可以接受类型为->的类型,但是Functors拥有我们不能使用的类型* -> *,但是.. (->)有一种* -> * -> *,我们可以用它来创建Functor的实例。


  1. 如果函数是Functor的实例,它们的fmap实现是什么?

现在我们知道函数可以是Functor的实例,现在是实现它们的时候了。挖掘into the source,我确实找到了 (->) a

的仿函数实例
* -> *

让我们看看如何实现该实现:

(->)的类型是:

instance  Functor ((->) r) where
    fmap = (.)

fmap代替fmap :: (a -> b) -> f a -> f b ,因为函数也可以是函子:

(->) a

可以简化为:

f

因此具有函子功能的fmap的最终类型定义为:

fmap :: (a  ->  b) ->  f a          -> f b
                       ( (->) r a)  -> f ( (->) r b) 

啊!如果我们仔细观察,我们会看到的是fmap :: (a -> b) -> f a -> f b ( r -> a) -> ( r -> b) 的类型,其中fmap :: (a -> b) -> ( r -> a) -> (r -> b) 的输出将作为第一个参数 function composition (.)的输入被馈送给我们结果r -> a

这意味着,我们可以放心地说

a -> b

这证明了这一点,当我们将fmap作为函子作为函数使用时,fmap的实例就是函数组合。

回想我们的问题:

(r -> b)

这可以看作是:

instance  Functor ((->) r) where
    fmap = (.)

答案:

((fmap length) Just) [1,2,3]

也许也许可以包含两个值fmap length Just [1,2,3] == (length . Just) [1,2,3] == (length (Just [1,2,3]) > (length (Just [1,2,3]) == 1 ,并且由于0包含某些内容,因此我们将1称为Just