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

Quotation Monad #374

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft

Quotation Monad #374

wants to merge 13 commits into from

Conversation

gusty
Copy link
Member

@gusty gusty commented Oct 11, 2020

This will implement #44

A quotation monad allow us to compose functions returning expressions.

At the end of the composition we get a single expression, internally containing markers where an evaluate function needs to run a sub-expression.

Since F# doesn't support the "eval" expression, we can't directly run this expressions in existing quotation evaluators, but we include an Expr.run function which takes any normal quotation evaluator and takes care of handling the internal marker. So, instead of doing evaluator expr we do Expr.run evaluator expr.

@cannorin cannorin self-requested a review October 13, 2020 07:54
Copy link
Member

@cannorin cannorin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented around it a lot and I think I understand why the current implementation looks like this. I think this is one of the best we can have for a "quotation monad", but here is my two cents.

Here, we can't make use of the created Expr<'a> value for anything but executing it, because it contains SpliceExpressions which make the expression illegal and have to be removed (by eval). This makes me wondering how this "monad" can be useful, since I suppose most people would want to use the computation expression to create a valid Expr<'a> value that can be consumed by other libraries (without actually running it), but it isn't possible (this is not a problem in MetaOCaml, since it's all about running the code and there is no API to directly manipulate the AST).

Since a F# code can have side effects anywhere, we can't easily define m >>= f without breaking the semantics. We have to syntactically inspect the f and replace every occurrence of the argument with x (this is not much of a problem in Template Haskell since it uses IO monad so it can be done safely). Luckily we can do this for the computation expression binding let! x = m in body by using [<ReflectedDefinition>] since it is always a lambda abstraction, but this means generic >>= operator and generic monad builder are no more usable for the quotation monad.

So I think we have several options:

  • Wrap the Expr<'a> type (in WrappedExpr<'a>, for example) and make it a monad.
    • Then we define val WrappedExpr.run : (Expr -> obj) -> WrappedExpr<'a> -> 'a.
    • And val WrappedExpr.toExpr : WrappedExpr<'a> -> Expr<'a>. This requires the syntactical replacement described above.
  • Create a specialized builder like quotation { ... } for the purpose described above.
    • We also have to implement the syntactical replacement for this.
    • Then we may or may not make Expr<'a> a monad.
  • Do both of them.
  • Do neither and keep it as is.

Also, I found an interesting paper around this topic: https://www.semanticscholar.org/paper/On-Meta-Programming-and-Code-Generation-in-F-Larjani/ca1a48245a767992f4461fe2e66217e7a4fa83d6

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.

None yet

2 participants