Vinyl: rtraverse с функцией, требующей ограничения, общего для всех полей

Я построил простой пример виниловой пластинки. Во-первых, некоторые языковые прагмы и импорт:

{-# LANGUAGE DataKinds, TypeOperators #-}

import Data.Vinyl
import Data.Vinyl.Functor
import Control.Applicative

фактический пример (для простоты он использует синоним типа HList):

mytuple :: HList [Integer,Bool]
mytuple = Identity 4 :& Identity True :& RNil

Это компилируется нормально. Но теперь я хочу напечатать виниловую запись, используя rtraverse:

printi :: Show a => Identity a -> IO (Identity a)
printi (Identity x) = print x *> pure (Identity x)

main :: IO ()
main = rtraverse printi mytuple *> pure ()

Это дает следующую ошибку: No instance for (Show x) arising from a use of ‘printi’, Что ожидается, я думаю, потому что rtraverse ожидает функцию без ограничений.

Как это решить? Это похоже на reifyConstraint будет частью решения, но я не знаю, как его использовать.

1 ответ

Решение

Вы правы, что reifyConstraint решит эту проблему. Эта функция выполняет преобразование (или "reify") ограничений в типы данных, а именно: Dict тип данных. Например

>:t reifyConstraint (Proxy :: Proxy Show) mytuple
(reifyConstraint (Proxy :: Proxy Show) mytuple)
  :: Rec (Dict Show :. Identity) '[Integer, Bool]

Каждый элемент в этой записи будет иметь форму Dict (Identity _), Dict определяется как

data Dict c x where Dict :: c x => x -> Dict c x

Теперь вам просто нужна функция обхода, которая может обрабатывать (Dict Show :. Identity) a в качестве входа.

printi :: Compose (Dict Show) Identity a -> IO (Compose (Dict Show) Identity a) 
printi x@(Compose (Dict a)) = print a >> return x

Обратите внимание, что вам не нужно Show ограничение на a - Show словарь классов хранится в Dict тип данных. Вы можете вернуться с помощью этой функции.

main = rtraverse printi (reifyConstraint (Proxy :: Proxy Show) mytuple)
Другие вопросы по тегам