2012-02-22 5 views
15

Мы можем использовать пару последовательности, чтобы создать гетерогенные списки в Haskell: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,()))) 

Есть ли способ, что мы можем сделать фильтрацию типа уровня в этих списках? То есть, определить некоторые полиморфные функции hfilter, что для различных типов a, b и 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 *:() -> () 

ответ

16

Это возможно с несколькими расширениями типа (как в сторону, пожалуйста, убедитесь, что ваш пример компиляции кода при отправке вопросов. Мне пришлось внести немало поправок).

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

Теперь мы можем фильтровать элементы по типу, явно определяя тип нужного нам списка.

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

Он работает путем определения нескольких параметров типа класса TypeFilter, который принимает тип гетерогенного списка и тип, который мы хотим отфильтровать. Затем мы определяем экземпляры для пустого списка/блока () и для списка, где спички типа (TypeFilter (t :* rest) t) и, наконец, для списка, где тип головы отличается от типа, который мы хотим получить (TypeFilter (a :* rest) t).

Примечания что в последнем случае имеется в настоящее время нет способа, чтобы показать, что a и t должны быть разных типов, но, когда они одни и те же OverlappingInstances подсчитывает экземпляр TypeFilter (t :* rest) t как более конкретный и выбирает его по TypeFilter (a :* rest) t один.

+0

Извините за проблемы с компиляцией, я отправлял с моего телефона. – rampion

+1

Хорошо, я смог получить от этого [версию, которая не требует 'OverlappingInstances', передав аргумент фильтра] (https://gist.github.com/1885439)' hfilter :: a -> h - > h'' и использует гетерогенные списки для вывода. Поэтому 'hfilter (undefined :: Int) hlist ::()' is '()', 'hfilter (undefined :: Int) hlist :: Int: *()' is '1: *()' и 'hfilter (undefined :: Int) hlist :: Int: * Int: *() 'is' 1: * 2: *() '. – rampion

+0

arg, но для этого требуется 'OverlappingInstances'. – rampion

2

Хотя существуют методы, чтобы делать то, что вы просите, существует очень высокая вероятность того, что вы не играете с силой Хаскелла здесь. Не могли бы вы уточнить o Ваши потребности? Обычно вы можете перечислить все варианты, которые вам понадобятся в алгебраическом типе данных. Затем ваш список будет однородным, позволяя вам сопоставлять шаблоны с элементами, которые будут работать на нем.

+0

Используя ADT, вы, вероятно, получите решение, подобное духу ['catMaybes'] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Maybe. html # v: catMaybes), который фильтрует список конкретным конструктором. –

+0

Я играю с написанием DSL на стеке (несколько напоминающий фактор) в haskell - гетерогенным списком в этом случае является стек. – rampion

+1

ах, я думаю, что я вижу, может быть, что-то в духе полиморфного стека на основе SNOC-пары Сами [описано здесь] (https://github.com/leonidas/codeblog/blob/master/2012/2012-02-17- concatenative-haskell.md)? –

Смежные вопросы