{-# language ConstraintKinds #-}
{-# language DataKinds #-}
{-# language DefaultSignatures #-}
{-# language FlexibleContexts #-}
{-# language FlexibleInstances #-}
{-# language FunctionalDependencies #-}
{-# language GADTs #-}
{-# language GeneralizedNewtypeDeriving #-}
{-# language KindSignatures #-}
{-# language MultiParamTypeClasses #-}
{-# language QuantifiedConstraints #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
module IO.Effects.Internal ( Effect, Program(..), Member(..), ProgramWithHandler, interpret, Handled, HandlerInScope ) where
import Data.Coerce ( Coercible, coerce )
import Unsafe.Coerce ( unsafeCoerce )
class ( forall x y a. Coercible ( e ( Program x ) a ) ( e ( Program y ) a ) ) => Effect e where
coerceEffect :: e ( Program x ) a -> e ( Program y ) a
{-# inline coerceEffect #-}
default coerceEffect :: Coercible ( e ( Program x ) a ) ( e ( Program y ) a ) => e ( Program x ) a -> e ( Program y ) a
coerceEffect =
coerce
instance ( forall x y a. Coercible ( e ( Program x ) a ) ( e ( Program y ) a ) ) => Effect e where
newtype Tagged a b =
Tagged b
class HandlerInScope s e es | s -> e es where
reflect :: Tagged s ( Handler e es )
data Skolem
newtype Magic e es r =
Magic ( HandlerInScope Skolem e es => Tagged Skolem r )
{-# inline withHandler #-}
withHandler
:: forall e es r.
Handler e es
-> ( forall s. HandlerInScope s e es => Tagged s r )
-> r
withHandler a k =
unsafeCoerce ( Magic k :: Magic e es r ) a
newtype Handled e s m a =
Handled ( e ( m :: * -> * ) a )
newtype Program ( es :: [ ( * -> * ) -> * -> * ] ) a =
Program
{ programToIO :: IO a
}
deriving
( Functor, Applicative, Monad )
{-# inline interpret #-}
interpret
:: Effect e
=> ( forall x. e ( Program es ) x -> Program es x )
-> ProgramWithHandler e es a
-> Program es a
interpret f p =
withHandler ( Nat ( f . coerceEffect ) ) ( reduceAndTag p )
where
reduceAndTag :: forall s e es a. Program ( Handled e s ': es ) a -> Tagged s ( Program es a )
reduceAndTag =
coerce
newtype Handler e es =
Nat ( forall a s. e ( Program ( Handled e s ': es ) ) a -> Program es a )
type ProgramWithHandler e es a =
forall s. HandlerInScope s e es => Program ( Handled e s ': es ) a
class Member e es where
send :: e ( Program es ) a -> Program es a
instance {-# overlaps #-} ( Effect e, HandlerInScope s e es ) => Member e ( Handled e s ': es ) where
{-# inline send #-}
send e =
case reflect @s of
Tagged ( Nat f ) ->
coerce ( f e )
instance {-# overlaps #-} ( Effect e, Member e es ) => Member e ( f ': es ) where
{-# inline send #-}
send e =
coerce ( send ( toEs e ) )
where
toEs :: forall a. e ( Program ( f ': es ) ) a -> e ( Program es ) a
toEs =
coerceEffect