haskellvinyl

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.


Solution

  • 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)