我的问题非常简单,因为任何人都开始使用haskell我一直在考虑类型,功能组成以及如何应用它们。我开始考虑((+) . (*))
的结果可能是什么。
现在很明显,这个问题的解决方案是开放ghci并找出答案。所以我这样做并检查了类型:
λ> :t ((*) . (+))
((*) . (+)) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
这种类型可能吗?我很难理解它可能是什么或它意味着什么?
再次为这个简单化的问题道歉,我试图放入函数的所有内容都失败了。我只是试图通过二元函数来开发函数组合的直觉。答案 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)不是数字。