Ограниченный неоднородный список

Я искал Hackage и не смог найти ничего похожего на следующее, но это кажется довольно простым и полезным. Есть ли библиотека, которая содержит тип данных?

data HList c where
  (:-) :: c a => a -> HList c
  Nil :: HList c

Все HLists, которые я нашел, могли иметь любой тип и не были ограничены.

Если нет, я загружу свой собственный.


person Clinton    schedule 12.09.2017    source источник
comment
Я тоже не видел. Я работал над чем-то подобным некоторое время назад, но у меня никогда не было этого в такой форме, чтобы я чувствовал, что его нужно загрузить. Кроме того, вы можете столкнуться с некоторыми странными углами типа система изучает это.   -  person David Young    schedule 12.09.2017


Ответы (3)


Я не уверен, что этот тип данных полезен...

  • Если вы действительно хотите, чтобы a был квалифицирован экзистенциально, я думаю, вам следует использовать обычные списки. Более интересным типом данных здесь был бы Exists, хотя я уверен, что его варианты уже есть во всем package Hackage:

    data Exists c where
      Exists :: c a => a -> Exists c
    

    Тогда ваш HList c изоморфен [Exists c], и вы по-прежнему можете использовать все обычные функции, основанные на списках.

  • С другой стороны, если вы не обязательно хотите, чтобы a в (:-) :: c a => a -> HList c было экзистенциально квалифицированным (наличие такого рода противоречит точке HList), вы должны вместо этого определить следующее:

    data HList (as :: [*]) where
      (:-) :: a -> HList as -> HList (a ': as)
      Nil :: HList '[]
    

    Затем, если вы хотите потребовать, чтобы все записи HList удовлетворяли c, вы можете создать класс типов для свидетеля внедрения из HList as в [Exists c], разрешение экземпляра которого работает только в том случае, если все типы в HList удовлетворяют ограничению:

    class ForallC as c where
      asList :: HList as -> [Exists c]
    
    instance ForallC '[] c where
      asList Nil = []
    
    instance (c a, ForallC as c) => ForallC (a ': as) c where
      asList (x :- xs) = Exists x : asList xs
    
person Alec    schedule 12.09.2017
comment
Связанный с этим вопрос: существует ли где-нибудь? Я загрузил полиданные, которые в основном существуют, но я мог бы также использовать чей-то другой пакет, если он уже существует, чтобы сохранить дублирование hackage.haskell.org/package/polydata-0.1.0.0/docs/ - person Clinton; 12.09.2017
comment
Он старый, но при поиске обнаружился пакет с именем exists< /а>... - person Alec; 12.09.2017
comment
Ой, подождите, Exists и Poly разные. - person Clinton; 12.09.2017
comment
@Alec Чтобы построить Exists c, вам нужно найти один пример a, такой что c a. Чтобы построить Poly c, вы должны показать, что a существует для всех a таких, что c a. Я думаю, что терминология такова: Exists экзистенциально квантифицировано, а Poly универсально квантифицировано. - person Cirdec; 12.09.2017
comment
@Cirdec спасибо! Пикша сбивала меня с толку. Глядя на исходный код, я понимаю, что вы имеете в виду. - person Alec; 12.09.2017
comment
Почему бы не использовать семейство типов для создания ограничения All c as (All c '[] = (); All c (a ': as) = (c a, All c as)) вместо преобразования из Hlist as в [Exist c], что приведет к потере информации о as? - person gallais; 12.09.2017
comment
@gallais Просто дело вкуса. Проблема с этим заключается в том, как использовать «Все» впоследствии. - person Alec; 12.09.2017
comment
@gallais Обратите внимание, что ForallC as c также выполняется точно тогда, когда также выполняется All c as - это также позволяет вам делать что-то с этой информацией. - person Alec; 12.09.2017
comment
Я не понимаю, почему вы не можете что-то сделать с All c as. Вы будете получать доступ к ограничениям точно так же, как вы получаете доступ к значениям в HList: используя индекс. - person gallais; 12.09.2017
comment
@gallais Я не уверен, что понимаю, что ты имеешь в виду, но мне бы хотелось, потому что это звучит интересно. У вас есть пример того, как это работает для All (или что-то подобное)? - person Alec; 12.09.2017
comment
Как насчет чего-то подобного? gist.github.com/gallais/48b6dba11b1fd57d4a7386cfe502ff94 - person gallais; 12.09.2017
comment
@gallais Спасибо! Теперь я понимаю, что вы имеете в виду. :) - person Alec; 12.09.2017
comment
Я бы сказал, что семейство типов All значительно легче фактически использовать ограничения на практике, чем использование только класса типов. Тогда нет причин сначала вычислять какую-то форму на уровне терминов, где словари присоединяются к значениям. - person kosmikus; 12.09.2017
comment
Быстрое обновление: пост Reddit предлагает существует пакет для вашего типа Exists. - person Daniel Wagner; 15.09.2017
comment
@DanielWagner Этот пакет уже был связан во втором комментарии. :) - person Alec; 15.09.2017
comment
@Алек Йиш, я должен быть слепым! - person Daniel Wagner; 15.09.2017

Пакет generics-sop предлагает это из коробки.

Неоднородный список может быть определен в generics-sop с помощью

data NP :: (k -> *) -> [k] -> * where
  Nil  :: NP f '[]
  (:*) :: f x -> NP f xs -> NP f (x ': xs)

и инстанцировать его в конструкторе типа удостоверения I (из generics-sop) или Identity (из Data.Functor.Identity).

Затем библиотека предлагает ограничение All, например.

All Show xs => NP I xs

— это тип разнородного списка, в котором все содержащиеся типы относятся к классу Show. Концептуально All — это семейство типов, которое вычисляет ограничение для каждого элемента в списке на уровне типов:

type family All (f :: k -> Constraint) (xs :: [k]) :: Constraint where
  All c '[]       = ()
  All c (x ': xs) = (c x, All c xs)

(Только в фактическом определении All дополнительно обернут в класс типа, чтобы его можно было частично применить.)

Кроме того, библиотека предлагает всевозможные функции, которые проходят и преобразовывают NP с учетом общего ограничения.

person kosmikus    schedule 12.09.2017

То, что вы действительно хотите, это

data HKList :: (k -> *) -> [k] -> * where
  Nil  :: HKList f '[]
  (:*) :: f x -> HKList f xs -> HKList f (x ': xs)

Который можно использовать либо как обычный разнородный список

type HList = HKList Identity

Или с дополнительной информацией о некотором постоянном типе e, прикрепленном к каждому значению (или другим интересным функторам)

HKList ((,) e)

Или с дополнительной информацией, записанной в словаре

data Has c a where
    Has :: c a => a -> Has c a

type ConstrainedList c = HKList (Has c)

Или сохраняйте списки только захваченных ограничений

data Dict1 :: (k -> Constraint) -> k -> * where
  Dict1 :: c k => Dict1 c k

Который вы можете использовать, чтобы определить идею всего списка типов, удовлетворяющих ограничению

class All c xs where
  dicts :: HKList (Dict1 c) xs

instance All c '[] where
  dicts = Nil

instance (All c xs, c x) => All c (x ': xs) where
  dicts = Dict1 :* dicts

Или что-нибудь еще, что вы можете сделать с добрым k -> *

Вы можете свободно переключаться между работой с All c xs => HList xs и HKList (Has c) xs

zipHKList :: (forall k. f k -> g k -> h k) -> HKList f xs -> HKList g xs -> HKList h xs
zipHKList _ Nil Nil = Nil
zipHKList f (x :* xs) (y :* ys) = f x y :* zipHKList f xs ys

allToHas :: All c xs => HKList Identity xs -> HKList (Has c) xs
allToHas xs = zipHKList f dicts xs
  where
    f :: Dict1 c k -> Identity k -> Has c k
    f Dict1 (Identity x) = Has x

hasToAll :: HKList (Has c) xs -> Dict (All c xs)
hasToAll Nil = Dict
hasToAll (Has x :* xs) =
  case hasToAll xs of
    Dict -> Dict

полный код

Я писал это несколько раз раньше для различных проектов, но я не знал, что это где-то есть в библиотеке, пока Kosmikus не указал, что это в generics-sop.

person Cirdec    schedule 12.09.2017
comment
Как обсуждалось в других ответах, я думаю, вам было бы несколько лучше превратить All в семейство типов и не вычислять явно dicts, а просто использовать их напрямую. Для этой цели generics-sop предлагает версии функций с ограничениями, такие как ваша zipHKList, которые могут использовать и распределять ограничение All по элементам. - person kosmikus; 12.09.2017