@hackage reflection1.5.1.2

Reifies arbitrary terms into types that can be reflected back into terms

This package addresses the configuration problem which is propogating configurations that are available at run-time, allowing multible configurations to coexist without resorting to mutable global variables or System.IO.Unsafe.unsafePerformIO.

An example is modular arithmetic where the modulus itself can be supplied at run-time:

foo :: Modular s => Modulus s
foo = 1000 * 1000 * 5 + 2000
>>>

given the following setup:

{-# LANGUAGE ScopedTypeVariables, RankNTypes, ConstraintKinds, FlexibleContexts, UndecidableInstances 125;

import Data.Proxy      (Proxy(Proxy))
import Data.Reflection (Reifies, reflect, reify)

and definitions:

data Modulus s = M { getModulus :: Integer }
type Modular s = Data.Reflection.Reifies s Integer

normalize :: forall s. Modular s => Integer -> Modulus s
normalize n = M (mod n modulus) where
   modulus = Data.Reflection.reflect (Data.Proxy.Proxy :: Data.Proxy.Proxy s)

instance Modular s => Num (Modulus s) where
   M a + M b = normalize (a + b)
   M a * M b = normalize (a * b)

withModulus :: Integer -> (forall s. Modular s => Modulus s) -> Integer
withModulus m v = Data.Reflection.reify m (getModulus . asProxyOf v)
   where
   asProxyOf :: f s -> Proxy s -> f s
   asProxyOf = const

That package is an implementation of the ideas presented in the paper "Functional Pearl: Implicit Configurations" by Oleg Kiselyov and Chung-chieh Shan (original paper). However, the API has been streamlined to improve performance.

Austin Seipp's tutorial Reflecting values to types and back provides a summary of the approach taken by this library, along with more motivating examples.