Polysemy: Strategy internals
In previous logs we have seen how to build interpreters thanks to Strategy
.
data BindE (m :: Type -> Type) a where
BindE :: m a -> (a -> m b) -> BindE m b
makeSem ''BindE
interpretBindFinal =
interpretFinal @IO $
\case
BindE f g -> do
fa <- runS f
ff <- bindS g
pure $ fa >>= ff
Let's unbox interpretFinal
:
interpretFinal
:: forall m e r a.
Member (Final m) r
=> (forall x rInitial. e (Sem rInitial) x -> Strategic m (Sem rInitial) x)
-> Sem (e ': r) a
-> Sem r a
interpretFinal n =
let
go = hoistSem $ \u -> case decomp u of
Right (Weaving e s wv ex ins) ->
injWeaving $
Weaving
(WithWeavingToFinal (runStrategy (n e)))
s
(go . wv)
ex
ins
Left g -> hoist go g
in
go
To give a brief overview:
Union m r a
is one of the parts ofSem
and represents effectsinjWeaving
adds an effect to thisUnion
decomp
pops out the first effect in theUnion
, selecting the right one (thanks to types)hoist
andhoistSem
change the lifted MonadsrunStrategy
is part ofStrategy
WithWeavingToFinal
is part ofFinal
Strategy is straightforward:
data Strategy m f n z a where
GetInitialState :: Strategy m f n z (f ())
HoistInterpretation :: (a -> n b) -> Strategy m f n z (f a -> m (f b))
GetInspector :: Strategy m f n z (Inspector f)
type Strategic m n a = forall f. Functor f => Sem (WithStrategy m f n) (m (f a))
type WithStrategy m f n = '[Strategy m f n]
runStrategy sem = \s wv ins -> run $ interpret
(\case
GetInitialState -> pure s
HoistInterpretation f -> pure $ \fa -> wv (f <$> fa)
GetInspector -> pure (Inspector ins)
) sem
to sum it up: Strategy
is mostly a way to inject functions, it is used as follows in Final
:
newtype Final m z a where
WithWeavingToFinal
:: ThroughWeavingToFinal m z a
-> Final m z a
type ThroughWeavingToFinal m z a =
forall f
. Functor f
=> f ()
-> (forall x. f (z x) -> m (f x))
-> (forall x. f x -> Maybe x)
-> m (f a)
As you see: ThroughWeavingToFinal
has the type of runStrategy
, which makes Final
and Strategy
intertwined, and highlight the relevance compared to Embed
.