当我用Haskell编写*时发生了什么?

我正试图理解的结果

(*) . (+) 

在Haskell。 我知道组合运算符只是math函数的标准组成 – 所以

 (f . g) = f (gx) 

但:

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

我正在努力理解这种types的签名。 我希望能够做到这样的事情:

 ((*) . (+)) 1 2 :: Num a => a -> a = (* (+ 1 2)) 

是什么意思 (*) 。 (+)的types签名? 我尝试了一些类似的东西(只是和它的签名匹配):

 ((*) . (+)) 1 (\x -> x + 1) 1 

但是这不能编译。 我正在尝试通过合成这些逻辑步骤,但我不完全了解如何得到这个结果(以及结果什么)。

我理解你的感受。 我发现function组合起初也很难把握。 帮助我了解事情的是types签名。 考虑:

 (*) :: Num x => x -> x -> x (+) :: Num y => y -> y -> y (.) :: (b -> c) -> (a -> b) -> a -> c 

现在当你写(*) . (+) (*) . (+)它实际上和(.) (*) (+) (即(*)(.)的第一个参数, (+)(.)的第二个参数):

 (.) :: (b -> c) -> (a -> b) -> a -> c |______| |______| | | (*) (+) 

因此, (*) (即Num x => x -> x -> x )的types签名与b -> c

 (*) :: Num x => x -> x -> x -- remember that `x -> x -> x` | |____| -- is implicitly `x -> (x -> x)` | | b -> c (.) (*) :: (a -> b) -> a -> c | | | |‾‾‾‾| Num x => xx -> x (.) (*) :: Num x => (a -> x) -> a -> x -> x 

因此, (+) (即Num y => y -> y -> y )的types签名与Num x => a -> x

 (+) :: Num y => y -> y -> y -- remember that `y -> y -> y` | |____| -- is implicitly `y -> (y -> y)` | | Num x => a -> x (.) (*) (+) :: Num x => a -> x -> x | | | | |‾‾‾‾| |‾‾‾‾| Num y => yy -> yy -> y (.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y 

我希望澄清Num (y -> y)Num y来自哪里。 你留下了一个非常奇怪的types函数(Num (y -> y), Num y) => y -> (y -> y) -> y -> y

是什么让它如此怪异,它预计yy -> yNum实例。 y应该是Num一个例子是可以理解的,但是y -> y ? 让y -> y成为Num一个例子似乎是不合逻辑的。 这不可能是正确的。

然而,当你看看函数的组成实际上是什么时,这是有道理的:

 ( f . g ) = \z -> f ( gz) ((*) . (+)) = \z -> (*) ((+) z) 

所以你有一个函数\z -> (*) ((+) z) 。 因此z必须显然是Num一个实例,因为(+)被应用于它。 因此, \z -> (*) ((+) z)Num t => t -> ...其中...(*) ((+) z) ,我们将find在一瞬间。

因此((+) z)Num t => t -> t因为它需要多一个数字。 但是,在将其应用于另一个数字之前,将(*)应用于该数字。

因此(*)期望((+) z)Num一个实例,这就是为什么t -> t有望成为Num一个实例。 (t -> t) -> t -> t和约束Num (t -> t)相加,得到types(Num (t -> t), Num t) => t -> (t -> t) -> t -> t

你真正想要结合(*)(+)是使用(.:)

 (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d f .: g = \xy -> f (gxy) 

因此(*) .: (+)\xy -> (*) ((+) xy) 。 现在有两个论点给(+)确保((+) xy)确实只是Num t => t而不是Num t => t -> t

因此((*) .: (+)) 2 3 5(*) ((+) 2 3) 5(*) 5 525 ,我相信是你想要的。

请注意, f .: g也可以写成(f .) . g (f .) . g(.:)也可以定义为(.:) = (.) . (.) (.:) = (.) . (.) 。 你可以在这里读更多关于它的内容:

什么(f。)。 g的意思是在Haskell?

希望有所帮助。

(*)(+)都有types签名Num a => a -> a -> a现在,如果你编写它们,你会得到一些时髦的东西。

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

这是因为(*)(+)期待两个“参数”。

(+)有一个参数给你一个函数。 的. 运算符期望这个函数(你看到的a -> a )。

这是(*) . (+)的含义(*) . (+) (*) . (+)

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

(*) . (+) (*) . (+)xfy映射到((x +) * f) y其中f是从aa的函数,也是一个数字。 (*)期望的一个函数的原因是使types匹配,而它需要两个参数,但该函数必须是一个数字,因为(*)只能用于数字。

真的,这个function根本就没有意义。

首先是一些扩展:

 {-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-} 

正如其他答案显示,你的function是

 weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a weird xg = (x +) * g 

但是这个函数确实有非奇怪的语义。

有一个差异列表的概念。 因此,有一个不同的整数概念。 我已经看到他们只用于依赖types的设置(例如在这里 ,但这不是唯一的情况)。 定义的相关部分是

 instance Enum DiffInt where toEnum n = (n +) fromEnum n = n 0 instance Num DiffInt where n + m = n . m n * m = foldr (+) id $ replicate (fromEnum n) m 

这在Haskell中没有多大意义,但对依赖types可能有用。

现在我们可以写

 test :: DiffInt test = toEnum 3 * toEnum 4 

要么

 test :: DiffInt test = weird 3 (toEnum 4) 

在两个例子中,从fromEnum test == 12

编辑

可以避免使用TypeSynonymInstances扩展:

 {-# LANGUAGE FlexibleContexts, FlexibleInstances #-} weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a weird xg = (x +) * g instance (Enum a, Num a) => Enum (a -> a) where toEnum n = (toEnum n +) fromEnum n = fromEnum $ n (toEnum 0) instance (Enum a, Num a) => Num (a -> a) where n + m = n . m n * m = foldr (+) id $ replicate (fromEnum n) m type DiffInt = Int -> Int 

像以前一样,我们可以写

 test' :: DiffInt test' = weird 3 (toEnum 4) 

但现在我们也可以写

 -- difference ints over difference ints type DiffDiffInt = DiffInt -> DiffInt test'' :: DiffDiffInt test'' = weird (toEnum 3) (toEnum (toEnum 4)) 

 main = print $ fromEnum $ fromEnum test' 

打印12

编辑2更好的链接添加。

让:

 m = (*) a = (+) 

然后

 (ma) x = (m (ax)) = m (ax) 

现在m期望一个Num a作为一个参数,另一方面(ax) ,即(x +)是一个一元函数(a -> a)的定义(+) 。 我想是什么发生了,是GHC试图统一这两种types,所以,如果你有一个既是一个数字和一元函数的types, m可以采取一个数字和一元函数,并返回一元函数,因为他们被认为相同的types。

正如@Syd所指出的,这种统一对于任何正常的数字types(例如整数和浮点数)都是没有意义的。

这里有很好的答案,但让我快速指出你出错的几个步骤。

首先,function构成的正确定义是

 (f . g) x = f (gx) 

你省略了LHS上的x 。 接下来,你应该记住,在Haskell中, hxy(hx) y是一样的。 所以,与你所期望的相反,

 ((*) . (+)) 1 2 = (((*) . (+)) 1) 2 = ((*) ((+) 1)) 2 = ((+) 1) * 2, 

现在你明白为什么失败了。 也,

 ((*) . (+)) 1 (\x -> x + 1) 1 

不起作用,因为约束Num (Int -> Int)不满足。