2012-02-22 13 views
15

Possiamo usare sequenze di coppia per creare liste eterogenee in Haskell:Haskell: filtrare una lista eterogenea per tipologia

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,()))) 

Esiste un modo che possiamo fare il filtraggio di tipo a livello su queste liste? Cioè, definire qualche funzione polimorfica hfilter tale che per i tipi distinti a, b e 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 *:() -> () 

risposta

16

E 'possibile con alcune estensioni di tipo (per inciso, si prega di verificare che il codice di esempio viene compilato durante la pubblicazione delle domande. Ho dovuto fare alcune correzioni).

{-# 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 

Ora siamo in grado di filtrare gli elementi per tipo definendo in modo esplicito il tipo di lista che vogliamo.

*Main> hfilter hlist :: [Int] 
[1,2] 
*Main> hfilter hlist :: [String] 
["hello"] 
*Main> hfilter hlist :: [Maybe Float] 
[Just 3.0] 
*Main> hfilter hlist :: [Maybe Int] 
[] 

Funziona definendo un multi-parametro di tipo classe TypeFilter, che prende il tipo di lista eterogenea e il tipo che vogliamo filtrare. Definiamo quindi le istanze per il vuoto lista/unità () e per una lista in cui il tipo di partite (TypeFilter (t :* rest) t) e, infine, per una lista in cui il tipo di testa è diverso da quello del tipo che vogliamo recuperare (TypeFilter (a :* rest) t).

Nota che in ultima istanza esiste attualmente alcun modo per indicare che a e t devono essere diversi tipi, ma quando sono gli stessi OverlappingInstances conteggi l'istanza TypeFilter (t :* rest) t come più specifica e sceglie sopra la TypeFilter (a :* rest) t uno.

+0

Mi dispiace per i problemi di compilazione, stavo postando dal mio telefono. – rampion

+1

Ok, sono riuscito a ottenere da [una versione che non richiede 'OverlappingInstances' passando un argomento filtro] (https://gist.github.com/1885439)' hfilter :: a -> h - > h'', che utilizza liste eterogenee per l'output. Quindi 'hfilter (undefined :: Int) hlist ::()' è '()', 'hfilter (undefined :: Int) hlist :: Int: *()' è '1: *()' e 'hfilter (undefined :: Int) hlist :: Int: * Int: *() 'è' 1: * 2: *() '. – rampion

+0

arg, ma per utilizzare effettivamente 'OverlappingInstances'. – rampion

2

Sebbene esistano metodi per fare quello che chiedi, c'è una probabilità molto alta che non stai giocando la forza di Haskell qui. Potresti elaborare n tue esigenze? Di solito è possibile enumerare tutte le varianti necessarie in un tipo di dati algebrico. La tua lista sarà quindi omogenea, permettendoti di abbinare gli elementi per operare su di essa.

+0

Usando un ADT, probabilmente si finirebbe con una soluzione simile nello spirito a ['catMaybes'] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Maybe. html # v: catMaybes), che filtra l'elenco da un particolare costruttore. –

+0

Sto giocando con la scrittura di un DSL basato su stack (un po 'simile al fattore) in haskell - l'elenco eterogeneo in questo caso è lo stack. – rampion

+1

ah, penso di vedere, forse qualcosa nello spirito dello stack polimorfico basato su coppie SNOC di Sami [descritto qui] (https://github.com/leonidas/codeblog/blob/master/2012/2012-02-17- concatenative-haskell.md)? –

Problemi correlati