我有一棵树,旨在在每个节点包含一个元组:
-- Should be initialized with `a' as a tuple of (Int, Int) or (Float, Float)
data MMTree a = Empty | Node a (MMTree a) (MMTree a) deriving Show
有没有办法限制a
,以便MMTree
只能 使用特定类型进行初始化;即,(Int, Int)
或(Float, Float)
而不是任何旧类型?
答案 0 :(得分:9)
是。您可以使用广义代数数据类型(GADT,http://en.wikibooks.org/wiki/Haskell/GADT),它们可以完全满足您的需要(结果类型可以取决于使用的构造函数)。作为一个简单的解决方案,您可以为每种可能的节点类型创建一个构造函数:
{-# LANGUAGE GADTs #-}
data MMTree a where
Empty :: MMTree a
NodeI :: (Int, Int) -> MMTree (Int, Int) -> MMTree (Int, Int) -> MMTree (Int, Int)
NodeF :: (Float, Float) -> MMTree (Float, Float) -> MMTree (Float, Float) -> MMTree (Float, Float)
但是,这个解决方案并不是很好(因为如果以后你想要为其他元素使用相同的树类型,你需要添加更多的构造函数)。所以,DataKinds
和TypeFamilies
来救援:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
data TreeType
= TInt
| TFloat
type family Elem (t :: TreeType) where
Elem TInt = (Int, Int)
Elem TFloat = (Float, Float)
data MMTree (t :: TreeType) where
Empty :: MMTree a
Node :: Elem a -> MMTree a -> MMTree a -> MMTree a
test1 :: MMTree TInt
test1 = Node (1, 1) Empty Empty
test2 :: MMTree TFloat
test2 = Node (2.0, 3.0) Empty Empty
如果您真的想在data
声明中限制使用的类型,那么这就是解决方案。但是,我想建议一个更简单的解决方案:只保留树的定义,如果你想处理一个树,其中节点应该包含数值的元组,只需编写具有类型签名的函数:
someFun :: (Num a) => MMTree (a, a) -> r
答案 1 :(得分:4)
如果你改为写
data MMTree a = Empty | Node (a, a) (MMTree a) (MMTree a) deriving Show
您可以保证节点中将包含相同类型的元组。这不是你要求的,但可能就是你真正需要的。当然它简化了问题:现在你只需要将a
限制为Int或Float,而不是它们的元组,如果这真的是你需要的那样。