Reputation: 89073
We can use pair sequences to create heterogenous lists in Haskell:
type a *: b = (a, b)
a *: b = (a, b)
infixr 5 *:
hlist :: Int *: String *: Maybe Float *: ()
hlist = 1 *: "hello" *: Just 3 *: () -- (1, ("hello", (Just 3, ())))
Is there a way we can do type-level filtering on these lists? That is, define some polymorphic function hfilter
such that for distinct types a
, b
, and c
:
hfilter :: a *: b *: c *: a *: b *: a *: () -> a *: a *: a *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () -> b *: b *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () -> c *: ()
hfilter :: a *: b *: c *: a *: b *: a *: () -> ()
Upvotes: 16
Views: 2031
Reputation: 767
Although there exist methods to do what you ask, there is a very high probability that you are not playing Haskell strength here. Could you elaborate on your needs ? Usually you can enumerate all variants that you will need in an algebraic data type. Your list will then be homogeneous, allowing you to pattern-match on elements to operate on it.
Upvotes: 2
Reputation: 24802
It's possible with a few type extensions (as an aside, please check that your example code compiles when posting questions. I had to make quite a few corrections).
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances #-}
type a :* b = (a, b)
a *: b = (a, b)
infixr 5 *:
infixr 5 :*
hlist :: Int :* String :* Int :* Maybe Float :* ()
hlist = 1 *: "hello" *: 2 *: Just 3 *: ()
class TypeFilter lst t where
hfilter :: lst -> [t]
instance TypeFilter () t where
hfilter _ = []
instance TypeFilter rest t => TypeFilter (t :* rest) t where
hfilter (a, rest) = a : hfilter rest
instance TypeFilter rest t => TypeFilter (a :* rest) t where
hfilter (_, rest) = hfilter rest
Now we can filter items by type by explicitly defining the type of the list we want.
*Main> hfilter hlist :: [Int]
[1,2]
*Main> hfilter hlist :: [String]
["hello"]
*Main> hfilter hlist :: [Maybe Float]
[Just 3.0]
*Main> hfilter hlist :: [Maybe Int]
[]
It works by defining a multi-parameter type-class TypeFilter
, which takes the type of the heterogenous list and the type we want to filter by. We then define instances for the empty list/unit ()
and for a list where the type matches (TypeFilter (t :* rest) t
) and finally for a list where the type of the head is different than the type we want to retrieve (TypeFilter (a :* rest) t)
.
Note that in the last instance there is currently no way to signify that a
and t
must be different types, but when they are the same OverlappingInstances
counts the instance TypeFilter (t :* rest) t
as more specific and chooses it over the TypeFilter (a :* rest) t
one.
Upvotes: 17