Open JKTKops opened 2 years ago
@dwightguth you weren't in the meeting, and we could use your input on this. Do we need backend support to implement this feature?
Yes, you are going to need backend support in order to implement this. Right now the symbol is implemented by means of creating a new function that performs the matching and returns a boolean, but there is no way to propagate the binding out of the function and back to the original rule with this approach. You will have to actually implement native support for this symbol in the backend if you want this capability. I'm not entirely sure how best it should work if that's what you want to do. A where clause on rules might be your best bet rather than trying to implicitly extract the terms from the rule using the existing syntax. If you go that route, then the decision tree generation algorithm in the llvm backend would have to become aware of these bindings, and the code generator would have to be modified in order to correctly produce those values when the side condition is evaluated. You would also have to figure out how to pass the values from the side condition to the rhs, because currently, side condition functions in the llvm backend simply return a boolean, which is not enough information in this case.
By "boolean" I presume you mean an LLVM i1
and not a K Bool
?
In that case, does the LLVM backend already have some matching function (generator, possibly) for regular matches that produces a structure with binding information? I'd think it would be possible to use the same method used for regular matches and treat the results the same way a regular side condition is treated in the decision tree. The new needed support would be a way to gather bindings from it. Perhaps this is exactly what you're suggesting, I'm not sure.
Also probably worth mentioning that Everett pointed out to me that you don't quite get where
bindings for free with this. If the pattern in the LHS of the where
binding doesn't match, the semantics here would be that the case fails to match rather than a crash.
I mean, the llvm backend represents the Bool type as an i1
, but yes, I do mean a native boolean. You might be right that we could just modify the decision tree generator to be aware of the binding symbol and directly bind the variables during matching. That's probably simpler than what I had in mind, but it is contingent on having an explicit guard syntax on sentences rather than trying to treat them as regular functions in the rhs or side condition.
Here is an example that shows the advantage that using :=K
would have over #let
.
syntax Prog ::= "convert" String | "result" Int
configuration <Top>
<k> $PGM:Prog </k>
<sum> 0 </sum>
<brg> 1 </brg>
<another> 2 </another>
<another1> 2 </another1>
</Top>
If the binding restriction on :=K
is lifted we could a rule like the following:
rule <k> convert STR => result I ... </k>
<sum> S => S +Int B +Int I </sum>
<brg> B </brg>
requires I :=K String2Int(STR)
Note that I
is used in multiple cells, but only in the LHS. Also, in the rule that prompted this example, the term that is bound to I
is more complex, and binding it to a variable greatly increases readability of the rule.
Trying to rewrite the above rule using #let
resulted in the following:
rule <Top>
<k> convert STR ~> K </k>
<sum> S:Int </sum> // Read and modified
<brg> B </brg> // Only read
A:AnotherCell
A1:Another1Cell
</Top>
=>
#let I = String2Int(STR) #in
<Top>
<k> result I ~> K </k>
<sum> S +Int B +Int I </sum>
<brg> B </brg>
A
A1
</Top>
=>
to the very top, because #let
cannot contain rewrites.NoSuchElementException: key not found: #cells
.A
and A1
with a single variable REST
, I get the following error: [Error] Compiler: Could not find sorts: [Bag]
. Importing the KCELLS module in which Bag seems to be defined results in more kompile errors.So using #let
is quite inconvenient in this case. Everett provided a quite readable alternative using a helper rule:
rule <k> convert STR => convert_2 String2Int(STR) ... </k>
rule <k> convert_2 I => result I ... </k>
<sum> S => S +Int B +Int I </sum>
<brg> B </brg>
The downside is that this it is somewhat less "obvious" what is going and increases the cognitive load a bit, but in the meantime this looks like a good solution.
For the frontend:
where WHERE_CLAUSES
(and WHERE_CLAUSES
can only be a bunch of _andBool_
of a bunch of _:=K_
).WHERE_CLAUSES
rhs should only contain variables which are already bound by either the LHS of the rule, or a prior clause in the WHERE_CLAUSE
.For the LLVM backend:
For the Haskell backend:
Motivation
This is the binding restriction on
:=K
:This feature would make it possible to describe View Patterns in K. The link is for haskell but has motivating examples. I am unsure what a special syntax sugar for such patterns would be but it also doesn't matter too much since using
:=K
is already pretty quiet.My motivating use case for this is pattern matching on bitstrings. @ehildenb tells me that the Plutus semantics share this use case with me. Consider bitpatterns in erlang. Say I want to write some binary decoding logic for a machine language. In Erlang I can write something like this:
In Haskell, with a bit of work to define the
bits
ViewPattern, I can write it like this:However to define a similar function in K, I have to figure out how to encode the match myself as a combination of
&Int
and==Int
so that I can "lift" it to the outer level. I can't call abits
function on the input, pattern match on the result, and keep trying otherdecode
patterns if that match fails.Example K Code
Suppose K allowed us to bind variables with
:=K
. This function could look something like this:It is also worth mentioning that this feature would also enable a simple syntactic sugar for
where
clauses ala #1685:translates directly into
This would also allow
PAT
to be more complicated than a single variable.Documentation
Remove the sentences about the restriction from the documentation of the pattern matching operator. Under the same section, a new paragraph like this could be added:
Potential Alternatives/Work-arounds
Work-arounds for this feature tend to be very use-specific and involve brittle utilities, code duplication, or both. It becomes necessary to write the logic associated with the view pattern manually, losing out on the benefits of built-in pattern matching in K.
I think it might be possible to emulate the continue-on-fail behavior of a regular pattern match in K today with the
#STUCK
strategy, by I haven't tried that as it would be far more confusing and hard to understand than the custom logic for pattern matching in my case.