这是一个有效的类型,我如何满足它? (组成两个二元函数)

时间:2014-11-04 00:59:40

标签: haskell composition

我的问题非常简单,因为任何人都开始使用haskell我一直在考虑类型,功能组成以及如何应用它们。我开始考虑((+) . (*))的结果可能是什么。

现在很明显,这个问题的解决方案是开放ghci并找出答案。所以我这样做并检查了类型:

λ> :t ((*) . (+))
((*) . (+)) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a

这种类型可能吗?我很难理解它可能是什么或它意味着什么?

再次为这个简单化的问题道歉,我试图放入函数的所有内容都失败了。我只是试图通过二元函数来开发函数组合的直觉。

2 个答案:

答案 0 :(得分:7)

不幸的是,GHC没有给出一个非常好的信息。这几乎肯定不是你想要的。默认情况下没有Num (a -> a)的实例并且实现一个实例,尽管它可能对某些事情有用,但可能导致非常不直观的运行时错误。即使在这种情况下,这个功能也不太可能有用。

让我们看看类型受限(*)(+),以简化情况并避免类型类增加的复杂性:

(*!) :: Int -> Int -> Int
(*!) = (*)

(+!) :: Int -> Int -> Int
(+!) = (+)

现在我们尝试

λ> :t (*!) . (+!)

<interactive>:1:8:
    Couldn't match type ‘Int -> Int’ with ‘Int’
    Expected type: Int -> Int
      Actual type: Int -> Int -> Int
    Probable cause: ‘(+!)’ is applied to too few arguments
    In the second argument of ‘(.)’, namely ‘(+!)’
    In the expression: (*!) . (+!)

表示我们没有将(+!)应用于足够的参数,以便能够将其结果应用于(*!)。如果我们扩展功能组合,我们会看到这一点,这可能会更清楚为什么它没有意义:

(*!) . (+!) == \x   -> (*!) ((+!) x)   -- definition of (.)
            == \x y -> (*!) ((+!) x) y -- eta-expansion
            == \x y -> ((+!) x) *! y   -- changed (*.) from prefix to infix

(*!)的左参数是一个函数,它与Int的预期类型不匹配。

使用两个参数

的函数进行组合

为了做到这一点,我们需要一个函数(b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c。幸运的是,这正是((.) . (.))的原因。

λ> :t ((.) . (.)) (*!) (+!)
((.) . (.)) (*!) (+!) :: Int -> Int -> Int -> Int

有些图书馆以名称(.:)(例如this one)提供此功能。有时候人们喜欢写它fmap . fmap(这虽然概括为不仅仅是正常的函数组合)。

虽然有点神秘,但通常会因此而避免。只是明确地写出函数几乎总是更清楚。

答案 1 :(得分:5)

这是一个有趣的问题......

首先,我会解释为什么你会得到你所做的类型。

(+)和(*)都有类型

Num a=>a->a->a

这基本上意味着他们有两个数字作为输入,并输出一个数字(应该从加法和乘法中预期)

函数组合将两个类型(a-> b)的函数链接在一起(当然,第一个的输出需要与下一个的输入相同)。

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

所以,乍一看,(+)或(*)似乎都不属于那种类型......除了在Haskell的世界中,你可以将它们视为类型

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

这是有意义的....如果你填写一个(+)的值,你得到一个函数,增加一个数字的值,例如

(+) 1 --evaluates to incrementByOne
where incrementByOne x = 1+x

所以,你可以把两者连在一起,但是......

请记住,(*)的输入必须是数字! (因为Num a=>

将(。)应用于(+)和(*)会产生类型

(Num (a -> a), Num a) => a -> (a -> a) -> a -> a

但是,它有一个奇怪的约束Num (a->a),它表明函数需要是一个数字。基本上不应该这样做,但Haskell中没有任何内容可以禁止它,因此编译器此时不会抱怨。只有当您尝试使用它执行检查的功能时。

((+) . (*)) 1 (+ 1) 2

<interactive>:16:1:
    No instance for (Num (a0 -> a0)) arising from a use of ‘it’
    In a stmt of an interactive GHCi command: print it

三个输入的类型正确,但约束除外....解释器在这里抱怨函数(+ 1)不是数字。

相关问题