I have a tree intended to contain a tuple at each node:
-- 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
Is there any way to restrict a so that MMTree can only be initialized with specific types; namely, (Int, Int) or (Float, Float) rather than any old type?
Yes. You can use generalized algebraic datatypes (GADTs, http://en.wikibooks.org/wiki/Haskell/GADT), which exactly do what you need (type of result can depend on used constructor). As a simple solution, you can make a constructor for each possible node type:
{-# 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)
However, this solution isn't very good (because you'll need to add more constructors if later you'll want to use the same tree type for other elements). So, DataKinds and TypeFamilies to the rescue:
{-# 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
This is the solution if you really would like to restrict used types in data declaration. However, I would like to suggest an easier solution: just leave your tree definition as is, and if you would like to process a tree where nodes are expected to contain tuples of numeric values, just write functions with type signature like that:
someFun :: (Num a) => MMTree (a, a) -> r
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With