-
Notifications
You must be signed in to change notification settings - Fork 54
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
Distinguish datatypes from other tables #223
Distinguish datatypes from other tables #223
Conversation
/// - The output is not a primitive | ||
/// - No merge function is provided | ||
/// - No default is provided | ||
Function(FunctionDecl), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I wasn't aware you could have functions with non primitive return values and custom merge functions...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's currently a very under-tested feature! I plan to use it more in the future though, it's essential for encoding proofs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be used to simulate the choice
construct in Datalog literature by setting :merge old
(as is used in proofs)
I am giving this branch a test with the Python test suite to see if anything breaks and will let you know this week! |
Thanks! That's super helpful |
Does this PR disallow |
Yep, datatypes can't have merge functions |
Doesn't this cause issues for proofs? Proof terms are datatypes, right? I like the ability to simulate the choice construct (for data types) in Datalog. I'm even thinking about having a |
PR to try out upstream PR: egraphs-good/egglog#223
I gave it a go on the Python bindings, and almost all the tests pass. The one that doesn't might be on my end, so I'll keep looking into it. It's a more complicated example that I am looking to refactor anyway a bit |
default: None, | ||
default: Some(Expr::Lit(Literal::Unit)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am curious why the relation now needs a default to work? It does make it a little more cumbersome to define relations with the function syntax. Would you ever want to define a function that returns Unit
but doesn't have this as a default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question-
Unit
is a primitive just like i64
. Before, we had special logic for making default units, making them special in a weird way. Now, I make it explicit that the relation
syntax is sugar for this function for convenience.
You can still have a function that returns Unit
without this default and use set
everywhere. But this is more convenient
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see.
Is it true then that using a function that does not return a default as an action is a no-op? And that's why you needed the special case before and now with it removed you have added a default?
As a test, this rule seems to have no effect, when there is no default (based on the viz):
(datatype Expr)
(function trigger () Expr)
(function foo () i64)
(trigger)
(rule ((= x (trigger))) ((foo)))
(run 1)
But it does when there is a default:
(datatype Expr)
(function trigger () Expr)
(function foo () i64 : default 10)
(trigger)
(rule ((= x (trigger))) ((foo)))
(run 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, shouldn't you get an exception when there isn't a default?
Otherwise, the action might fail halfway-through!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, so if I am understanding that correctly, only functions with defaults can be used as actions?
Yep, this PR supports proofs. For proofs we need a way to talk about stable ids for terms, so we need some kind of table which is stable (I call those datatypes). If you want something else, just use a non-datatype table instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This makes sense now. I left a small comment but looks great!
@@ -747,9 +752,6 @@ impl EGraph { | |||
let value = if let Some(out) = function.nodes.get(values) { | |||
out.value | |||
} else if make_defaults { | |||
if function.merge.on_merge.is_some() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why's this no longer needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make_defaults
implies that this isn't a datatype. Either there is a merge function, or you get an exception because you tried to merge two primitives.
Tables with merge functions are fundamentally different than those without- we have places in the code where we check for a merge function or defaults. This PR clarifies that distinction.
It also enforces that the
set
keyword is only used on non-datatype tables, clarifying the confusion betweenset
andunion
and making it clear whenset
is needed.Depends on #222