`<$` 函数在 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 Stringlength 是一个字符串函数,所以我们可以在 getContents 上 fmap 长度:

getContents              :: IO String
length                   ::    String -> Int
fmap length getContents  ::              IO Int

当 运行 这将读取所有标准输入,获取输入的长度,然后 return 它(作为 IO 操作)。

现在,const z 是一个函数,它忽略了它的参数并且总是 returns z。所以如果我在 getContentsfmap (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 所需要的函数。

让我们重写 fmapconst 的类型,在每个中使用单独的类型变量以避免混淆:

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)