Reified dictionaries

Tweet
Posted on February 6, 2017 by Kwang Yul Seo

GADT allows us to reify a constraint as an explicit dictionary. With ConstraintKinds, we can further generalize this trick. In this post, I will explain how this trick works.

In his article, Constraint Kinds for GHC, Max Bolingbroke showed a trick of reifying a constraint as an explicit dictionary using a GADT:

{-# LANGUAGE GADTs #-}

data ShowDict a where
  ShowDict :: Show a => ShowDict a

showish :: ShowDict a -> a -> String
showish ShowDict x = show x

use_showish :: String
use_showish = showish ShowDict 10

How does this trick work? GADTs extension plays an essential role here. When GADTs is enabled, a type-class context given in the constructor is available by pattern matching. In this example above, pattern matching on ShowDict makes the Show a type-class context available in the body of the showish function.

Operationally, the ShotDict constructor has a hidden field that stores the (Show a) dictionary that is passed to ShowDict; so when pattern matching that dictionary becomes available for the right-hand side of the match. Section 9.4.7 of the GHC user guide explains this behavior in details.

We can observe the (Show a) dictionary instance hidden in the constructor by dumping the GHC simplifier output. Pattern matching on the constructor reveals the hidden dictionary $dShow_aKG as follows.

showish_roY :: forall a_ayV. ShowDict a_ayV -> a_ayV -> String
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType]
showish_roY =
  \ (@ a_aKE) (ds_d10M :: ShowDict a_aKE) (x_ayW :: a_aKE) ->
    case ds_d10M of _ [Occ=Dead] { ShowDict $dShow_aKG ->
    show @ a_aKE $dShow_aKG x_ayW
    }

With ConstraintKinds extension, we can further generalize this idea by passing an arbitrary context to the constructor.

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}

data Dict ctxt where
  Dict :: ctxt => Dict ctxt

showish' :: Dict (Show a) -> a -> String
showish' Dict x = show x

use_showish' :: String
use_showish' = showish' Dict 10