Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: filtering a heterogenous list by type

Tags:

haskell

filter

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 *: () ->  ()
like image 862
rampion Avatar asked Feb 22 '12 13:02

rampion


2 Answers

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.

like image 55
shang Avatar answered Oct 08 '22 00:10

shang


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.

like image 31
Paul R Avatar answered Oct 07 '22 22:10

Paul R