Reputation: 10356
I would like to do this:
data Foo n = Foo $(tuple n Integer)
and thus allow
x :: Foo 3
x = Foo (1, 2, 3)
y :: Foo 5
y = Foo (5, 4, 3, 2, 1)
Currently I have simply
data Foo = Foo [Integer]
which can be made to work, but throws away a lot of nice compile-time checking. I'll be making a few different Foo data objects, and each will have a fixed number of foo
s throughout its lifespan, and it feels silly to not be able to check that in the type system.
Is this possible in haskell?
Upvotes: 1
Views: 264
Reputation: 54574
You can do something like
{-# LANGUAGE OverlappingInstances #-}
data Foo a = Foo Int a
single :: Int -> Foo ()
single n = Foo n ()
append :: Int -> Foo a -> Foo (Foo a)
append n foo = Foo n foo
size foo = subtract 1 $ fold (const (+1)) 0 foo
asList foo = reverse $ fold (flip (:)) [] foo
class FooOp a where
fold :: (b -> Int -> b) -> b -> a -> b
fooMap :: (Int -> Int) -> a -> a
fromList :: [Int] -> a
instance FooOp (Foo ()) where
fold op m (Foo n ()) = m `op` n
fooMap op (Foo n ()) = single (op n)
fromList [x] = single x
instance FooOp n => FooOp (Foo n) where
fold op m (Foo n foo) = fold op (m `op` n) foo
fooMap op (Foo n foo) = Foo (op n) $ fooMap op foo
fromList (x:xs) = Foo x $ fromList xs
Note that fromList is an unsafe operation (but there is no way around, as the list type holds no length information).
Upvotes: 1
Reputation: 89043
How about
data IntAnd b = Int :. b
infixr 5 :.
So that way you can do
data Foo n = Foo n
x :: Foo (IntAnd (IntAnd Int))
x = Foo (3 :. 4 :. 5)
y :: Foo (IntAnd (IntAnd (IntAnd (IntAnd Int))))
y = Foo (5 :. 4 :. 3 :. 2 :. 1)
Or, if you want something a bit closer to your original syntax, try TypeFamilies
:
data One
data Succ a
type Two = Succ One
type Three = Succ Two
type Four = Succ Three
type Five = Succ Four
class NTuple a where
type IntTuple a
instance NTuple One where
type IntTuple One = Int
instance (NTuple a) => NTuple (Succ a) where
type IntTuple (Succ a) = IntAnd (IntTuple a)
x :: Foo (IntTuple Three)
x = Foo (3 :. 4 :. 5)
y :: Foo (IntTuple Five)
y = Foo (5 :. 4 :. 3 :. 2 :. 1)
Or with even more magic (TypeOperators
, MultiParamTypeClasses
, and FlexibleInstance
, oh my!):
data a :. b = a :. b
infixr 5 :.
class NTuple a b where
type HomoTuple a b
instance NTuple One b where
type HomoTuple One b = b
instance (NTuple a b) => NTuple (Succ a) b where
type HomoTuple (Succ a) b = b :. HomoTuple a b
x :: Foo (HomoTuple Three Int)
x = Foo ( 3 :. 4 :. 5 )
y :: Foo (HomoTuple Five Int)
y = Foo ( 1 :. 2 :. 3 :. 4 :. 5 )
Upvotes: 2
Reputation: 12000
Instead of tuples, use fixed-length vectors. See f.e. http://www.haskell.org/pipermail/haskell/2005-May/015815.html.
Upvotes: 4
Reputation: 198014
Nope. Not possible.
The closest alternative to what you want looks something like this:
data FooZero = Zero
data FooSucc a = Succ Int a
type Foo0 = FooZero
type Foo1 = FooSucc Foo0
type Foo2 = FooSucc Foo1
...
Upvotes: 1