将参数映射到函数 - fmap($ 3)(4+)

Šef*_*ana 1 haskell

使用$ in Learn You Haskell书中的阅读函数应用程序我找到了将$应用于函数列表的示例.

map ($ 3) [(4+), (10*), (^2), sqrt]
Run Code Online (Sandbox Code Playgroud)

我想尝试类似的东西并减少应用于一个功能的例子

fmap ($ 3) (4+) 
Run Code Online (Sandbox Code Playgroud)

但我得到的错误是我不明白的

• Non type-variable argument in the constraint: Num (a -> b)
  (Use FlexibleContexts to permit this)
• When checking the inferred type
    it :: forall a b. (Num (a -> b), Num a) => (a -> b) -> b
Run Code Online (Sandbox Code Playgroud)

你能帮我理解为什么它在第一种情况下起作用但它不在第二种情况下吗?我怎样才能达到预期的效果?

谢谢

lef*_*out 6

您需要map在第一个示例中,因为您有一个完整的容器,并且每个容器都要应用于该数字.在该示例中,您确实可以替换mapfmap,它可以在任何容器上工作(在任何仿函数上).

Prelude> fmap ($ 3) [(4+), (10*), (^2), sqrt]    -- list functor
[7.0,30.0,9.0,1.7320508075688772]
Prelude> fmap ($ 3) (Just (4+))                  -- `Maybe` functor
Just 7
Prelude> fmap ($ 3) (do y<-readLn; return (y+))  -- `IO` functor
> 100
103
Run Code Online (Sandbox Code Playgroud)

然而,(4+)它本身并不是包含在任何仿函数中的函数,它本身就是一个函数.所以,你真的不需要任何fmap:

Prelude> ($ 3) (4+)
7
Run Code Online (Sandbox Code Playgroud)

当然你可以进一步简化4+3......

如果由于某种原因,你确实需要使用fmap,无论,你需要它的操作身份的仿函数:

Prelude> :m +Data.Functor.Identity
Prelude Data.Functor.Identity> fmap ($ 3) (Identity (4+))
Identity 7
Run Code Online (Sandbox Code Playgroud)

身份仿函数是一个非常无聊的容器,它总是包含一个元素.


这不是不现实的BTW:在Haskell中,我们希望尽可能保持代码通用.你可能有一个能够处理任意仿函数的函数(更常见的是,任意monad,它们是特殊的仿函数),但是可能希望在只包含一个元素的简单上下文中使用它.或者,您可能希望将不同的monad功能堆叠为monad变换器 ; 那么你通常会从Identity"香草单子"开始.

  • @DanielMcLaury你怎么知道它们何时需要语义?你需要在"functor-y值"和"plain-ish values"之间进行分类.但是在Haskell中,类型是从头开始的黑盒子 - 多态函数无法调查它所使用的类型是否具有任何额外的结构.你可以做的是要求一个类型必须是functor包装的,但是要检查编译器必须能够清楚地将它与`fa`形式分开.例如,`Int`不是这种形式,但实际上没有办法从语言中证明这一点. (2认同)