Я думаю, что это самый дальний в расширении системы типа Haskell, который я даже был, и я столкнулся с ошибкой, которую мне не удалось выяснить. Извините заранее за длину, это самый короткий пример, который я мог бы создать, который все еще иллюстрирует проблему, с которой я сталкиваюсь. У меня есть рекурсивную GADT чей вид является рекламируемый список, что-то вроде следующего:Как обработать рекурсивный GADT с видом :: '[SomeDataKind]
GADT Определение
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
data DataKind = A | B | C
-- 'parts' should always contain at least 1 element which is enforced by the GADT.
-- Lets call the first piece of data 'val' and the second 'subdata'.
-- All data constructors have these 2 fields, although some may have
-- additional fields which I've omitted for simplicity.
data SomeData (parts :: [DataKind]) where
MkA :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('A ': subparts)
MkB :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('B ': subparts)
MkC :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('C ': subparts)
deriving instance Show (SomeData parts)
Проблема
То, что я пытаюсь сделать, это пройти данные и выполнить некоторые операции, например, распространить первый найденный вверх Just
Char
.
раздражающих недостающие функции - необходимы для следующего раздела
Теперь, потому что, видимо, нет поддержки синтаксиса записи для GADTs (https://ghc.haskell.org/trac/ghc/ticket/2595) вы должны написать их вручную, так вот они:
getVal :: SomeData parts -> Maybe Char
getVal (MkA val _) = val
getVal (MkB val _) = val
getVal (MkC val _) = val
-- The lack of record syntax for GADTs is annoying.
updateVal :: Maybe Char -> SomeData parts -> SomeData parts
updateVal val (MkA _val sub) = MkA val sub
updateVal val (MkB _val sub) = MkB val sub
updateVal val (MkC _val sub) = MkC val sub
-- really annoying...
getSubData :: SomeData (p ': rest) -> Maybe (SomeData rest)
getSubData (MkA _ sub) = sub
getSubData (MkB _ sub) = sub
getSubData (MkC _ sub) = sub
Test Data
Итак, что я хочу сделать. Пройдите вниз по структуре сверху, пока не найду значение Just
. Поэтому, учитывая следующие начальные значения:
a :: SomeData '[ 'A ]
a = MkA (Just 'A') Nothing
b :: SomeData '[ 'B ]
b = MkB (Just 'B') Nothing
c :: SomeData '[ 'C ]
c = MkC (Just 'C') Nothing
bc :: SomeData '[ 'B, 'C ]
bc = MkB Nothing (Just c)
abc :: SomeData '[ 'A, 'B, 'C ]
abc = MkA Nothing (Just bc)
Ожидаемый результат
Я хотел бы иметь что-то вроде этого:
> abc
MkA Nothing (Just (MkB Nothing (Just (MkC (Just 'C') Nothing))))
> propogate abc
MkA (Just 'C') (Just (MkB (Just 'C') (Just (MkC (Just 'C') Nothing))))
Предыдущие попытки
Я сделал несколько уколы на нем, сначала с регулярной функцией:
propogate sd =
case getVal sd of
Just _val ->
sd
Nothing ->
let
newSubData = fmap propogate (getSubData sd)
newVal = join . fmap getVal $ newSubData
in
updateVal newVal sd
Это дает ошибку:
Broken.hs:(70,1)-(81,35): error: …
• Occurs check: cannot construct the infinite type: rest ~ p : rest
Expected type: SomeData rest -> SomeData (p : rest)
Actual type: SomeData (p : rest) -> SomeData (p : rest)
• Relevant bindings include
propogate :: SomeData rest -> SomeData (p : rest)
(bound at Broken.hs:70:1)
Compilation failed.
Я также попробовал класс типов и пытается соответствовать по структуре:
class Propogate sd where
propogateTypeClass :: sd -> sd
-- Base case: We only have 1 element in the promoted type list.
instance Propogate (SomeData parts) where
propogateTypeClass sd = sd
-- Recursie case: More than 1 element in the promoted type list.
instance Propogate (SomeData (p ': parts)) where
propogateTypeClass sd =
case getVal sd of
Just _val ->
sd
Nothing ->
let
-- newSubData :: Maybe subparts
-- Recurse on the subdata if it exists.
newSubData = fmap propogateTypeClass (getSubData sd)
newVal = join . fmap getVal $ newSubData
in
updateVal newVal sd
Это приводит к ошибке:
Broken.hs:125:5-26: error: …
• Overlapping instances for Propogate (SomeData '['A, 'B, 'C])
arising from a use of ‘propogateTypeClass’
Matching instances:
instance Propogate (SomeData parts)
-- Defined at Broken.hs:91:10
instance Propogate (SomeData (p : parts))
-- Defined at Broken.hs:95:10
• In the expression: propogateTypeClass abc
In an equation for ‘x’: x = propogateTypeClass abc
Compilation failed.
Я также пробовал сочетания совпадений на SomeData '[]
и SomeData '[p]
безрезультатно.
Я надеюсь, что мне не хватает чего-то простого, но я не нашел документацию в любом месте о том, как обрабатывать структуру, подобную этой, и я нахожусь в пределах моего понимания, если система типа Haskell, на данный момент в любом случае:).Еще раз, извините за длинный пост и любая помощь будет принята с благодарностью :)
Ничего себе, спасибо за невероятно тщательный ответ. Это имеет прекрасный смысл и настолько элегантнее, особенно часть DataPart. Один вопрос о DataPart - вопрос о том, как добавить данные для других типов. Изменение его на 'A_Data :: ((x ==? 'A) ~' True)' аналогично для остальных не работает. –
А, я понял. 'A_Data :: ((x ==? 'A) ~' True) => Целое число -> DataPart x' работает, но' ... => Целое число -> DataPart 'A' не делает. Однако я не совсем понимаю, почему. –
Тип переменной 'x' в конструкторе' A_Data :: ((x ==? 'A) ~' True) => ... -> DataPart 'A' вообще не связан с переменной типа в индексе типа - он работает только потому, что переменные типа привязаны неявно, но в этих двух случаях переменные типа играют совершенно разные роли - в первом случае конструктор содержит ограничение на параметр типа, в последнем - экзистенциально квантованный тип переменная (с ограничением), а также доказательство того, что параметр точно «A», но экзистенциально квантифицированная переменная и параметр полностью не связаны. – user2407038