Skip to content

Commit

Permalink
Update 2013-05-15-from-functions-to-term-rewriting-and-back.md
Browse files Browse the repository at this point in the history
added ```rascal tags to enable syntax highlighting
  • Loading branch information
jurgenvinju authored Jun 12, 2024
1 parent dac1cec commit b0d0df4
Showing 1 changed file with 14 additions and 14 deletions.
28 changes: 14 additions & 14 deletions blog/2013-05-15-from-functions-to-term-rewriting-and-back.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The design choice seems obvious for people who have been programming in ASF+SDF,

In Rascal we write functions in a Java/C/C# like syntax:

```
```rascal
int fac(int n) {
if (n == 0)
return 1;
Expand All @@ -22,13 +22,13 @@ int fac(int n) {

Or slightly shorter and more elegant we could write:

```
```rascal
int f(int n) = n == 0 ? 1 : n * f(n - 1);
```

In fact, function definitions are just rewrite rules with a funny syntax, the `int n` is actually a pattern that matches integers only and binds them to the variable `n`. This means we can write more concrete patterns and separate the case distinction:

```
```rascal
int f(0) = 1;
default int f(int n) = n * f(n - 1);
```
Expand All @@ -39,7 +39,7 @@ The default keyword here indicates to try this alternative only after the other

So far we have written a function which is total, i.e. it has to provide a result for all elements of the parameter types. To make this more rewriting-like, where we have normal forms, consider the interaction with constructor functions in the following example:

```
```rascal
data Bool
= t()
| f()
Expand All @@ -56,7 +56,7 @@ Here we see the `and` function defined for two cases, where the first argument m
For those of us who are used to rewrite rules, we see rules that are labeled by the sort of the terms that are being rewritten.
Here are some example expressions executed in the console:

```
```rascal
rascal>and(t(),t())
Bool: t();
rascal>and(f(),t())
Expand All @@ -69,13 +69,13 @@ Bool: and(or(t(),t()),t())

The key benefit of being able to use pattern matching for dynamic dispatch, is extensibility. Suppose we add "maybe" to our logical language in a separate module. This is possible since data signatures are extensible:

```
```rascal
data Bool = maybe();
```

Now we need to reconsider the semantics of the `and` function, and without changing the original definitions for `and` we simply type these extensions to implement three-valued logic:

```
```rascal
Bool and(maybe(), maybe()) = maybe()
Bool and(maybe(), true()) = maybe()
Bool and(maybe(), false()) = false()
Expand All @@ -96,14 +96,14 @@ These operators may occur in the parameter positions of function definitions, ju

Here is a function to remove double elements from a list, term rewriting style:

```
```rascal
list[value] dup([*value a, value e, *value b, e, *value c]) = dup([*a, e, *b, *c])
default list[value] dup(list[value] l) = l;
```

And here is the same function but type parametrized:

```
```rascal
list[&T] dup([*&T a, &T e, *&T b, e, *&T c]) = dup([*a, e, *b, *c])
default list[&T] dup(list[&T] l) = l
```
Expand All @@ -112,7 +112,7 @@ default list[&T] dup(list[&T] l) = l

The big issue with rewrite rules is that they are applied automatically and this is sometimes cumbersome. Functions do not have this issue. Perhaps we should not have defined the boolean semantics so directly, and have wrapped it in a function:

```
```rascal
data Bool
= t()
| f()
Expand All @@ -126,21 +126,21 @@ Bool eval(and(t(), Bool b)) = b;

This reads as labeled rules, we have two rules called "eval" that could be applied but will never be applied automatically unless somebody calls them as a function.

```
```rascal
rascal>eval(and(f(),t()))
Bool: f()
```

Or, if we wish to apply this rule bottom-up through an entire boolean expression:

```
```rascal
rascal>visit (and(and(f(),t()),t())) { case Bool b => eval(b); }
Bool: f();
```

In the above we used visit to automate the recursion, but we could have manually implemented the recursion as well:

```
```rascal
Bool eval(and(t(), Bool b)) = eval(b);
```

Expand All @@ -158,4 +158,4 @@ This is the reason why all pattern variables in function definitions need to be

## Conclusion

These were some thoughts on the correspondence between functions and rewrite rules as we put it into Rascal. We came from a term rewriting world and wanted to keep using their power of pattern matching and open extensibility. Now we are in a world of functional and imperative programming where we can control their application with the flick of a for loop.
These were some thoughts on the correspondence between functions and rewrite rules as we put it into Rascal. We came from a term rewriting world and wanted to keep using their power of pattern matching and open extensibility. Now we are in a world of functional and imperative programming where we can control their application with the flick of a for loop.

0 comments on commit b0d0df4

Please sign in to comment.