当我用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)不满足。