Learn You a Haskell创建Tree
代数数据类型:
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
我的理解是a
可以是任何类型。
所以我尝试创建一个treeInsert
函数,根据Tree
值将Side
放在左侧或右侧:
data Side = L | R
singletonTree :: a -> Tree a
singletonTree x = Node x EmptyTree EmptyTree
treeInsert :: a -> Tree a -> Tree a
treeInsert x EmptyTree = singletonTree x
treeInsert x@(L, _) (Node a left right) = Node a (treeInsert x left) right -- ERROR
treeInsert x@(R, _) (Node a left right) = Node a left (treeInsert x right)
但是我遇到了编译时错误:
Couldn't match expected type `a' with actual type `(Side, t0)'
`a' is a rigid type variable bound by
the type signature for treeInsert :: a -> Tree a -> Tree a
at File.hs:10:15
In the pattern: (L, _)
In an equation for `treeInsert':
treeInsert x@(L, _) (Node a left right)
= Node a (treeInsert x left) right
Failed, modules loaded: none.
也许a
仍然是任何类型,但我的模式匹配无效?
答案 0 :(得分:7)
因为a
通常可以是Tree a
中的任何类型,并不意味着它可以是传递给treeInsert
的树中的任何类型。您需要将其细化为实际允许(L, _)
和(R, _)
模式匹配的类型。
实际上,您可以删除treeInsert
上的类型注释并进行编译,之后您可以向GHCi询问:t
的正确类型(然后根据需要重新添加该注释) ):
treeInsert :: (Side, t) -> Tree (Side, t) -> Tree (Side, t)
答案 1 :(得分:4)
treeInsert :: a -> Tree a -> Tree a
因此treeInsert
将获得任何类型的值,以及包含相同类型的树,并生成相同类型的树。
您的公式尝试将x
与模式(L, _)
和(R, _)
匹配。可是等等;这种类型说我可以用我喜欢的任何类型来称呼它。我应该能够像treeInsert ["this", "is", "a", "list", "of", "String"] EmptyTree
那样称呼它。如何将[String]
类型的内容与(L, _)
之类的模式进行匹配;仅适用于(Side, t0)
形式的类型(适用于任何类型t0
)。
调用函数时,a
等类型变量非常灵活;您可以使用您喜欢的任何类型的任何值,该功能将起作用。
但你不是致电 treeInsert
,而是实施它。对于实施者,类似a
的变量非常限制性而不是灵活的。这是 1 的必然折衷。 来电者可自由选择他们想要的任何东西; 实现者必须提供适用于调用者可能选择的任何代码的代码(即使是调用者程序在完成函数后很长时间内构成的类型)。
因此,您无法针对(L, _)
等模式对其进行测试。或任何其他有意义的模式。你也不能将它传递给任何“具体”功能,只能传递给接受任何可能类型的其他功能。事实上,在treeInsert
类型a -> Tree a -> Tree a
中,没有办法在您插入的所有值中使用任何属性来决定它们将去哪里;对于您可能想要用于决定是将其放在左侧树还是右侧树中的任何属性,不能保证该属性将被有意义地定义。
您需要根据您可以检查的内容(例如也传入的树的结构)进行插入,或者使用为调用者提供较少灵活性的类型,并实际为您提供有关您正在插入的值,例如treeInsert :: (Side, t) -> Tree (Side, t) -> Tree (Side, t)
,正如ØrjanJohansen建议的那样。
1 而且非常严格的限制实际上是Haskell整体上获得了很大的力量,因为很多真正有用且通用的代码依赖来实现什么功能某些类型不能做。