Вопрос по type-families, typeclass, haskell – Типовые многопоточные гетерогенные списки и дефолты (?) С семействами типов?

3

Я работаю над библиотекой, в которой я хочу определить рекурсивный класс, который я упростил здесь:

{-# LANGUAGE MultiParamTypeClasses 
           , FlexibleInstances #-}

data Snoc st b c = Snoc (st b) (c -> b) 
data Top a = Top

class StackTo a st c  where
     runStack :: st c -> (c -> a)
instance StackTo a Top a where
     runStack _ = id
instance (StackTo a st b) => StackTo a (Snoc st b) c where
     runStack (Snoc st' cb) = runStack st' . cb

Это позволяет мне сделать, например,

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head :: [(a,x)] -> a
*Main Data.Label> f [('a',undefined)] 
'a'

Но это, кажется, требует осторожного использования аннотаций типов, в противном случае ...

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head

<interactive>:1:1:
    No instance for (StackTo a0 Top b0)
      arising from a use of `runStack'
    Possible fix: add an instance declaration for (StackTo a0 Top b0)
    In the expression: runStack
    In the expression: runStack $ Snoc (Snoc Top fst) head
    In an equation for `it': it = runStack $ Snoc (Snoc Top fst) head

Я думаю, что это те же вопросы, которые рассматриваются вэтот вопрос, но у меня возникли проблемы с адаптацией этого решения здесь. Могу ли я использовать семейства типов или какой-либо другой метод, чтобы найти более удобное для пользователя решение для моего рекурсивного стека продолжения?

Вы можете рассмотреть возможность просто иметьclass Stack st со связанными типами, такими какtype Domain st a а такжеtype Codomain st a или что-то подобное, но я недостаточно внимательно прочитал, чтобы понять, сработает ли это для вас. Daniel Wagner

Ваш Ответ

2   ответа
7

обобщить заголовок экземпляра и специализироваться на контексте экземпляра.

instance a ~ b => StackTo a Top b where
    runStack _ = id

При выборе экземпляра для использования GHC проверяет только доступные заголовки экземпляров, а не контексты, и выбирает тот (если есть), который соответствует тому, что в настоящее время известно о типе. Он не будет специализировать тип перед тем, как сделать этот выбор, даже если специализация позволит сопоставить одну или несколько доступных головок экземпляров. Таким образом, разница между приведенным здесь примером и примером, приведенным выше, заключается в том, что этот случай является более общим: этот применяется всякий раз, когда средний типTopтогда как ваш применяется только тогда, когда средний типTop and мы знаем достаточно о двух других типах, чтобы знать, что они равны.

Ваш будет перекрываться с меньшим количеством других потенциальных примеров, но это будет стимулировать двигатель вывода более сильно.

Большое спасибо! Можете ли вы уточнить разницу междуinstance StackTo a Top a а такжеinstance a ~ b => StackTo a Top b? Бывший болееconstraint в то время как последний являетсяassertion равенства? (документы, кажется, не предполагают, так что я, вероятно, выключен) jberryman
@jberryman Я добавил к ответу немного больше объяснений.
6

Kleene star ГАДТ не справится с этой работой?

data Star r a b where
  Nil   :: Star r a a
  Cons  :: r a b -> Star r b c -> Star r a c

compose :: Star (->) a b -> a -> b
compose Nil          = id
compose (Cons f fs)  = compose fs . f

Но если вам нужен подход класса типов, я бы не вмешивался.

Я на самом деле делаю вариант написанной мною библиотеки, основанной на thrist, которая является именно этой конструкцией (хотя я и не знал о звезде Клини, спасибо!), Поэтому я намеренно выставляю промежуточные типы. jberryman

Похожие вопросы