Skip to content

Instantly share code, notes, and snippets.

@tebello-thejane
Last active April 24, 2017 18:02
Show Gist options
  • Save tebello-thejane/ae668f666aefbeec06f94fcdfdfe18f5 to your computer and use it in GitHub Desktop.
Save tebello-thejane/ae668f666aefbeec06f94fcdfdfe18f5 to your computer and use it in GitHub Desktop.
Abusing EKmett's `reflection` library to thread (almost-)arbitrary state through your program.
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Data.Reflection
import Data.Proxy
data T a = T a deriving Show
main :: IO ()
main = do
provideContext (T 'c') $
provideContext 2 $
provideContext "string" $
provideContext plob $
provideContext (T (42 :: Int)) $ -- *1
fa
plob :: Double -> IO ()
plob x = putStrLn $ "I print " ++ (show x)
fa :: (ProvidesContext Integer, ProvidesContext String,
ProvidesContext (T Char), ProvidesContext (T Int),
ProvidesContext (Double -> IO ()))
=> IO ()
fa = do
let c = extractContext :: T Char
putStrLn $ "c is " ++ show c
putStrLn "Doing A"
fc
fb
let theFunc = extractContext :: (Double -> IO ())
theFunc 3.1415926
fb :: ProvidesContext Integer => IO ()
fb = do
let val = extractContext :: Integer
putStrLn $ "Val is " ++ show val
fc :: (ProvidesContext (T Int), ProvidesContext String) => IO ()
fc = do
let t = extractContext :: T Int
putStrLn $ "T is " ++ show t
let str = extractContext :: String
putStrLn $ "Str is " ++ show str
newtype Context a = Context {extract :: a} deriving Show
type ProvidesContext a = Given (Context a) -- *2 & *5
provideContext :: forall a t . a -> (ProvidesContext a => t) -> t -- *4
provideContext a f = give (Context a) f
extractContext :: forall a . ProvidesContext a => a
extractContext =
let
context = given :: Context a -- *3
in
extract context
-- *1 unfortunately type inference fails here, and tries to make the number an Integer.
-- *2 Type context synonyms require ConstraintKinds.
-- *3 We need ScopedTypeVariables so that GHC knows that this `a` is the same as the `a` in the main type.
-- *4 RankNTypes.
-- *5 FlexibleContexts is one of those extensions -- linke MultiParamTypeClasses -- which one is surprised to learn aren't already part of the core language...
@tebello-thejane
Copy link
Author

reflection's documentation for the Given typeclass says that we should be careful to give only a single value of a specific type at a time. This makes Given perfectly useable -- if one is careful -- which is great since I can barely understand the documentation (and Reifies seems to demand one pass around Proxy values).

Consider using this to pass around a global entity cache, configuration data, &c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment