Last active
April 24, 2017 18:02
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# 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... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
reflection
's documentation for theGiven
typeclass says that we should be careful togive
only a single value of a specific type at a time. This makesGiven
perfectly useable -- if one is careful -- which is great since I can barely understand the documentation (andReifies
seems to demand one pass aroundProxy
values).Consider using this to pass around a global entity cache, configuration data, &c.