对 (fmap length Just) [1,1,1,1] 与 fmap length $ Just [1,1,1,1] 感到困惑
Confused about (fmap length Just) [1,1,1,1] vs. fmap length $ Just [1,1,1,1]
我知道括号会强制执行不同的操作顺序,但我不太理解第一个结果:
>> (fmap length Just) [1, 2, 3]
1
鉴于以下内容非常合理 - 我们将长度函数提升到 Just 结构上,所以我们应该得到 "Just [length of list]":
>> fmap length $ Just [1, 2, 3]
Just 3
第一种情况是怎么回事?
在第一种情况下,您正在获取 Functor
的函数实例,为此 fmap = (.)
,因此:
fmap length Just [1,2,3]
=
(length . Just) [1,2,3]
=
length (Just [1,2,3])
Maybe
的 Foldable
实例表示 Nothing
的长度为 0
,Just
的长度为 1
-- 如果您认为 Maybe a
有点像最多包含一个 a
的 a
的集合,这是非常明智的。
长话短说
fmap
只能应用于两个参数,所以首先让我们主要关注 fmap length Just
- 函数上的
fmap
实例是(.)
即函数组合,所以表达式基本上是(length . Just) [1,2,3]
- 上面的表达式等价于
length (Just [1,2,3])
- 请注意,我们将
length
应用于 Maybe
值,这取决于 Maybe 的 Foldable
实例。 Foldable
是类型构造函数的类型类,而不是类型(就像 Functor
),因此根据类型构造函数采用的参数类型(即 a
在 Maybe a
)。列表 [1, 2, 3]
只是一个干扰,因为 lenght 只能查询最外层,所以表达式与 length (Just ())
. 相同
- 一个
Maybe
值只能包含 0 或 1 个值,而 Just
包含 1。所以答案是 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
看看我们得到什么。
fmap :: (a -> b) -> f a -> f b
t a Int -> f (t a) -> f Int
用 ghci 确认
> :t fmap length
fmap length :: (Functor f, Foldable t) => f (t a) -> f Int
可以肯定地说 Functions
也是 Functors
(稍后会详细介绍)。
这意味着我们可以说上面的f
是 -> a
而(t a)
是Maybe a
这没关系,因为Maybe
有一个可折叠的实例定义. IE
f (t a)
可以写成 (-> a) (Maybe a)
可以写成 a -> Maybe a
是 Just
.
的类型
类似f Int
可以写成 (-> a) Int
类似于 a -> Int
因此 fmap lenght Just
的类型定义是
> :t fmap length Just
fmap length Just :: a -> Int
但这并没有完全消除我的困惑,现在我知道我们得到了 Int
,但为什么呢?有件事让我很担心。看看上面的类型,唯一可能的方法是如果 Just 的值以某种方式被输入到长度中。
好的!将数据输入另一个函数让我想起了一些事情。啊哈!它类似于 Function composition
,我的意思是如果我们有这样的东西:
fmap length Just [1,2,3] == (length . Just) [1,2,3] == (length (Just [1,2,3])
这或许可以解释这一点。但是为什么我们要在这里编写函数。这里有些东西不符合要求。
看起来两个函数的fmap相当于它们的组合。这可能吗?
让我们go-ahead解开这个疑惑。我们需要问自己两个问题:
- Function 可以是 Functor 的实例吗?
- 如果函数是 Functor 的实例,那么它们的 fmap 实现是什么?
- Function 可以是 Functor 的实例吗?
稍微挖掘一下,我发现是的——Function 可以是仿函数的一个实例。任何函数都可以写成 a -> b
,也可以进一步写成 (->) a b
,因为->
只不过是一个接受两个参数的类型构造函数。
现在我们知道 Functors
可以接受 * -> *
类型,但是 (->)
有一种我们不能使用的 * -> * -> *
类型,但是..那 (->) a
呢,它有一种* -> *
,我们可以用它来做一个Functor的实例。
- 如果函数是 Functor 的实例,那么它们的 fmap 实现是什么?
现在我们知道函数可以是 Functor 的一个实例,是时候实现它们了。挖掘 into the source,我确实找到了 (->)
的仿函数实例
instance Functor ((->) r) where
fmap = (.)
让我们看看我们是如何实现的:
fmap
的类型是:
fmap :: (a -> b) -> f a -> f b
用 (->) a
代替 f
因为,函数也可以是函子:
fmap :: (a -> b) -> f a -> f b
( (->) r a) -> f ( (->) r b)
可以简化为:
fmap :: (a -> b) -> f a -> f b
( r -> a) -> ( r -> b)
所以以函数作为仿函数的 fmap 的最终类型定义是:
fmap :: (a -> b) -> ( r -> a) -> (r -> b)
啊哈!如果我们仔细观察,我们看到的是,它看起来完全像 function composition (.)
的类型,其中 r -> a
的输出作为第一个参数 a -> b
的输入提供给我们结果 (r -> b)
也就是说,我们可以放心地说
instance Functor ((->) r) where
fmap = (.)
因此这证明了这一点,当我们将 fmap 作为函子放在函数上时,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
A Maybe 可以包含两个值 0
和 1
,并且由于 Just
包含一些东西,我们得到 Just a
作为 1
我知道括号会强制执行不同的操作顺序,但我不太理解第一个结果:
>> (fmap length Just) [1, 2, 3]
1
鉴于以下内容非常合理 - 我们将长度函数提升到 Just 结构上,所以我们应该得到 "Just [length of list]":
>> fmap length $ Just [1, 2, 3]
Just 3
第一种情况是怎么回事?
在第一种情况下,您正在获取 Functor
的函数实例,为此 fmap = (.)
,因此:
fmap length Just [1,2,3]
=
(length . Just) [1,2,3]
=
length (Just [1,2,3])
Maybe
的 Foldable
实例表示 Nothing
的长度为 0
,Just
的长度为 1
-- 如果您认为 Maybe a
有点像最多包含一个 a
的 a
的集合,这是非常明智的。
长话短说
fmap
只能应用于两个参数,所以首先让我们主要关注fmap length Just
- 函数上的
fmap
实例是(.)
即函数组合,所以表达式基本上是(length . Just) [1,2,3]
- 上面的表达式等价于
length (Just [1,2,3])
- 请注意,我们将
length
应用于Maybe
值,这取决于 Maybe 的Foldable
实例。Foldable
是类型构造函数的类型类,而不是类型(就像Functor
),因此根据类型构造函数采用的参数类型(即a
在Maybe a
)。列表[1, 2, 3]
只是一个干扰,因为 lenght 只能查询最外层,所以表达式与length (Just ())
. 相同
- 一个
Maybe
值只能包含 0 或 1 个值,而Just
包含 1。所以答案是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
看看我们得到什么。
fmap :: (a -> b) -> f a -> f b
t a Int -> f (t a) -> f Int
用 ghci 确认
> :t fmap length
fmap length :: (Functor f, Foldable t) => f (t a) -> f Int
可以肯定地说 Functions
也是 Functors
(稍后会详细介绍)。
这意味着我们可以说上面的f
是 -> a
而(t a)
是Maybe a
这没关系,因为Maybe
有一个可折叠的实例定义. IE
f (t a)
可以写成 (-> a) (Maybe a)
可以写成 a -> Maybe a
是 Just
.
类似f Int
可以写成 (-> a) Int
类似于 a -> Int
因此 fmap lenght Just
的类型定义是
> :t fmap length Just
fmap length Just :: a -> Int
但这并没有完全消除我的困惑,现在我知道我们得到了 Int
,但为什么呢?有件事让我很担心。看看上面的类型,唯一可能的方法是如果 Just 的值以某种方式被输入到长度中。
好的!将数据输入另一个函数让我想起了一些事情。啊哈!它类似于 Function composition
,我的意思是如果我们有这样的东西:
fmap length Just [1,2,3] == (length . Just) [1,2,3] == (length (Just [1,2,3])
这或许可以解释这一点。但是为什么我们要在这里编写函数。这里有些东西不符合要求。
看起来两个函数的fmap相当于它们的组合。这可能吗?
让我们go-ahead解开这个疑惑。我们需要问自己两个问题:
- Function 可以是 Functor 的实例吗?
- 如果函数是 Functor 的实例,那么它们的 fmap 实现是什么?
- Function 可以是 Functor 的实例吗?
稍微挖掘一下,我发现是的——Function 可以是仿函数的一个实例。任何函数都可以写成 a -> b
,也可以进一步写成 (->) a b
,因为->
只不过是一个接受两个参数的类型构造函数。
现在我们知道 Functors
可以接受 * -> *
类型,但是 (->)
有一种我们不能使用的 * -> * -> *
类型,但是..那 (->) a
呢,它有一种* -> *
,我们可以用它来做一个Functor的实例。
- 如果函数是 Functor 的实例,那么它们的 fmap 实现是什么?
现在我们知道函数可以是 Functor 的一个实例,是时候实现它们了。挖掘 into the source,我确实找到了 (->)
instance Functor ((->) r) where
fmap = (.)
让我们看看我们是如何实现的:
fmap
的类型是:
fmap :: (a -> b) -> f a -> f b
用 (->) a
代替 f
因为,函数也可以是函子:
fmap :: (a -> b) -> f a -> f b
( (->) r a) -> f ( (->) r b)
可以简化为:
fmap :: (a -> b) -> f a -> f b
( r -> a) -> ( r -> b)
所以以函数作为仿函数的 fmap 的最终类型定义是:
fmap :: (a -> b) -> ( r -> a) -> (r -> b)
啊哈!如果我们仔细观察,我们看到的是,它看起来完全像 function composition (.)
的类型,其中 r -> a
的输出作为第一个参数 a -> b
的输入提供给我们结果 (r -> b)
也就是说,我们可以放心地说
instance Functor ((->) r) where
fmap = (.)
因此这证明了这一点,当我们将 fmap 作为函子放在函数上时,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
A Maybe 可以包含两个值 0
和 1
,并且由于 Just
包含一些东西,我们得到 Just a
作为 1