我正在浏览Haskell的this简介,其中包含以下示例:
data Tree a = Leaf a | Branch (Tree a) (Tree a)
它说的基本上也是定义类型:
Branch :: Tree a -> Tree a -> Tree a
Leaf :: a -> Tree a
我理解Leaf的定义是说它将a
映射到Tree
a
。但我不理解分支定义,我将其视为" Tree
a
Tree
a
Tree
分支到a
} .icon
这听起来不正确。这是因为默认情况下所有Haskell函数都被调用了吗?
答案 0 :(得分:4)
没有。如果它有如下签名:
Branch :: (Tree a, Tree a) -> Tree a
然后你应该给它一个元组,并调用构造函数,如:
Branch (tree1,tree2)
Branch
有签名:
Branch :: Tree a -> Tree a -> Tree a
或使用明确的括号:
Branch :: Tree a -> (Tree a -> Tree a)
因此,当您向Branch
提供第一个参数(例如Branch tree1
)时,签名为:
(Branch t1) :: Tree a -> Tree a
当您最终输入第二个参数t2
时,其类型将折叠为:
((Branch t1) t2) :: Tree a
更方便的例子是例如(+)
,它不是构造函数,而是函数。 (+)
我们的加(+)
有签名
(+) :: Int -> Int -> Int
(Haskells plus更通用,但现在不要考虑这个。)
如果你现在写(5+)
,你已经将你的功能专门用于一个功能:
(5+) :: Int -> Int
换句话说,您构建了一个 new 函数,该函数会将5
添加到给定数字。
由于(a,b) -> c
和a -> b -> c
的签名有点等同,因此Haskell提供了curry
和uncurry
函数:
curry :: ((a, b) -> c) -> a -> b -> c
uncurry :: (a -> b -> c) -> ((a, b) -> c)
如果你决定需要一个额外的Branch
构造函数来构造一个元组,你可以构造这样的构造函数:
branchTuple :: (Tree a, Tree a) -> Tree a
branchTuple = uncurry Branch
答案 1 :(得分:2)
Branch (Tree a) (Tree a)
是一个带有两个类型参数的构造函数,两个参数都是Tree a
,构造函数的结果是Tree a
,因此
Tree a -> Tree a -> Tree a
我理解Leaf的定义是说它将a映射到a的树。但我不明白分支定义,不应该是这样的:
Branch :: (Tree a, Tree a) -> Tree a
不,因为如果您使用(Tree a, Tree a)
,那么您将定义一个单元类型参数,它是一个元组。
答案 2 :(得分:2)
我把它读作“分支映射树的树到树的树”
您可以将定义视为一个函数,它接受Tree a
并返回另一个函数,该函数也需要Tree a
并生成Tree a
(这是最终结果,有两个分支的树)。您Branch
的签名等同于
Branch :: Tree a -> (Tree a -> Tree a)
每个函数只有一个输入参数的概念称为currying。