Это возможно с несколькими расширениями типа (как в сторону, пожалуйста, убедитесь, что ваш пример компиляции кода при отправке вопросов. Мне пришлось внести немало поправок).
{-# 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
один.
Извините за проблемы с компиляцией, я отправлял с моего телефона. – rampion
Хорошо, я смог получить от этого [версию, которая не требует '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
arg, но для этого требуется 'OverlappingInstances'. – rampion