【问题标题】:confused about function as instance of Functor in haskell对作为 Haskell 中 Functor 实例的函数感到困惑
【发布时间】:2012-05-04 20:59:55
【问题描述】:

Functor中fmap的类型是:

fmap :: Functor f => (a -> b) -> f a -> f b

看起来,首先将函数(a -> b)应用到f a的参数上,创建一个b类型的结果,然后应用f,结果是f b

Maybe a 为例:

 fmap show (Just 1)
 result is : Just "1"

同说:

Just (show 1)

但当(->) 用作函子时(在Control.Monad.Instances 中)

import Control.Monad.Instances
(fmap show Just) 1
result is : "Just 1"

即先应用Just,再应用show。在另一个例子中,结果是一样的:

 fmap (*3) (+100) 1
 result is 303

为什么不先*3,然后+100

【问题讨论】:

    标签: haskell functor


    【解决方案1】:

    (->) rfmap 实例(即函数)实际上只是组合。来自the source itself

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

    因此,在您的示例中,我们可以将fmap 替换为(.),并进行一些转换

    fmap (*3) (+100) 1 => 
    (.) (*3) (+100) 1  =>
    (*3) . (+100) $ 1  => -- put (.) infix
    (*3) (1 + 100)     => -- apply (+100)
    (1 + 100) * 3         -- apply (*3)
    

    也就是说,函数的fmap从右到左组合它们(与(.)完全一样,这是明智的,因为它是(.))。

    换个角度看(用于(双重)确认!),我们可以使用类型签名:

    -- general fmap
    fmap :: Functor f => (a -> b) -> f a -> f b
    
    -- specialised to the function functor (I've removed the last pair of brackets)
    fmap :: (a -> b) -> (r -> a) -> r -> b 
    

    所以首先需要将r 类型的值(第三个参数)转换为a 类型的值(通过r -> a 函数),以便a -> b 函数可以将其转换为b 类型的值(结果)。

    【讨论】:

    【解决方案2】:

    Functor中fmap的类型是:

    fmap :: Functor f => (a -> b) -> f a -> f b
    

    看起来,首先将函数 (a -> b) 应用于 f a 的参数 创建 b 类型的结果,然后对其应用 f,结果为 f b

    那是fmap 的类型,但是你对那个类型的解释是错误的。

    您似乎假设f a 有一个参数,并且该参数的类型为a

    考虑xs :: [a]

    • 也许是xs = []
    • 也许是xs = [x1]
    • 也许是xs = [x1, x2]
    • ...

    type f a 是一个函子 f,只有一个类型参数 a。但是f a 类型的 不一定采用F x 的形式,从上面的第一种和第三种情况可以看出。

    现在考虑fmap f xs

    • 也许是fmap f xs = []
    • 也许是fmap f xs = [f x1]
    • 也许是fmap f xs = [f x1, f x2]
    • ...

    我们根本不需要申请f(第一种情况)!或者我们可能会多次应用它(第三种情况)。

    我们所做的是将a 类型的东西替换为b 类型的东西。但是我们保持较大的结构不变 --- 没有添加新元素,没有删除任何元素,它们的顺序保持不变。


    现在让我们考虑函子(c ->)。 (记住,仿函数只接受一个类型参数,所以(->) 的输入是固定的。)

    c -> a 是否包含a?它可能根本不包含任何as,但是当我们给它一个c 时,它可以以某种方式凭空变魔术。但是来自fmap 的结果具有c -> b 类型:当我们看到c 时,我们只需要提供一个b

    所以我们可以说fmap f x = \y -> f (x y)

    在这种情况下,我们按需应用f --- 每次我们返回的函数被应用时,f 也被应用。

    【讨论】:

    • 是的,您的回答很棒!我犯了一个大错误。非常感谢。
    • 我将“类型参数”与具体参数混淆了
    【解决方案3】:

    需要以这种方式定义以使类型生效。正如您所指出的,fmap 的类型是:

    fmap :: Functor f => (a -> b) -> f a -> f b
    

    让我们考虑函子f((->) c) 的情况

    注意:我们实际上想把它写成(c ->),即来自c的函数,但Haskell不允许我们这样做。)

    那么f a实际上就是((->) c a),相当于(c -> a),同样对于f b,所以我们有:

    fmap :: (a -> b) -> (c -> a) -> (c -> b)
    

    即我们需要两个函数:

    • f :: a -> b
    • g :: c -> a

    并构造一个新函数

    • h :: c -> b

    但是只有一种方法可以做到这一点:你必须先申请 g 以获得 a 类型的东西,然后申请 f 以获得 b 类型的东西,这意味着你 必须定义

    instance Functor ((->) c) where
        fmap f g = \x -> f (g x)
    

    或者,更简洁,

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

    【讨论】:

      【解决方案4】:

      fmap for (->) 的定义类似于 fmap = (.)。 所以,(fmap f g) x(f . g) xf (g x)。在你的情况下(*3) ((+100) 1),等于3 * (100 + 1),结果是303

      【讨论】:

        【解决方案5】:

        为了形成一个函数类型,你需要为(->)提供2种参数,即单输入参数类型和返回类型。

        一个Functor只能接受1个类型参数,所以你必须确定输入参数类型(因为它是从左到右的第一个),这使得函数的返回类型成为Functor的类型参数.

        所以对于函数(Functor)a->b,你需要给fmap一个b->xxx类型的函数ff而不是a->xxx才能工作,这意味着函数ff只能在a之后应用->b 适用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-17
          • 2021-12-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多