【问题标题】:Haskell. Functions in Num expressions哈斯克尔。 Num 表达式中的函数
【发布时间】:2016-05-16 12:08:28
【问题描述】:

我开始学习 Haskell。在学习教程时,我发现以下示例可以在算术表达式中使用函数:

module FunNat where

    instance Num a => Num (t -> a) where
        (+) = fun2 (+)
        (*) = fun2 (*)
        (-) = fun2 (-)

        abs      = fun1 abs
        signum   = fun1 signum

        fromInteger = const . fromInteger

    fun1 :: (a -> b) -> ((t -> a) -> (t -> b))
    fun1 = (.)

    fun2 :: (a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))
    fun2 op a b = \t -> a t `op` b t

该示例有效。但我无法理解(+) 函数是如何转换为两个参数的函数的。据我所知,每个(+) 都替换为fun2 (+)。 fun2 相当于一个参数的函数\t -> a t 'op' b t,但我们应该有一个两个参数的函数(类似于(\t1 t2 -> (\x -> x ) t1 + (\x -> x) t2))。我认为这里应该应用一些 Haskell 类型的基本概念,但我不知道它们是什么。

编辑 1

我知道fun2 是三个参数的函数。我无法理解内部表达转换。我的推理方式如下:(+) 3 4 = (+) (\x->3) (\x->4) = fun2 (+) (\x->3) (\x->4) = \t -> (\x->3) t + (\x->4) t 这是什么t?或者我的推理哪里错了?是否有必要换一种方式思考?

编辑 2

我认为我已经对这个问题有所了解(谢谢大家!)。所以:

  1. 当我们编写(+) 3 4 时 - 在这种情况下,使用来自Num 的简单操作,没有来自FunNat 的特殊功能。要从FunNat 使用(+),必须写成fun2 (+) 3 4 或fun2 (+) (\x -> 3) (\x -> 4)。但是这些表达式要求对任何第三个参数进行评估;

  2. 为了演示 FunNat 的特定功能,我们可以使用以下示例 ((*)-(+))(取自教程)或其他形式 - (-) (*) (+)。两个表达式都有两个参数。在这种情况下,我们有:((*)-(+)) = fun2 (-) (*) (+) = \t -> (*) t - (+) t = \t -> (\t1 t2 -> t1 * t2) t - (\t1 t2 -> t1 + t2) t = (\t t2 -> t * t2) - (\t t2 -> t + t2) = \t t2 -> (t * t2) - (t + t2)。最后的表达式只需要Num。我希望所有这些都是正确的

  3. 正如教程中解释的那样,但我不明白为什么,使用 (*) 和 (+) 作为 fun2 的参数的可能性,期望 (t->a) 基于以下(取自教程):t1 -> t2 -> a = t1->a' 其中a' = t2 -> a。这里使用了curring。

所以所有必要的事实都在表面上,但我没有考虑类型推断的重要机制。

【问题讨论】:

  • fun2 (+) 根据定义等同于\a b t -> a t + b t。它实际上是 3 个参数的函数 - 它的类型是 Num a => {-1-}(t -> a) -> {-2-}(t -> a) -> {-3-}t -> a
  • 感谢您的回答。我试图澄清问题。
  • @您的编辑。t 是 lambda 绑定的变量。 (\x -> 3) t 只是 3 所以\t -> 3 + 4 是最终表达式。
  • 但是为了计算\t -> 3 + 4,我们需要传递任何t。我的意思是,如果我们输入let f = \t -> 3 + 4,那么为了得到我们需要的结果,例如输入 f 42。我的意思是,如果我们最终得到 \t -> (\x->3) t + (\x->4) t 表达式是正确的,我们需要一些额外的 any t 来评估它,即 (+) 3 4 42。还是不行?
  • (+) 3 4 = (+) (\x->3) (\x->4) -- 这大错特错。 3 的类型为 Integer,但 \x -> 3 的类型为 a -> Integer。这两者不相等。至于其余的:

标签: function haskell


【解决方案1】:

(+) 的类型是Num a => a -> a -> a。由于您正在为 Num a => t -> a 类型创建 Num 的实例,因此请考虑显式签名

(+) :: Num a => (t -> a) -> (t -> a) -> (t -> a)(1)

或等效地(通过柯里化)

(+) :: Num a => (t -> a) -> (t -> a) -> t -> a(2)

这句话的意思是:“如果你给我 2 个策略来从 t 生成 Num a => a,我可以创建一个新策略来从 t 生成 Num a => a,使用 Num.(+) a's"的实例

我认为让你感到困惑的部分是这个特定的(+) 可以被视为一个返回函数 (1) 的 2 个参数的函数,或者返回一个值 (2) 的 3 个参数的函数。由于 Haskell 中的函数是柯里化的,因此它们实际上是一回事。

【讨论】:

    【解决方案2】:

    在类型签名中,括号关联到右侧,因此:

    f :: a -> (b -> c -> d)
    

    等同于:

    f :: a -> b -> c -> d
    

    所以下面指出的括号是多余的:

    fun2 :: (a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))
                             ^                                ^
    

    并移除它们:

    fun2 :: (a -> b -> c) -> (t -> a) -> (t -> b) -> (t -> c)
              arg1             arg2        arg3
    

    【讨论】:

    • 感谢您的回答。我试图澄清问题。
    猜你喜欢
    • 1970-01-01
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多