Skip to content

Instantly share code, notes, and snippets.

@soareschen
Last active February 11, 2018 11:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save soareschen/a4f344c7fd53d21e2f7e6ec7ba259000 to your computer and use it in GitHub Desktop.
Save soareschen/a4f344c7fd53d21e2f7e6ec7ba259000 to your computer and use it in GitHub Desktop.
Constraint-preserving composition of functions with constraints
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FunctionalDependencies #-}
import GHC.Exts
class Handler h a where
type HCtx h a :: Constraint
handle :: HCtx h a => h a -> a -> String
class Handler h b => Middleware m h a b where
type MCtx m a b :: Constraint
extendHandler :: (Handler h b, HCtx h b, MCtx m a b) =>
m h a b -> h b -> (a -> String)
data BaseHandler a = BaseHandler (a -> String)
data ConstrainedHandler (p :: Constraint) a =
ConstrainedHandler (p => a -> String)
data ConstrainedMiddleware
(p :: Constraint) h a b = ConstrainedMiddleware {
getMiddleware :: ((Handler h b, HCtx h b, p) => h b -> a -> String)
}
instance Handler BaseHandler a where
type HCtx BaseHandler a = ()
handle (BaseHandler h) = h
instance Handler (ConstrainedHandler p) a where
type HCtx (ConstrainedHandler p) a = p
handle (ConstrainedHandler h) = h
instance (Handler h b) =>
Middleware (ConstrainedMiddleware p) h a b where
type MCtx (ConstrainedMiddleware p) a b = p
extendHandler (ConstrainedMiddleware m) h = m h
composeHandlers ::
forall f g a.
(Handler f a, Handler g a) =>
f a ->
g a ->
ConstrainedHandler ((HCtx f a), (HCtx g a)) a
composeHandlers f g = ConstrainedHandler $ \c ->
"(compose-handler " ++ (handle f c) ++ " " ++ (handle g c) ++ ")"
applyMiddleware ::
forall m h a b.
(Handler h b,
Middleware m h a b) =>
m h a b ->
h b ->
ConstrainedHandler (HCtx h b, MCtx m a b) a
applyMiddleware m h = ConstrainedHandler $ extendHandler m h
class HasFoo a where
getFoo :: a -> String
class HasBar a where
getBar :: a -> String
fooHandler :: forall a. ConstrainedHandler (HasFoo a) a
fooHandler = ConstrainedHandler $ \x ->
"(foo-handler " ++ (getFoo x) ++ ")"
barHandler :: forall a. ConstrainedHandler (HasBar a) a
barHandler = ConstrainedHandler $ \x ->
"(bar-handler " ++ (getBar x) ++ ")"
barMiddleware ::
forall h a b.
ConstrainedMiddleware (SetBar a b) h a b
barMiddleware = ConstrainedMiddleware $ \h x ->
handle h $ setBar x "injectedBarVal"
combinedHandler = composeHandlers fooHandler barHandler
extendedHandler = applyMiddleware barMiddleware combinedHandler
data Config = Config { foo :: String, bar :: String }
data PartialConfig = PartialConfig { foo :: String }
instance HasFoo Config where
getFoo = foo
instance HasBar Config where
getBar = bar
class HasBar b => SetBar a b | a -> b where
setBar :: a -> String -> b
instance SetBar PartialConfig Config where
setBar (PartialConfig fooVal) barVal = Config { foo = fooVal, bar = barVal }
result = handle extendedHandler $ PartialConfig { foo = "fooVal" }
main :: IO ()
main = putStrLn result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment