I'm trying to encode a list of items which have types restricted to be instances of some type class:
{-# LANGUAGE RankNTypes, TypeSynonymInstances, LiberalTypeSynonyms #-}
module Test where
class Someable a where
some :: a -> String
data Some = Some String
type SomeGroup = forall a. Someable a => [a]
instance Someable Some where
some (Some v) = v
instance Someable SomeGroup where
some (x:xs) = (some x) ++ ", " ++ (some xs)
main = do
putStrLn $ show.some [Some "A", [Some "B", Some "C"]]
But compilation fails with error:
Test.hs:14:10:
Illegal polymorphic or qualified type: SomeGroup
In the instance declaration for `Someable SomeGroup'
It seems I even failed to define instance for type synonymous...
I'm aware of heterogenous collections wiki article, but want to know why exactly my approach doesn't work — it seems natural to me to define type by restricting collection only to contain items with types which is instance of some type class.
You can also cook something up using GADTs. That may be slightly shorter in some cases, and it makes explicit which type dictionaries are available after pattern matching.
Here's a slight variant of your example:
A couple of minor notes:
putStrLn . show
is the same asprint
.Int
because integer literals are treated specially, that is42
is translated tofromInteger 42
of typeNum a => a
. A bit clunky when constructing the list directly, but most of the time it'll work more smoothly.Cons
-ing elements onto a list, but the syntax will never look as nice as Haskell's built-in syntax!And a major point is that you lose the use of all the standard list functions. I usually use this kind of solution when my list processing needs are extremely limited; on the other hand, a fold is written quickly enough... Your mileage will vary though, I don't know what you actually want to use this list for.
If I understand things correctly, there needs to be a data type wrapping the existential in order for there to be a place to store the type class dictionary along with each element.
Adding some wrappers makes it work:
Of course, this is a bit ugly. I'm not aware of any better way, unfortunately.