Description
If you remove type declaration from hoistParserT
compiler will infer this type:
hoistParserT :: ∀ b n s a m.
( m (Tuple (Either ParseError a) (ParseState s))
-> n (Tuple (Either ParseError b) (ParseState s))
)
-> ParserT s m a -> ParserT s n b
This is much useful than (m ~> n) -> ParserT s m a -> ParserT s n a
.
For example if you want a function like this:
todo :: ∀ m x. x -> ParserT s (State x) Unit -> ParserT s m x
You wouldn't find anything that fit's this except hoistParserT
, but you can't push x
from State
out. If type was weakened then it's possible to write this:
todo :: ∀ m x. x -> ParserT s (State x) Unit -> ParserT s m x
todo i p = hoistParserT unState p
where
unState :: ∀ z y m. State x (Tuple (Either y Unit) z) -> m (Tuple (Either y x) z)
unState s = case runState s i of
Tuple (Tuple e state) res -> pure (Tuple (e $> res) state)
So I'm proposing weakening type signature. as otherwise one would need to copy hoistParserT
and change signature, which I think is not desired.
If reason, for using natural transformation, is to not expose internal structure of ParserT
, then we can masaje it a bit:
hoistParserT :: ∀ b n s a m.
(∀ e s'. m (Tuple (Either e a) s')
-> n (Tuple (Either e b) s')
)
-> ParserT s m a -> ParserT s n b
or something like this:
-- name is arbitrary
type Internal s a = Internal (Tuple (Either ParseError a) (ParseState s))
-- derive functor
hoistParserT :: ∀ m a n b. (m (Internal s a) -> n (Internal s b)) -> ParserT s m a -> ParserT s n b
-- or don't even expose arbitrary and change type to:
hoistParserT :: ∀ m a n b f. (Functor f) => (m (f a) -> n (f b)) -> ParserT s m a -> ParserT s n b
hoistParserT f (ParserT m) = ParserT (mapExceptT (mapStateT g) m)
where
g = (map Internal) >>> f >>> (map unInternal)
unInternal (Internal x) = x
-- this way the `todo` implementation will look like this
todo :: ∀ m x. x -> ParserT s (State x) Unit -> ParserT s m x
todo i p = hoistParserT unState p
where
unState :: ∀ m x f. (Functor f) => State x (f Unit) -> m (f x)
unState s = case runState s i of
-- I like this approach as this line is much simpler that in prev implementation
Tuple f res -> pure f $> res
hoist
is not suitable for this new type then maybe call it something else? for example mapParserT
and change hoistParserT
:
+mapParserT f (ParserT m) = ParserT (mapExceptT (mapStateT f) m)
hoistParserT :: (m ~> n) -> ParserT s m ~> ParserT s n
-hoistParserT f (ParserT m) = ParserT (mapExceptT (mapStateT f) m)
+hoistParserT = mapParserT