What is a higher-order effect?

To me local “makes sense”, though maybe I’m stuck in stockholm. My reasoning:

  1. The monomorphic version of local is merely modifying an ordinary (locally scoped) function parameter, so no problems here:

    local :: (r -> r) -> ReaderT r m a -> ReaderT r m a
    local f (ReaderT onEnv) = ReaderT $ \env -> onEnv (f env)
    
  2. Similarly, the polymorphic mtl version is pretty routine since it’s just dictionary passing:

    local :: MonadReader r m => (r -> r) -> m a -> m a
    

So my vague impression is that the semantics for effectful, etc. should be pretty sensible too, since they’re morally equivalent to typeclasses, right?

I don’t use local often, but I have used it for namespacing logs. For instance:

addNamespace :: MonadReader [String] m => String -> m a -> m a
addNamespace ns = local (++ [ns])

foo :: MonadReader [String] m => m ()
foo = addNamespace "foo" $ do
  log "something"
  addNamespace "doThing" $ log "more logs"

-- [foo]: something
-- [foo.doThing]: more logs

IMO the dynamic dispatch is muddying the waters with local specifically, since I’m not sure why you’d ever want different behavior. In other words, this static version in bluefin seems useful and sensible:

local ::
  forall r e es a.
  (e :> es) =>
  Reader r e ->
  (r -> r) ->
  (forall e1. Reader r e1 -> Eff (e1 :& es) a) ->
  Eff es a
local (MkReader ns) f = runReader (f ns)

addNamespace ::
  forall e es a.
  (e :> es) =>
  Reader [String] e ->
  String ->
  (forall e1. Reader [String] e1 -> Eff (e1 :& es) a) ->
  Eff es a
addNamespace r ns = local r (++ [ns])

foo ::
  forall e1 e2 es.
  ( e1 :> es,
    e2 :> es
  ) =>
  IOE e1 ->
  Reader [String] e2 ->
  Eff es ()
foo io rdr = addNamespace rdr "foo" $ \rdr2 -> do
  log io rdr2 "something"
  addNamespace rdr2 "doThing" $ \rdr3 -> log io rdr3 "more logs"

I implemented it here.

That said, effectful and friends do let you supply genuine dynamic behavior for Local and Ask. I don’t see why this would be fundamentally impossible with bluefin (fleeting attempt here), but I’ve yet to figure it out.

1 Like