Skip to content

Natural transformation is too strong constraint for hoistParserT? #53

Closed
@safareli

Description

@safareli

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions