Haskell 和功能组合
Haskell and function composition
我在 Haskell 中学习了一些基本的函数组合,当我在玩的时候,我意识到了一些我无法真正解释的事情。当我使用以下代码块时,编译器似乎对此很满意并且工作正常:
doSomeX x = if x==7 then True else False
doSomeY (x,y) = x+y+1
doSomeXY = doSomeX.doSomeY
然而,当我将 doSomeY 拆分为 2 个参数而不是一对时,即:
doSomeX x = if x==7 then True else False
doSomeY x y = x+y+1
doSomeXY = doSomeX.doSomeY
我收到以下错误:
No instance for (Num a0) arising from a use of `doSomeY'
The type variable `a0' is ambiguous
Relevant bindings include
doSomeXY :: a0 -> Bool (bound at test.hs:21:1)
Note: there are several potential instances:
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Num Integer -- Defined in `GHC.Num'
instance Num Double -- Defined in `GHC.Float'
...plus three others
In the second argument of `(.)', namely `doSomeY'
In the expression: doSomeX . doSomeY
In an equation for `doSomeXY': doSomeXY = doSomeX . doSomeY
不过我真的不明白为什么。在这两种情况下,doSomeY
的 return 类型与函数 doSomeX
的 arg 类型相同,为什么 doSomeY
的输入类型会有所不同?我是否遗漏了一些基本知识?
谢谢
差异是由 doSomeY
在第一种情况下产生一个数字与在第二种情况下产生一个函数引起的,当应用于一个参数时。
给定
doSomeX x = if x==7 then True else False
doSomeY x y = x+y+1
我们可以推断类型(这些可能不是最通用的类型,但它们可以满足我们的目的):
doSomeX :: Int -> Bool
doSomeY :: Int -> Int -> Int
记住类型签名中的->
关联到右边,所以doSomeY
的类型等同于
doSomeY :: Int -> (Int -> Int)
考虑到这一点,考虑(.)
的类型:
(.) :: (b -> c) -> (a -> b) -> a -> c
如果你定义
doSomeXY = doSomeX.doSomeY
...相当于(.) doSomeX doSomeY
,意思是(.)
的第一个参数是doSomeX
,第二个参数是doSomeY
。因此,类型 b -> c
((.)
的第一个参数)必须匹配类型 Int -> Bool
(doSomeX
的类型)。所以
b ~ Int
c ~ Bool
因此,
(.) doSomeX :: (a -> Int) -> a -> Bool
现在,将其应用于 doSomeY
会导致类型错误。如上所述,
doSomeY :: Int -> (Int -> Int)
因此,在为 (.) doSomeX doSomeY
推断类型时,编译器必须将 a -> Int
((.) doSomeX
的第一个参数)与 Int -> (Int -> Int)
([=18 的类型=]). a
可以和Int
统一,但是后半部分不行。因此编译器 barfs.
(.)
定义为
(.) f g x = f (g x)
所以 (f . g) x y = (.) f g x y = f (g x) y
,而不是 f (g x y)
,如您所愿。
另一方面,(f . g) (x,y) = f (g (x,y))
,因为(x,y)
是一个值,一个二元组,所以你的第一个版本是可以的。
我在 Haskell 中学习了一些基本的函数组合,当我在玩的时候,我意识到了一些我无法真正解释的事情。当我使用以下代码块时,编译器似乎对此很满意并且工作正常:
doSomeX x = if x==7 then True else False
doSomeY (x,y) = x+y+1
doSomeXY = doSomeX.doSomeY
然而,当我将 doSomeY 拆分为 2 个参数而不是一对时,即:
doSomeX x = if x==7 then True else False
doSomeY x y = x+y+1
doSomeXY = doSomeX.doSomeY
我收到以下错误:
No instance for (Num a0) arising from a use of `doSomeY'
The type variable `a0' is ambiguous
Relevant bindings include
doSomeXY :: a0 -> Bool (bound at test.hs:21:1)
Note: there are several potential instances:
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Num Integer -- Defined in `GHC.Num'
instance Num Double -- Defined in `GHC.Float'
...plus three others
In the second argument of `(.)', namely `doSomeY'
In the expression: doSomeX . doSomeY
In an equation for `doSomeXY': doSomeXY = doSomeX . doSomeY
不过我真的不明白为什么。在这两种情况下,doSomeY
的 return 类型与函数 doSomeX
的 arg 类型相同,为什么 doSomeY
的输入类型会有所不同?我是否遗漏了一些基本知识?
谢谢
差异是由 doSomeY
在第一种情况下产生一个数字与在第二种情况下产生一个函数引起的,当应用于一个参数时。
给定
doSomeX x = if x==7 then True else False
doSomeY x y = x+y+1
我们可以推断类型(这些可能不是最通用的类型,但它们可以满足我们的目的):
doSomeX :: Int -> Bool
doSomeY :: Int -> Int -> Int
记住类型签名中的->
关联到右边,所以doSomeY
的类型等同于
doSomeY :: Int -> (Int -> Int)
考虑到这一点,考虑(.)
的类型:
(.) :: (b -> c) -> (a -> b) -> a -> c
如果你定义
doSomeXY = doSomeX.doSomeY
...相当于(.) doSomeX doSomeY
,意思是(.)
的第一个参数是doSomeX
,第二个参数是doSomeY
。因此,类型 b -> c
((.)
的第一个参数)必须匹配类型 Int -> Bool
(doSomeX
的类型)。所以
b ~ Int
c ~ Bool
因此,
(.) doSomeX :: (a -> Int) -> a -> Bool
现在,将其应用于 doSomeY
会导致类型错误。如上所述,
doSomeY :: Int -> (Int -> Int)
因此,在为 (.) doSomeX doSomeY
推断类型时,编译器必须将 a -> Int
((.) doSomeX
的第一个参数)与 Int -> (Int -> Int)
([=18 的类型=]). a
可以和Int
统一,但是后半部分不行。因此编译器 barfs.
(.)
定义为
(.) f g x = f (g x)
所以 (f . g) x y = (.) f g x y = f (g x) y
,而不是 f (g x y)
,如您所愿。
另一方面,(f . g) (x,y) = f (g (x,y))
,因为(x,y)
是一个值,一个二元组,所以你的第一个版本是可以的。