2014-10-16 150 views
2

我打算包含在每个节点的元组树:Haskell:将“a”限制为某些类型?

-- 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)而不是任何旧类型?

+2

创建一个私有类型类并仅为这些类型实例化它。 – rightfold 2014-10-16 17:42:11

回答

8

是的。您可以使用广义代数数据类型(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) 

但是,这个方案是不是很好(因为你需要添加更多的构造函数,如果以后你需要使用其他元素使用相同的树型)。所以,DataKindsTypeFamilies救援:

{-# 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 
4

如果改为写

data MMTree a = Empty | Node (a, a) (MMTree a) (MMTree a) deriving Show 

可以保证节点都将在他们同一类型的元。这不完全是你要求的,但可能是你真正需要的。当然,它简化了这个问题:现在你只需要将a限制为Int或Float,而不是它们的元组,如果这真的是你需要的。

+2

除此之外,您可以将数据构造函数设为私有,然后创建由模块导出的智能构造函数。这些构造函数只允许在其中存储'Num a =>(a,a)'值,即'empty :: Num a => MMTree a'和'node :: Num a =>(a, a) - > MMTree a - > MMTree a - > MMTree a',很像'Data.Map.empty'和'Data.Map.fromList'。 – bheklilr 2014-10-16 18:10:30

相关问题