danidiaz
danidiaz

Reputation: 27766

Vinyl: rtraverse with a function requiring a constraint shared by all fields

I have constructed a simple example of a Vinyl record. First, some language pragmas and imports:

{-# LANGUAGE DataKinds, TypeOperators #-}

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

the actual example (it employs the HList type synonym for simplicity):

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

This compiles ok. But now I want to print the Vinyl record using rtraverse:

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

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

This gives the following error: No instance for (Show x) arising from a use of ‘printi’. Which is expected I guess, because rtraverse expects a function with no constraints.

How to solve this? It seems like reifyConstraint will be a part of the solution, but I don't know how to use it.

Upvotes: 2

Views: 153

Answers (1)

user2407038
user2407038

Reputation: 14588

You are correct that reifyConstraint will solve this problem. What this function does is convert (or "reify") constraints into datatypes, namely the Dict datatype. For example

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

Each element in this record will have form Dict (Identity _). Dict is defined as

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

Now you simply need a traversal function which can handle a (Dict Show :. Identity) a as an input.

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

Note that you don't need a Show constraint on a - the Show class dictionary is stored in the Dict datatype. You can rtraverse with this function.

main = rtraverse printi (reifyConstraint (Proxy :: Proxy Show) mytuple)

Upvotes: 5

Related Questions