部分应用的函数类型 (a ->) 作为 Haskell 中的 Functor 实例
Partially applied function type (a ->) as Functor instance in Haskell
我正在阅读 "Programming in Haskell" 这本书(第二版),偶然发现练习 2,第 12 章,第 2 部分:
instance Functor ((->) a) where
fmap = TODO
答案是:
instance Functor ((->) a) where
fmap = (.)
这让我挠了挠头好一阵子。我想它在直觉层面上对我来说确实有意义(部分应用函数类型 a ->
是一个函子,而组合是 fmap
),但我认为一些很好的例子会巩固我对练习的理解。
我想到了这两个:
main = do
putStrLn . show $ (fmap (+1) (*2)) (5 :: Int)
putStrLn . show $ (fmap (show) (+1)) 3
我的示例是否正确说明了练习?
fmap
给定两个参数:
- 部分应用函数(函数)
- 另一个部分应用的函数(仿函数)
更新
fmap
给定两个参数:
- function(函数)
- 另一个函数(函子)
我觉得很奇怪,我不确定我的概念是否正确。
我在 SO 上看到了一些类似的问题(比如 ) where this one 几乎是我要找的,但不完全是(我只是在寻找函子的例子,没有别的 - 没有应用程序,也没有单子)。
真的没有比这更重要的了,对于一个函子 f
, fmap
的实现(已知对于任何可能的 f
最多只有一个实现)必须具有类型 (a -> b) -> f a -> f b
,并满足两个函子定律:
fmap id = id
fmap (g . h) = fmap g . fmap h
当 f
是类型构造函数时 (->) r
- 即当 f a
表示 r -> a
- 那么所需的类型签名是:
(a -> b) -> (r -> a) -> (r -> b)
(最后一对括号是不必要的,但我把它们留在里面是因为它使 "pattern" 更容易看到),很容易看到正是 [= 的签名22=]运算符。
至于这两个定律,很明显,当您写下它们所说的内容时,它们必须成立。我将通过详细地写出所有内容来证明它们:
fmap id = (.) id
= \g -> id . g
= \g -> (\a -> id (g a))
= \g -> (\a -> g a)
= \g -> g
= id
和
fmap (g . h) = (.) (g . h)
= \g1 -> (g . h) . g1
= \g1 -> \a -> ((g . h) . g1) a
= \g1 -> \a -> g (h (g1 a))
(fmap g) . (fmap h) = ((.) g) . ((.) h)
= \g1 -> ((.) g) (h . g1)
= \g1 -> g . h . g1
= \g1 -> \a -> g (h (g1 a))
所以那些也是一样的。
(不要太担心最后的推导 - 通常这些事情似乎很难遵循如何从一行到下一行的逻辑,即使在这里它们基本上都是使用的定义组合。这实际上只是表达了函数组合是关联的这一明显且众所周知的事实。无论如何,这是一个普遍的结果,除了我相信某些病态类型外,如果满足第一个函子定律,那么第二个将总是自动满足。)
重要的是:当 f
被定义为 f a = r -> a
时,组合运算符与 fmap
具有相同的类型,并且满足两个函子定律 - 因此组合是一个fmap
的合法定义(以及 仅 这样的定义)为 f
创建一个 Functor
实例。真的没有比这更重要的了,至少在形式上是这样。
我正在阅读 "Programming in Haskell" 这本书(第二版),偶然发现练习 2,第 12 章,第 2 部分:
instance Functor ((->) a) where
fmap = TODO
答案是:
instance Functor ((->) a) where
fmap = (.)
这让我挠了挠头好一阵子。我想它在直觉层面上对我来说确实有意义(部分应用函数类型 a ->
是一个函子,而组合是 fmap
),但我认为一些很好的例子会巩固我对练习的理解。
我想到了这两个:
main = do
putStrLn . show $ (fmap (+1) (*2)) (5 :: Int)
putStrLn . show $ (fmap (show) (+1)) 3
我的示例是否正确说明了练习?
fmap
给定两个参数:
- 部分应用函数(函数)
- 另一个部分应用的函数(仿函数)
更新
fmap
给定两个参数:
- function(函数)
- 另一个函数(函子)
我觉得很奇怪,我不确定我的概念是否正确。
我在 SO 上看到了一些类似的问题(比如
真的没有比这更重要的了,对于一个函子 f
, fmap
的实现(已知对于任何可能的 f
最多只有一个实现)必须具有类型 (a -> b) -> f a -> f b
,并满足两个函子定律:
fmap id = id
fmap (g . h) = fmap g . fmap h
当 f
是类型构造函数时 (->) r
- 即当 f a
表示 r -> a
- 那么所需的类型签名是:
(a -> b) -> (r -> a) -> (r -> b)
(最后一对括号是不必要的,但我把它们留在里面是因为它使 "pattern" 更容易看到),很容易看到正是 [= 的签名22=]运算符。
至于这两个定律,很明显,当您写下它们所说的内容时,它们必须成立。我将通过详细地写出所有内容来证明它们:
fmap id = (.) id
= \g -> id . g
= \g -> (\a -> id (g a))
= \g -> (\a -> g a)
= \g -> g
= id
和
fmap (g . h) = (.) (g . h)
= \g1 -> (g . h) . g1
= \g1 -> \a -> ((g . h) . g1) a
= \g1 -> \a -> g (h (g1 a))
(fmap g) . (fmap h) = ((.) g) . ((.) h)
= \g1 -> ((.) g) (h . g1)
= \g1 -> g . h . g1
= \g1 -> \a -> g (h (g1 a))
所以那些也是一样的。
(不要太担心最后的推导 - 通常这些事情似乎很难遵循如何从一行到下一行的逻辑,即使在这里它们基本上都是使用的定义组合。这实际上只是表达了函数组合是关联的这一明显且众所周知的事实。无论如何,这是一个普遍的结果,除了我相信某些病态类型外,如果满足第一个函子定律,那么第二个将总是自动满足。)
重要的是:当 f
被定义为 f a = r -> a
时,组合运算符与 fmap
具有相同的类型,并且满足两个函子定律 - 因此组合是一个fmap
的合法定义(以及 仅 这样的定义)为 f
创建一个 Functor
实例。真的没有比这更重要的了,至少在形式上是这样。