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

Mik*_*H-R 9 haskell composition

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

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

?> :t ((*) . (+))
((*) . (+)) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

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

再次为这个简单化的问题道歉,我试图加入到函数中的一切都失败了.我只是试图通过二元函数来开发函数组合的直觉.

Dav*_*vid 7

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

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

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

(+!) :: Int -> Int -> Int
(+!) = (+)
Run Code Online (Sandbox Code Playgroud)

现在,当我们尝试

?> :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: (*!) . (+!)
Run Code Online (Sandbox Code Playgroud)

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

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

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

使用两个参数的函数进行组合

为了做到这一点,我们需要一个功能(b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c.幸运的是,这正是如此((.) . (.)).

?> :t ((.) . (.)) (*!) (+!)
((.) . (.)) (*!) (+!) :: Int -> Int -> Int -> Int
Run Code Online (Sandbox Code Playgroud)

有些库在名称下提供此功能(.:)(如此).有时人们喜欢写它fmap . fmap(虽然这不仅仅是普通的功能组合).

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


jam*_*idh 5

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

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

(+)和(*)都有类型

Num a=>a->a->a
Run Code Online (Sandbox Code Playgroud)

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

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

(.)::(b->c)->(a->b)->a->c
Run Code Online (Sandbox Code Playgroud)

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

(+)::Num a=>a->(a->a)
Run Code Online (Sandbox Code Playgroud)

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

(+) 1 --evaluates to incrementByOne
where incrementByOne x = 1+x
Run Code Online (Sandbox Code Playgroud)

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

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

将(.)应用于(+)和(*)会产生您的类型

(Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

但是,它有一个奇怪的约束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
Run Code Online (Sandbox Code Playgroud)

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