Haskell 哈斯克尔:限制;a「;对某些类型?

Haskell 哈斯克尔:限制;a「;对某些类型?,haskell,types,tuples,Haskell,Types,Tuples,我有一棵树,打算在每个节点上包含一个元组: -- 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),而不是任何旧的类型 data MMTree 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)
,而不是任何旧的类型

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

您可以保证节点中有相同类型的元组。这并不是你想要的,但可能是你真正需要的。当然,它简化了问题:现在您只需将
a
限制为Int或Float,而不是它们的元组,如果您真的需要的话。

是的。您可以使用广义代数数据类型(GADTs),它完全满足您的需要(结果的类型可以取决于所使用的构造函数)。作为一个简单的解决方案,您可以为每种可能的节点类型创建构造函数:

{-# 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)
但是,这个解决方案不是很好(因为如果以后要对其他元素使用相同的树类型,则需要添加更多构造函数)。因此,
datatypes
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
如果您确实希望限制
数据中使用的类型
声明,那么这就是解决方案。但是,我想建议一个更简单的解决方案:保持树定义不变,如果您想处理一个节点预期包含数值元组的树,只需编写具有如下类型签名的函数:

someFun :: (Num a) => MMTree (a, a) -> r

创建一个私有类型类并仅为这些类型实例化它。除此之外,您还可以将数据构造函数设置为私有,然后创建由模块导出的智能构造函数。例如,这些构造函数只允许在其中存储
numa=>(a,a)
值,即
empty::numa=>mmtreea
node::numa=>(a,a)->mmtreea->mmtreea->mmtreea->mmtreea->mmtreea
,非常类似于
Data.Map.empty
Data.Map.fromList