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

Yilin's powerful syntax technique #1079

Merged
merged 5 commits into from
Feb 19, 2021
Merged

Conversation

kenbot
Copy link
Collaborator

@kenbot kenbot commented Feb 18, 2021

Introduces Yilin's powerful new syntax technique, replacing PR #1055.

  • Starts from Yilin's commits, preserving it in the history
  • Has cake and also eats it, 🎂 nom nom nom
  • Moves the Focus keywords from monocle.syntax.FocusSyntax into a monocle.Focus.KeywordContext trait, because it not like the other xxxSyntax traits and shouldn't live with them or look like them. Alternative naming suggestions welcome
  • Moves the extension ... def focus pre-applied Focus into an AppliedFocusSyntax, which is like the other syntax traits
  • Haven't mixed that into monocle.syntax.all because I haven't figured out how to do that correctly with the different Scala version directories, suggestions welcome I figured it out. Lmk if you want me to remove it from object Focus as well
  • Fixed up error messages that still somehow saying "GenLens"
  • Moved the keyword stripping logic inside the macro, because I considered that it belongs in there, it is not truly an external bolt-on. You can't separate the internal logic from the fact that the keyword context was passed in
  • Split the general lambda function configuring out from ParserLoop into a new thing LambdaConfigParser, which also does the keyword magic stripping. These boundaries make sense, because this initial config stuff is very slow moving and should not change often. The feature loop stuff in ParserLoop however, is fast moving and often needs to change.
  • ParserParams renamed to ParserConfig, and unused type argument removed.

@kenbot kenbot changed the title Yil Yilin's powerful syntax technique Feb 18, 2021
@yilinwei
Copy link
Collaborator

LGTM.

Raised #1080 to track the rest of the issue.

@kenbot kenbot merged commit c67fb0d into optics-dev:master Feb 19, 2021
@simlei
Copy link

simlei commented Jan 14, 2022

Hi, I'm a total outsider here; but I got very interested seeing the KeywordContext ?=> stuff in Monocle3. I tried to figure it out by reading #1055 — but I think an insider's explanation would be super helpful. Would someone care to elaborate or link me to elaborations of how cake is had and eaten too, and how this is probably a Scala 3 feature to be desired by generic library authors? I shall be most happy :)

@kenbot
Copy link
Collaborator Author

kenbot commented Jan 15, 2022

Hi @simlei!
The problem we had was that we wanted both:

  • to have type-safe "keywords" inside the focus expression (eg each in Focus[Basket](_.items.each.price))
  • we didn't want to have to import obnoxious "extensions on everything" , or force the user to separately import the Focus syntax, which we thought would be an unacceptable UX penalty.

We define our "keywords" in the KeywordContext trait; so the context function lambda: (KeywordContext ?=> From => To) means that (a) the From => To lambda is typechecked with respect to an implicitly provided KeywordContext, allowing the keywords to be used within; however (b), the Focus.apply function can't be run without providing a given KeywordContext instance.

So we want the juicy keyword goodness from (a), but we want to avoid paying for (b).

Fortunately, because this is a macro, we can do this. Firstly, the Scala compiler typechecks the lambda term provided by the user, which works, because it sees the KeywordContext ?=> bit. The typechecking we wanted is done - but we don't want the KeywordContext ?=> bit anymore, and certainly don't want to instantiate or provide one. So secondly, inside the macro we match on the user's lambda and throw away the KeywordContext ?=> bit. The real code that gets generated and run is created from the information in the user's supplied lambda argument, but the KeywordContext argument and the keyword method calls (ie .each, .index, etc) are all gone.

Hopefully this is a bit clearer, please feel free to ask more questions.

@simlei
Copy link

simlei commented Jan 15, 2022

Hi @kenbot, thank you very much for taking the time to outline this. I think this clears it up for me :) Also, this is the example most useful for me for learning about context lambdas that I have seen to date.
Cheers :)

@julien-truffaut
Copy link
Member

@kenbot I wonder if there would be a benefit to mark KeywordContext as erased https://docs.scala-lang.org/scala3/reference/experimental/erased-defs.html

@kenbot
Copy link
Collaborator Author

kenbot commented Jan 15, 2022 via email

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

Successfully merging this pull request may close these issues.

4 participants