当我用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
。
是什么让它如此怪异,它预计y
和y -> y
是Num
实例。 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 5
是25
,我相信是你想要的。
请注意, 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
是从a
到a
的函数,也是一个数字。 (*)
期望的一个函数的原因是使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)
不满足。