`<$` 函数在 Functor class 中如何工作?
How does `<$` function work in the Functor class?
在 Functor class 定义中,我们将 <$
函数定义为:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
(<$) = fmap . const
const
函数定义:
const :: a -> b -> a
const x _ = x
我知道 <$
函数等价于:
\x -> fmap (const x)
fmap . const
如何等同于上面的lambda表达式?我对函数组合的理解是 const
的输出类型应该匹配 fmap
的输入类型,但是 fmap
的输入类型是函数 (a -> b)
而不是 a
这是 const
函数输出的内容。
注意:
(f . g) x = f (g x)
(见definition of (.)
)所以,
(fmap . const) x = fmap (const x)
原答案
为了具体起见,让我们使用 IO 仿函数。
fmap f
通过将 f
应用于计算结果来进行 IO 计算。
例如- getContents
是一个 IO String
,length
是一个字符串函数,所以我们可以在 getContents 上 fmap 长度:
getContents :: IO String
length :: String -> Int
fmap length getContents :: IO Int
当 运行 这将读取所有标准输入,获取输入的长度,然后 return 它(作为 IO 操作)。
现在,const z
是一个函数,它忽略了它的参数并且总是 returns z
。所以如果我在 getContents
上 fmap (const 'w')
我会:
getContents :: IO String
const 'w' :: String -> Char
fmap (const 'w') getContents :: IO Char
执行时,这将首先读入所有标准输入,然后丢弃该输入和 return 字符 'w'。
另一个答案很好地解决了问题,"How does fmap . const
equate to the lambda expression above?",所以我想解决不同的部分:
My understanding of function composition is that output type of const
should match the input type of fmap
, but the input type of fmap
is the function (a -> b)
not a
which is what the const
function outputs.
在这个答案中,我将论证 const
的输出类型确实是 fmap
所需要的函数。
让我们重写 fmap
和 const
的类型,在每个中使用单独的类型变量以避免混淆:
fmap :: Functor f => (a -> b) -> (f a -> f b)
const :: c -> (d -> c)
现在,必须要问:const
的输出类型是什么?在您的问题中,您假定输出类型为 c
(在按上述方式更正类型变量重命名之后)。但实际上这是一个小小的误会;真正的输出类型是 d -> c
!
const
的输出实际上是一个函数。现在,正如您所说,它的输出必须与 fmap
的输入相匹配。根据我们上面的命名,这意味着我们必须选择满足等式 d ~ a
(阅读:类型 d
和类型 a
是同一类型)并满足 c ~ b
。那么我们将有:
const :: b -> (a -> b)
fmap :: (a -> b) -> (f a -> f b)
fmap . const :: b -> (f a -> f b)
在 Functor class 定义中,我们将 <$
函数定义为:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
(<$) = fmap . const
const
函数定义:
const :: a -> b -> a
const x _ = x
我知道 <$
函数等价于:
\x -> fmap (const x)
fmap . const
如何等同于上面的lambda表达式?我对函数组合的理解是 const
的输出类型应该匹配 fmap
的输入类型,但是 fmap
的输入类型是函数 (a -> b)
而不是 a
这是 const
函数输出的内容。
注意:
(f . g) x = f (g x)
(见definition of (.)
)所以,
(fmap . const) x = fmap (const x)
原答案
为了具体起见,让我们使用 IO 仿函数。
fmap f
通过将 f
应用于计算结果来进行 IO 计算。
例如- getContents
是一个 IO String
,length
是一个字符串函数,所以我们可以在 getContents 上 fmap 长度:
getContents :: IO String
length :: String -> Int
fmap length getContents :: IO Int
当 运行 这将读取所有标准输入,获取输入的长度,然后 return 它(作为 IO 操作)。
现在,const z
是一个函数,它忽略了它的参数并且总是 returns z
。所以如果我在 getContents
上 fmap (const 'w')
我会:
getContents :: IO String
const 'w' :: String -> Char
fmap (const 'w') getContents :: IO Char
执行时,这将首先读入所有标准输入,然后丢弃该输入和 return 字符 'w'。
另一个答案很好地解决了问题,"How does fmap . const
equate to the lambda expression above?",所以我想解决不同的部分:
My understanding of function composition is that output type of
const
should match the input type offmap
, but the input type offmap
is the function(a -> b)
nota
which is what theconst
function outputs.
在这个答案中,我将论证 const
的输出类型确实是 fmap
所需要的函数。
让我们重写 fmap
和 const
的类型,在每个中使用单独的类型变量以避免混淆:
fmap :: Functor f => (a -> b) -> (f a -> f b)
const :: c -> (d -> c)
现在,必须要问:const
的输出类型是什么?在您的问题中,您假定输出类型为 c
(在按上述方式更正类型变量重命名之后)。但实际上这是一个小小的误会;真正的输出类型是 d -> c
!
const
的输出实际上是一个函数。现在,正如您所说,它的输出必须与 fmap
的输入相匹配。根据我们上面的命名,这意味着我们必须选择满足等式 d ~ a
(阅读:类型 d
和类型 a
是同一类型)并满足 c ~ b
。那么我们将有:
const :: b -> (a -> b)
fmap :: (a -> b) -> (f a -> f b)
fmap . const :: b -> (f a -> f b)