run two interpreters in one function - Polysemy

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Alex Chapman

I'm trying to run two interpreters in one function, with a type like this:

adaptFormatRemote
   :: Members
     '[ StandardataClient
      , Input (Maybe (HashMap FieldName Text))
      ] r
   => UUID -> UUID
   -> Sem (Input (Maybe [Text]) ': Reader Header ': r) a
   -> Sem r a

It needs to read from input of Maybe (HashMap FieldName Text), then it makes a web API call to work out how to convert this into Maybe [Text], and provide this as input for the next step. This I can do, but the tricky bit is that the web API call also gives me a Header, which I also want to make available for a downstream step. Can anyone give me any pointers for how to do this?

TheMatten

Isn't it that you actually want Input (Maybe [Text], Header)?

Alex Chapman

No, because I will have a stream of [Text], but only one unchanging Header, so it seems wasteful to stream the Header with the [Text].

Alex Chapman

Also, they are consumed at different points in the program.

TheMatten

I see - then it may be more convenient to have Reader on top and do something like:

adapt m = do
  header <- _
  interpret _ (runReader header m)
Alex Chapman

thanks, I'll give that a go

Alex Chapman

seems to have worked -- compiled at least!

Alex Chapman

Another question... is there an easy way to do this:

mapReader :: (b -> c) -> Sem (Reader c ': r) a -> Sem (Reader b ': r) a

It feels a lot like fmap (or contramap), except of course it isn't changing the a type.

Georgi Lyubenov // googleson78

I think reinterpretH will do the job here.
If you don't actually need the local operation of Reader you can entirely avoid the Tactical stuff that's inherent in reinterpretH by using Input instead of Reader and reinterpret instead of reinterpretH

Georgi Lyubenov // googleson78

we had some discussion/progress on this issue here but it went nowhere pretty much

I played around with this a bit to see how it would look. What's peoples opinion on whether this actually brings any benefit (i.e. some functions that use these classes? - I want to be able to...
Alex Chapman

Oh, interesting thought! I'm yet to successfully use any of the Tactical stuff, so avoiding it for now would be nice.

Georgi Lyubenov // googleson78
mapInput f = reinterpret \case
  Input -> fmap f input

something like this?

Alex Chapman

Yes, exactly. Thanks :)

Alex Chapman

/me changes all Readers to Inputs

Alex Chapman

I can do that, and use runInputConst to effectively make Input a Reader. But what if I try the same with Writer, changing it to Output? Is there a runOutputConst equivalent? It might look like this:

runOutputOnce :: (o -> Sem r ()) -> Sem (Output o ': r) a -> Sem r a

There's runOutputSem, which has that type signature, but it runs for every value, and I only want it to run once.

Alex Chapman

Or maybe I'm misunderstanding how this works. If my program only calls output once then runOutputSem will only have one value to act on.

Georgi Lyubenov // googleson78

I don't understand what you mean by runOutputConst :/

Georgi Lyubenov // googleson78

or rather I don't understand what you want runOutputConst to do

if your program calls output once runOutputSem will only "execute" once, but what if it calls output more than once?

Alex Chapman

Yeah, I concluded that it didn't actually make sense, and what I wanted was runOutputSem.