Conversation
|
If you think this is valuable, I'll go on documenting. Maybe an example in the module doc? And some comments on the instances? |
|
@HeinrichApfelmus ping :) any interest? Are docs needed? |
|
Thanks for pinging me! I finally managed to take a look, specifically concerning the question of whether this function requires access to the constructors or not. 🤔 Wow. 😲 It looks like the definition of If the target were a monad, we could write the following function in terms of hoistProgramT
:: (Monad m, Monad n)
=> (forall a. m a -> n a)
-> ProgramT instr m b
-> ProgramT instr n b
hoistProgramT nat program =
joinProgramT $ nat $ do
firstInstruction <- viewT program
return $ case firstInstruction of
Return a -> return a
i :>>= k -> singleton i >>= (hoistProgramT nat . k)
joinProgramT :: Monad n => n (ProgramT instr n a) -> ProgramT instr n a
joinProgramT = join . liftand then define instance MFunctor (ProgramT instr) where
hoist = hoistProgramTHowever, the Haskell compiler will reject this definition of The reason why you are able to implement it with direct access to the constructors is that the constructor Lift :: m a -> ProgramT instr m a
-- vs
lift :: Monad m => m a -> ProgramT instr m aHm. Given that I think that the definition of |
|
Thanks for coming back to this! :)
I don't quite agree with your assessment. See Gabriella439/Haskell-MMorph-Library#11, people have suggested to add the additional constraint before, and it wasn't needed. I've written quite a few So in summary I believe that:
|
I'm afraid, but I disagree on this point. 😅
hoistProgramT
:: (Monad m, Monad n)
=> (forall a. m a -> n a)
-> ProgramT instr m b
-> ProgramT instr n bexists, and that a function with type signature hoist
:: Monad m
=> (forall a. m a -> n a)
-> ProgramT instr m b
-> ProgramT instr n bmost likely does not exist. I'm happy to add an implementation |
The idea behind
MFunctoris thatProgramTandProgramViewTshould be compatible with functions likelift :: MonadTrans t => m x -> t m x,liftIO :: MonadIO m => IO x -> m x,flip evalStateT s :: StateT s m a -> m a,flip runReaderT r :: ReaderT r m a -> m a, and so on. Such functions are "monad morphisms", andhoistallows to use them on the inner monad of an operational construction.For example,
hoist liftIO :: MonadIO m => ProgramT instr IO a -> ProgramT instr m a.Another typical use case is when you want to handle some part of your monad stack inside
ProgramT, i.e.hoist $ flip runReaderT r :: ProgramT (ReaderT r m) a -> ProgramT m a.I often need functions like this.
To do