Haskell: фильтрация разнородного списка по типу - PullRequest
15 голосов
/ 22 февраля 2012

Мы можем использовать парные последовательности для создания гетерогенных списков в 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 *: () ->  ()

Ответы [ 2 ]

16 голосов
/ 22 февраля 2012

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

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

2 голосов
/ 22 февраля 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...