Я создаю некоторую инфраструктуру для выполнения удаленных вызовов процедур в Haskell, и по причинам, которые здесь слишком длинны для объяснения, я не могу повторно использовать существующие библиотеки.
Итак, вот настройка: у меня есть класс типов для сериализации и десериализации данных:
class Serializable a where
encode :: a -> B.ByteString
decode :: B.ByteString -> Maybe a
maxSize :: a -> Int
, где B
- Data.ByteString .
Я могу использовать это для реализации сериализации целых чисел, логических значений, списков сериализуемых таблиц, кортежей сериализуемых переменных и т. Д.
Теперь я хочу отправить некоторые аргументы по сети на сервер, который затем выполняет вычисления на основе этих аргументов и отправляет обратно результат. Поэтому я создаю экзистенциальный тип, представляющий вещи, которые можно сериализовать:
data SerializableExt = forall t . Serializable t => SerializableExt t
потому что я хочу отправить что-то типа [SerializableExt]
.
Так что, конечно, мне нужно создать экземпляр Serializable SerializableExt
. Здесь начинается проблема:
Для реализации decode :: B.ByteString -> Maybe SerializableExt
мне нужно знать конкретный тип, который оборачивает экзистенциальный тип SerializableExt.
Таким образом, я реализую encode :: SerializableExt -> B.ByteString
как сериализацию конкретного типа вместе со значением:
encode (SerializableExt x) = encode (typeOf x, x)
с использованием typeOf
из Тип данных . Проблема сейчас в реализации decode :: B.ByteString -> Maybe SerializableExt
:
decode bs =
let (tyenc, xenc) = splitPair bs -- Not really important. It just splits bs into the two components
in case (decode tyenc :: Maybe TypeRep) of
Just ty -> SerializableExt <$> _ -- Somehow invoke decode xenc, where the choice of which decode to execute depends on the value of ty.
_ -> Nothing
Но я не вижу, как заполнить дыру здесь. Из-за разделения Хаскеллом уровня значений и уровня типов я не могу использовать значение ty для устранения неоднозначности вызова decode xenc
, верно?
Есть ли способ решить эту проблему и на самом деле положить что-то в дыру, которая будет делать то, что я хочу? Или вы можете придумать другой дизайн?
РЕДАКТИРОВАТЬ: Один из способов сделать это будет следующим:
decode bs =
let (tyenc, xenc) = splitPair bs
in SerializableExt <$>
case (decode tyenc :: Maybe TypeRep) of
Just ty
| ty == typeRep (Proxy :: Proxy Int) -> decode xenc :: Maybe Int
| ty = typeRep (Proxy :: Proxy ()) -> decode xenc :: Maybe ()
| ...
_ -> Nothing
но это плохо по нескольким причинам:
- Расширять утомительно.
- Он не может обрабатывать пары (или вообще: кортежи) в общем; каждый
комбинация типов должна быть обработана.
- Это не очень Хаскели