use*_*425 45 haskell functional-programming function-composition
我试图了解结果
(*) . (+)
Run Code Online (Sandbox Code Playgroud)
在哈斯克尔.我知道合成算子只是数学函数的标准组成 - 所以
(f . g) = f (g x)
Run Code Online (Sandbox Code Playgroud)
但:
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)
我很难理解这种类型的签名.我希望能够做到这样的事情:
((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))
Run Code Online (Sandbox Code Playgroud)
的意义是什么 (*) .(+)的签名?我尝试用它来玩它(只是匹配它的签名):
((*) . (+)) 1 (\x -> x + 1) 1
Run Code Online (Sandbox Code Playgroud)
但那无法编译.我正在尝试在编写这些步骤时完成逻辑步骤,但我还没有完全理解它是如何得到这个结果的(以及结果是什么).
Aad*_*hah 67
我理解你的感受.我发现功能组成起初也很难掌握.什么帮助我解决这个问题是类型签名.考虑:
(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)
现在当你写(*) . (+)
它时实际上是相同的(.) (*) (+)
(即(*)
第一个参数(.)
和(+)
第二个参数(.)
):
(.) :: (b -> c) -> (a -> b) -> a -> c
|______| |______|
| |
(*) (+)
Run Code Online (Sandbox Code Playgroud)
因此,(*)
(即Num x => x -> x -> x
)的类型签名与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 => x x -> x
(.) (*) :: Num x => (a -> x) -> a -> x -> x
Run Code Online (Sandbox Code Playgroud)
因此,(+)
(即Num y => y -> y -> y
)的类型签名与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 => y y -> y y -> y
(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y
Run Code Online (Sandbox Code Playgroud)
我希望澄清其中Num (y -> y)
并Num y
从何而来.你留下了一个非常奇怪的类型的功能(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 ( g z)
((*) . (+)) = \z -> (*) ((+) z)
Run Code Online (Sandbox Code Playgroud)
所以你有一个功能\z -> (*) ((+) z)
.因此z
必须明确是Num
因为(+)
适用于它的一个实例.这样的类型\z -> (*) ((+) z)
是Num t => t -> ...
其中...
的类型(*) ((+) z)
,我们将在稍后发现.
因此它((+) z)
是类型的,Num t => t -> t
因为它需要一个更多的数字.但是,在将其应用于其他数字之前,将应用于该数字(*)
.
因此(*)
期望((+) z)
成为一个实例Num
,这就是为什么t -> t
期望成为的实例Num
.因此将...
其替换为(t -> t) -> t -> t
并且Num (t -> t)
添加约束,从而产生类型(Num (t -> t), Num t) => t -> (t -> t) -> t -> t
.
你真正想要结合(*)
并(+)
使用的方式(.:)
:
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)
Run Code Online (Sandbox Code Playgroud)
因此(*) .: (+)
是一样的\x y -> (*) ((+) x y)
.现在提出两个论点来(+)
确保这((+) x y)
确实是公正的Num t => t
而不是Num t => t -> t
.
因此((*) .: (+)) 2 3 5
,(*) ((+) 2 3) 5
哪个是(*) 5 5
哪个25
,我认为是你想要的.
注意,f .: g
也可以写为(f .) . g
,(.:)
也可以定义为(.:) = (.) . (.)
.你可以在这里读更多关于它的内容:
希望有所帮助.
(*)
并且(+)
都有类型签名Num a => a -> a -> a
现在,如果你编写它们,你会得到一些时髦的东西.
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)
那是因为(*)
并(+)
期待两个"论点".
(+)带一个参数可以获得一个函数.该.
运营商预计功能(a -> a
你看).
这是意思 (*) . (+)
x f y
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)
(*) . (+)
映射x f y
到((x +) * f) y
哪里f
是从函数a
到a
,这也是一个数字.原因是(*)
期望一个函数是使类型匹配而它需要两个参数,但该函数必须是一个数字,因为(*)
它只适用于数字.
真的,这个功能毫无意义.
首先是一些扩展:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}
Run Code Online (Sandbox Code Playgroud)
正如其他答案所示,你的功能是
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (x +) * g
Run Code Online (Sandbox Code Playgroud)
但是这个函数确实有非奇怪的语义.
有一个差异列表的概念.因此,存在差异整数的概念.我已经看到它们仅在依赖类型的设置中使用(例如,这里,但这不是唯一的情况).该定义的相关部分是
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
Run Code Online (Sandbox Code Playgroud)
这在Haskell中没有多大意义,但对于依赖类型可能很有用.
现在我们可以写了
test :: DiffInt
test = toEnum 3 * toEnum 4
Run Code Online (Sandbox Code Playgroud)
要么
test :: DiffInt
test = weird 3 (toEnum 4)
Run Code Online (Sandbox Code Playgroud)
在这两种情况下fromEnum test == 12
.
编辑
可以避免使用TypeSynonymInstances
扩展名:
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
weird :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
weird x g = (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
Run Code Online (Sandbox Code Playgroud)
和以前一样,我们可以写
test' :: DiffInt
test' = weird 3 (toEnum 4)
Run Code Online (Sandbox Code Playgroud)
但现在我们也可以写
-- difference ints over difference ints
type DiffDiffInt = DiffInt -> DiffInt
test'' :: DiffDiffInt
test'' = weird (toEnum 3) (toEnum (toEnum 4))
Run Code Online (Sandbox Code Playgroud)
和
main = print $ fromEnum $ fromEnum test'
Run Code Online (Sandbox Code Playgroud)
打印12
.
EDIT2添加了更好的链接.
归档时间: |
|
查看次数: |
1526 次 |
最近记录: |