Экземпляр функтора для GADT с ограничением типа

Сегодня я хотел исследовать, можно ли построить тип данных таким образом, чтобы он сохранял не данные типа своей сигнатуры типа, а другое его представление. Итак, вот моя попытка GADT, который имеет конструктор типа типа a, но конструктор данных типа ByteString.

{-# LANGUAGE GADTs #-}
import Data.ByteString.Char8
import Data.Serialize

data Serialized a where
    MkSerialized :: (Serialize a) => ByteString -> Serialized a

Теперь я могу определить функцию decode' следующим образом:

decode' :: (Serialize a) => Serialized a -> a
decode' (MkSerialized bs) = let Right r = (decode bs) in r

И это работает:

let s = MkSerialized (encode "test") :: Serialized String
print $ decode' s     -- prints "test"

Моя проблема в том, что я хочу, чтобы Serialized был экземпляром Functor.

instance Functor Serialized where
    fmap f (MkSerialized bs) = MkSerialized (encode (f (right (decode bs))))
                               where right (Right r) = r

Но я получаю ошибку (Serialize b) не может быть выведен. Как я могу ограничить экземпляр Functor, чтобы Serialize применялся в fmap?


person Phae7rae    schedule 17.06.2013    source источник
comment
Вы не можете. Functor не позволяет требовать ограничения на параметры типа. Есть ограниченный класс функтора, RFunctor в rmonad пакете. Может быть, ты сможешь это использовать.   -  person Daniel Fischer    schedule 18.06.2013
comment
Это не связано с вашим вопросом - это действительно невозможно с Functor, но я считаю своим долгом упомянуть: пожалуйста, не используйте Data.ByteString.Char8 по умолчанию! Это неработающий модуль, который поощряет неправильный код. Иногда его можно использовать, но ваш код также хорошо работает с Data.ByteString, что не способствует недопониманию Unicode.   -  person shachaf    schedule 18.06.2013
comment
Как бы то ни было, вы можете создать тип данных в стиле CoYoneda, например data Serialized a where MkSerialized :: Serialize x => ByteString -> (x -> a) -> Serialized a, который хранит ByteString и функцию пост-десериализации, и у которого есть экземпляр Functor. Но, конечно, это противоречит цели.   -  person shachaf    schedule 18.06.2013
comment
Это тесно связано с темой статьи Скалторпа и др. Проблема ограниченной монады.   -  person    schedule 18.06.2013
comment
Я еще не добавлял это в Hackage, но в библиотеке Summit есть Mappable class, который может быть ограничен типами параметров Functor, а также сам тип Functor.   -  person bfops    schedule 18.06.2013


Ответы (1)


Это можно сделать с помощью функтора CoYoneda.

Идея проста: иметь дополнительное функциональное поле, в котором вы собираете свои fmaping функции. Когда вы декодируете свое значение, примените эту функцию.

Вот код:

{-# LANGUAGE GADTs #-}
import Data.ByteString.Char8
import Data.Serialize

data Serialized a where
    MkSerialized
      :: (Serialize a)
      => ByteString -> (a -> b) -> Serialized b

decode' :: Serialized a -> a
decode' (MkSerialized bs f) = let Right r = decode bs in f r

instance Functor Serialized where
    fmap f (MkSerialized bs g) = MkSerialized bs (f . g)

Это также имеет преимущество автоматического объединения нескольких fmap вместо повторяющихся декодирований и кодировок, как это было бы в вашем случае.

person Roman Cheplyaka    schedule 19.06.2013
comment
Хотя это на самом деле не решает мою проблему (так как я бы хотел fmap выполнять повторяющиеся де- / кодировки), я приму этот ответ b / c. Я вижу, что моя первоначальная идея невозможна, и это наиболее практично. способ определить функтор для ограниченного GADT. Кроме того, интересное чтение для функторов GADT и Yoneda. - person Phae7rae; 20.06.2013