Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Polymorphism Instead of Product #8

Open
BebeSparkelSparkel opened this issue Jun 1, 2024 · 8 comments
Open

Use Polymorphism Instead of Product #8

BebeSparkelSparkel opened this issue Jun 1, 2024 · 8 comments

Comments

@BebeSparkelSparkel
Copy link
Owner

BebeSparkelSparkel commented Jun 1, 2024

I wonder how useful the product of monads/applicatives really is. In our paper on bidirectional programming we mainly chose it for simplicity of exposition, but in practice I'd much prefer an approach based on polymorphism. Rather than constructing a biparser as a pair of parser and printer, we can write it as a polymorphic function parameterized by biparser primitives and instantiate it separately to parsers and printers (and possibly more).

Originally posted by @Lysxia in Lysxia/generic-data#68 (comment)

@BebeSparkelSparkel
Copy link
Owner Author

BebeSparkelSparkel commented Jun 1, 2024

@Lysxia Could you elaborate a bit more?

Also, could you define what you think newtype Biparser context s m n u v = Biparser' {unBiparser :: FwdBwd m n u v} code link should be changed to?

@Lysxia
Copy link

Lysxia commented Jun 1, 2024

You would not have a Biparser type, but a class.

class (Profunctor p, forall u. Monad (p u)) => Biparser p where
  char :: p Char Char
  -- other biparser primitives


newtype Parser u v = Parser (String -> Maybe (v, String))
newtype Printer u v = Printer (u -> Maybe (v, String))

instance Biparser Parser where ...
instance Biparser Printer where ...

Then a biparser is a polymorphic value

twoChars :: Biparser p => p String String
twoChars = do
  x <- head `lmap` char
  y <- (head . tail) `lmap` char
  pure [x, y]

@BebeSparkelSparkel
Copy link
Owner Author

The class is a good idea. I think I'll also create transformers so different monads can be used.

newtype Fwd m u v = Fwd {unFwd :: m v}
instance Biparser (Fwd m) where ...

newtype Bwd m u v = Bwd {unBwd :: u -> m v}
instance Biparser (Fwd m) where ...

@BebeSparkelSparkel
Copy link
Owner Author

BebeSparkelSparkel commented Jun 4, 2024

@Lysxia Instead of having a Biparser class what do you think of having capability constraints? Like:

char ::
  ( IsSequence text
  , UpdateStateWithElement s
  , MonadState s m
  , MonadFail m
  , text ~ TextType s
  , char ~ Element text
  ) => p char char

This allows for scaling the monad's capabilities for which combinators are used.

@Lysxia
Copy link

Lysxia commented Jun 5, 2024

I think if you just want to be able to allow different combinations of primitives, having individual classes for those primitives is both simple and good enough.

Exposing MonadState presumes you can get the parser state and put it back, which isn't possible if your parser state is external (e.g. using IORef or streaming from a file handle).

@BebeSparkelSparkel
Copy link
Owner Author

Maybe this is a bad idea, but I was thinking of using lazy IO for those cases.

@BebeSparkelSparkel
Copy link
Owner Author

After thinking about it you are definitely right about the primitives

@BebeSparkelSparkel
Copy link
Owner Author

@Lysxia In some cases there is divergence between forwards and backwards. Is there a way to allow for divergence without having to define a placeholder monad with the constraint instances?

What I have come up with is this but it seems unwieldy since all the constraint instances will have to be defined for Proxy.

class Divergence m f b | m -> f b where
  -- | First argument is run forward and the second is run backwards
  diverge :: f a -> b a -> m a

instance Divergence (Fwd u) (Fwd u) Proxy where diverge = const
instance Divergence (Bwd u) Proxy (Bwd u) where diverge = flip const

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants