我无法理解haskell中的作曲方式,
例如:
((.) . (+)) 3 (*2) 9 = 9 * 2 + 3 = 21
这个答案是由ghci产生的
对我来说,应该是:
((.) . (+)) 3 (*2) 9 = (3+9) * 2
类似地,我无法理解为什么((.) . (+))
的类型是Num c => c -> (a -> c) -> a -> c
你可以试着解释一下吗?
答案 0 :(得分:1)
在此表达式中,(.)
再次应用于(.)
和(+)
两个参数。让我们一步一步。首先,将(.)
部分应用于(.)
。我们将明确地写下类型签名,使用不同的类型变量来区分两者:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) :: (t -> u) -> (s -> t) -> s -> u
现在看一下(.) (.)
的类型(部分应用程序)。 (这可能是最棘手的一步,因为所涉及的类型表达式的大小。下一步是相同的,但类型更简单。)第一个参数类型为(b -> c)
,与{{1}统一},意思是
(t -> u) -> (s -> t) -> s -> u
当我们执行部分应用程序时,我们将第一个参数删除到b ~ t -> u
c ~ (s -> t) -> s -> u
(因为我们已经给它一个值)并在替换剩余的(.)
和{之后查看类型{1}}带有新类型。
b
因为c
是右关联的,我们可以删除一些多余的括号来获取
(.) :: (b -> c) -> (a -> b ) -> a -> c
(.) (.) :: (a -> (t -> u)) -> a -> ((s -> t) -> s -> u)
现在,我们将 应用于->
(同样,为了清晰起见,使用新的类型变量)。 (.) (.) :: (a -> t -> u) -> a -> (s -> t) -> s -> u
的第一个参数的类型为(+) :: Num v => v -> v -> v
,我们需要与(.) (.)
统一。换句话说,我们会使用a -> t -> u
<替换v -> v -> v
类型中a
,t
和u
的所有匹配项/ p>
(.) (.)
正如您所看到的(在我们添加v
约束之后),这相当于您看到的类型:
(.) (.) :: (a -> t -> u) -> a -> (s -> t) -> s -> u
(.) (.) (+) :: v -> (s -> v) -> s -> v
对于正在评估的实际表达式,
Num v
请记住-- v ~ c, s ~ a
Num v => v -> (s -> v) -> s -> v
Num c => c -> (a -> c) -> a -> c
((.) . (+)) 3 (*2) 9
从左到右工作。首先,(.)
适用于f . g = \x -> f (g x)
。
(.) . (+)
3
的部分应用适用于((.) . (+)) 3 == (.) ((+) 3)
== (.) (3 +)
:
.
最后,此功能适用于9:
(* 2)