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

feat: evaluator #2816

Merged
merged 9 commits into from
Jun 13, 2023
Merged

feat: evaluator #2816

merged 9 commits into from
Jun 13, 2023

Conversation

aljazerzen
Copy link
Member

@aljazerzen aljazerzen commented Jun 12, 2023

This is a PRQL interpreter. It does not allow reading any data from outside sources, but only computes the values of expressions, defined in PRQL.

For example:

1 + 2

... is evaluated into:

3

I've implemented the following things:

  • feat: basics of eval
  • pipelines
  • select, derive, filter
  • aggregate
  • arg closures
  • window and columnar

The purpose of this a tool to aid language design, issue #2723 in particular. I've implemented behavior of "Solution 2", as a trial to see how feasible it is.

Adapting this evaluator is much easier than adapting the whole compiler, so this is a nice tool for experimentation with new language features.

@aljazerzen aljazerzen changed the title eval feat: toy evaluator Jun 12, 2023
@aljazerzen aljazerzen changed the title feat: toy evaluator feat: evaluator Jun 12, 2023
@aljazerzen aljazerzen requested review from max-sixty and snth June 12, 2023 17:14
Copy link
Member

@max-sixty max-sixty left a comment

Choose a reason for hiding this comment

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

No objections, looks like impressive code; I'm not sure what we'll use it for yet, vs. using DuckDB as an engine, but you have much more context than me....

@@ -59,6 +60,12 @@ enum Command {
input: clio_extended::Input,
},

/// Parse & evaluate & generate PRQL code back
// TODO: this is not meant to be used apart from language design helper
// it is not polished (will panic a lot), so should we prefix with `debug:`?
Copy link
Member

Choose a reason for hiding this comment

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

Yeah if we can put it behind a beta group, I've seen that pattern work well. (Though we don't really have groups yet; we could do that with the Sql items too, in the meantime debug: seems fine)

#[test]
fn tuples() {
assert_display_snapshot!(eval(r"
{{a_a = 4, a_b = false}, b = 2.1 + 3.6, c = [false, true, false]}
Copy link
Member

Choose a reason for hiding this comment

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

I didn't know we supported nested types yet!

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, the compiler does not support it, but you can parse them :D

Which is enough for the evaluator.

@aljazerzen
Copy link
Member Author

No, this is not an engine that we'd actually use - it's for trying language features out without having to implement the whole compilation to SQL. For testing out the corner cases and finding the cases of ambiguity.

Think of it as a "semantics specification" of everything that PRQL should be capable of - nested arrays, tuples, weird functional programming patterns, actual recursive functions. But encapsulated into PRQL source, no outside data sources.

@aljazerzen
Copy link
Member Author

Aside: I'm actually quite impressed with language design, as the core of the language semantics fits into 400 SLOC.

@snth
Copy link
Member

snth commented Jun 12, 2023

Great idea for iterating on ideas!

Can you give an example of how I would actually use it?

So I run

prqlc eval "{{a_a = 4, a_b = false}, b = 2.1 + 3.6, c = [false, true, false]}"

and then what do I get back?

@aljazerzen
Copy link
Member Author

So idea is that some PRQL expressions are values are some are not:

  • all literals are values,
  • arrays of values are values,
  • tuples of values are values,
  • functions are values,
  • function calls are not values,
  • operations are not values.

Eval takes an arbitrary expression and converts it into a value. So, evaluating function calls and operations.

Your example is already a value, so it would not be changed. But you could pipe it trough derive {d = (floor b) + 2}, which would return:

{{a_a = 4, a_b = false}, b = 2.1 + 3.6, c = [false, true, false], d = 7}

@aljazerzen aljazerzen enabled auto-merge (squash) June 13, 2023 12:21
@aljazerzen aljazerzen merged commit 5425a67 into main Jun 13, 2023
@aljazerzen aljazerzen deleted the eval branch June 13, 2023 12:38
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.

3 participants