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)