Типы данных с дырками - PullRequest
       16

Типы данных с дырками

5 голосов
/ 25 августа 2011

Так что, если у меня есть два типа данных, которые в основном одинаковы, я могу написать их так:

data A t = A1 | A2 | A3 | A4 (B t)
data B t = B1 | B2 | B3 | B4 t

type AX = A X
type AY = A Y

Теперь легко писать функции через AX и AY или 'A t', если онине волнуетНо тогда как мне написать преобразование?

convert :: AX -> AY
convert (A4 (B4 x)) = A4 (B4 (xToY x))
convert ax = ay -- type error

Так что теперь мне нужно выписать все остальные случаи вручную, хотя ни один из них не зависит от параметра типа.Что еще хуже, хотя я могу сопоставить конструктор, не завися от аргументов с помощью 'A {}', это невозможно, если мне нужны эти аргументы для восстановления данных.

Есть ли более хороший способ сделать это?Я чувствую, что GADT должен быть в состоянии выразить это, но трудно понять, как исключить переменную типа из терминов, которые от нее не зависят.Я думаю, что я должен был бы иметь отдельные типы для A1 A2 и т. Д., И тогда я бы потерял закрытость и проверку регистра ... кроме того, я не хочу писать Show, Eq и Typeable вручную!Единственный другой способ, который я могу придумать, - это перестроить всю структуру, чтобы изолировать изменяющуюся часть, т.е.

data A t = Independent | B4 t
data Independent = A1 | A2 | ...

Но это затрудняет любое другое использование данных в пользу одной функции преобразования.

Конечно, еще один вариант - забыть о безопасности типов и включить 'B4 (Either XY)' и добавить несколько приятных ошибок времени выполнения, когда оно имеет неправильное значение.

Может быть, есть лучший способприблизиться к «почти одинаковым» типам данных?

Обновление: поэтому я обошел стороной, написав псевдо-fmap:

convert :: (a -> b) -> A a -> A b

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

1 Ответ

2 голосов
/ 25 августа 2011

Похоже, что в основном вы хотите, чтобы A и B были Functor с.К счастью, вы можете позволить Haskell получать очевидные Functor экземпляры для этих типов, используя -XDeriveFunctor.Затем используйте этот код:

data A t = A1 | A2 | A3 | A4 (B t) deriving Functor
data B t = B1 | B2 | B3 | B4 t deriving Functor

...

convert :: AX -> AY
convert a = fmap xToY a

Вот пример сеанса GHCi после добавления некоторых фиктивных определений для X, Y и xToY,а также наличие всего производного Show:

*Main> convert $ A4 B1
A4 B1
*Main> convert $ A4 (B4 X)
A4 (B4 Y)
*Main> convert $ A2
A2

Обновление

Если вы действительно хотите, чтобы A и B были «заполнены» вещами типа X или Y, вы можете ограничить их следующим образом:

class XorY t where

instance XorY X
instance XorY Y

data XorY t => A t = A1 | A2 | A3 | A4 (B t)
data XorY t => B t = B1 | B2 | B3 | B4 t

Конечно, это исключит возможность создания A и B Functor с, поэтому описанный выше трюк выигралбольше не работает.

...