Update dawn-in-coq to new math delimiters
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
parent
e543904995
commit
5eb0d1548c
|
@ -17,13 +17,13 @@ and that's what I reached for when making this attempt.
|
||||||
#### Expressions and Intrinsics
|
#### Expressions and Intrinsics
|
||||||
This is mostly the easy part. A UCC expression is one of three things:
|
This is mostly the easy part. A UCC expression is one of three things:
|
||||||
|
|
||||||
* An "intrinsic", written \\(i\\), which is akin to a built-in function or command.
|
* An "intrinsic", written \(i\), which is akin to a built-in function or command.
|
||||||
* A "quote", written \\([e]\\), which takes a UCC expression \\(e\\) and moves it onto the stack (UCC is stack-based).
|
* A "quote", written \([e]\), which takes a UCC expression \(e\) and moves it onto the stack (UCC is stack-based).
|
||||||
* A composition of several expressions, written \\(e_1\\ e_2\\ \\ldots\\ e_n\\), which effectively evaluates them in order.
|
* A composition of several expressions, written \(e_1\ e_2\ \ldots\ e_n\), which effectively evaluates them in order.
|
||||||
|
|
||||||
This is straightforward to define in Coq, but I'm going to make a little simplifying change.
|
This is straightforward to define in Coq, but I'm going to make a little simplifying change.
|
||||||
Instead of making "composition of \\(n\\) expressions" a core language feature, I'll only
|
Instead of making "composition of \(n\) expressions" a core language feature, I'll only
|
||||||
allow "composition of \\(e_1\\) and \\(e_2\\)", written \\(e_1\\ e_2\\). This change does not
|
allow "composition of \(e_1\) and \(e_2\)", written \(e_1\ e_2\). This change does not
|
||||||
in any way reduce the power of the language; we can still
|
in any way reduce the power of the language; we can still
|
||||||
{{< sidenote "right" "assoc-note" "write \(e_1\ e_2\ \ldots\ e_n\) as \((e_1\ e_2)\ \ldots\ e_n\)." >}}
|
{{< sidenote "right" "assoc-note" "write \(e_1\ e_2\ \ldots\ e_n\) as \((e_1\ e_2)\ \ldots\ e_n\)." >}}
|
||||||
The same expression can, of course, be written as \(e_1\ \ldots\ (e_{n-1}\ e_n)\).
|
The same expression can, of course, be written as \(e_1\ \ldots\ (e_{n-1}\ e_n)\).
|
||||||
|
@ -42,10 +42,10 @@ of an inductive data type in Coq.
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 12 15 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 12 15 >}}
|
||||||
|
|
||||||
Why do we need `e_int`? We do because a token like \\(\\text{swap}\\) can be viewed
|
Why do we need `e_int`? We do because a token like \(\text{swap}\) can be viewed
|
||||||
as belonging to the set of intrinsics \\(i\\), or the set of expressions, \\(e\\). While writing
|
as belonging to the set of intrinsics \(i\), or the set of expressions, \(e\). While writing
|
||||||
down the rules in mathematical notation, what exactly the token means is inferred from context - clearly
|
down the rules in mathematical notation, what exactly the token means is inferred from context - clearly
|
||||||
\\(\\text{swap}\\ \\text{drop}\\) is an expression built from two other expressions. In statically-typed
|
\(\text{swap}\ \text{drop}\) is an expression built from two other expressions. In statically-typed
|
||||||
functional languages like Coq or Haskell, however, the same expression can't belong to two different,
|
functional languages like Coq or Haskell, however, the same expression can't belong to two different,
|
||||||
arbitrary types. Thus, to turn an intrinsic into an expression, we need to wrap it up in a constructor,
|
arbitrary types. Thus, to turn an intrinsic into an expression, we need to wrap it up in a constructor,
|
||||||
which we called `e_int` here. Other than that, `e_quote` accepts as argument another expression, `e` (the
|
which we called `e_int` here. Other than that, `e_quote` accepts as argument another expression, `e` (the
|
||||||
|
@ -69,12 +69,12 @@ Inductive value :=
|
||||||
| v_quot (e : expr).
|
| v_quot (e : expr).
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, `v_quot (e_int swap)` would be the Coq translation of the expression \\([\\text{swap}]\\).
|
Then, `v_quot (e_int swap)` would be the Coq translation of the expression \([\text{swap}]\).
|
||||||
However, I didn't decide on this approach for two reasons:
|
However, I didn't decide on this approach for two reasons:
|
||||||
|
|
||||||
* There are now two ways to write a quoted expression: either `v_quote e` to represent
|
* There are now two ways to write a quoted expression: either `v_quote e` to represent
|
||||||
a quoted expression that is a value, or `e_quote e` to represent a quoted expression
|
a quoted expression that is a value, or `e_quote e` to represent a quoted expression
|
||||||
that is just an expression. In the extreme case, the value \\([[e]]\\) would
|
that is just an expression. In the extreme case, the value \([[e]]\) would
|
||||||
be represented by `v_quote (e_quote e)` - two different constructors for the same concept,
|
be represented by `v_quote (e_quote e)` - two different constructors for the same concept,
|
||||||
in the same expression!
|
in the same expression!
|
||||||
* When formalizing the lambda calculus,
|
* When formalizing the lambda calculus,
|
||||||
|
@ -88,9 +88,9 @@ but also such that here is no way to prove `IsValue (e_int swap)`, since _that_
|
||||||
a value. But what does "provable" mean, here?
|
a value. But what does "provable" mean, here?
|
||||||
|
|
||||||
By the [Curry-Howard correspondence](https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence),
|
By the [Curry-Howard correspondence](https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence),
|
||||||
a predicate is just a function that takes _something_ and returns a type. Thus, if \\(\\text{Even}\\)
|
a predicate is just a function that takes _something_ and returns a type. Thus, if \(\text{Even}\)
|
||||||
is a predicate, then \\(\\text{Even}\\ 3\\) is actually a type. Since \\(\\text{Even}\\) takes
|
is a predicate, then \(\text{Even}\ 3\) is actually a type. Since \(\text{Even}\) takes
|
||||||
numbers in, it is a predicate on numbers. Our \\(\\text{IsValue}\\) predicate will be a predicate
|
numbers in, it is a predicate on numbers. Our \(\text{IsValue}\) predicate will be a predicate
|
||||||
on expressions, instead. In Coq, we can write this as:
|
on expressions, instead. In Coq, we can write this as:
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 19 19 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 19 19 >}}
|
||||||
|
@ -178,8 +178,8 @@ Although the stacks are written in reverse order (which is just a consequence of
|
||||||
I hope that the correspondence is fairly clear. If it's not, try reading this rule out loud:
|
I hope that the correspondence is fairly clear. If it's not, try reading this rule out loud:
|
||||||
|
|
||||||
> The rule `Sem_swap` says that for every two values `v` and `v'`, and for any stack `vs`,
|
> The rule `Sem_swap` says that for every two values `v` and `v'`, and for any stack `vs`,
|
||||||
evaluating `swap` in the original stack `v' :: v :: vs`, aka \\(\\langle V, v, v'\\rangle\\),
|
evaluating `swap` in the original stack `v' :: v :: vs`, aka \(\langle V, v, v'\rangle\),
|
||||||
results in a final stack `v :: v' :: vs`, aka \\(\\langle V, v', v\\rangle\\).
|
results in a final stack `v :: v' :: vs`, aka \(\langle V, v', v\rangle\).
|
||||||
|
|
||||||
With that in mind, here's a definition of a predicate `Sem_int`, the evaluation predicate
|
With that in mind, here's a definition of a predicate `Sem_int`, the evaluation predicate
|
||||||
for intrinsics:
|
for intrinsics:
|
||||||
|
@ -189,8 +189,8 @@ for intrinsics:
|
||||||
Hey, what's all this with `v_quote` and `projT1`? It's just a little bit of bookkeeping.
|
Hey, what's all this with `v_quote` and `projT1`? It's just a little bit of bookkeeping.
|
||||||
Given a value -- a pair of an expression `e` and a proof `IsValue e` -- the function `projT1`
|
Given a value -- a pair of an expression `e` and a proof `IsValue e` -- the function `projT1`
|
||||||
just returns the expression `e`. That is, it's basically a way of converting a value back into
|
just returns the expression `e`. That is, it's basically a way of converting a value back into
|
||||||
an expression. The function `v_quote` takes us in the other direction: given an expression \\(e\\),
|
an expression. The function `v_quote` takes us in the other direction: given an expression \(e\),
|
||||||
it constructs a quoted expression \\([e]\\), and combines it with a proof that the newly constructed
|
it constructs a quoted expression \([e]\), and combines it with a proof that the newly constructed
|
||||||
quote is a value.
|
quote is a value.
|
||||||
|
|
||||||
The above two function in combination help us define the `quote` intrinsic, which
|
The above two function in combination help us define the `quote` intrinsic, which
|
||||||
|
@ -222,8 +222,8 @@ true and false. Why don't we do the same thing?
|
||||||
{{< foldtable >}}
|
{{< foldtable >}}
|
||||||
|UCC Spec| Coq encoding |
|
|UCC Spec| Coq encoding |
|
||||||
|---|----|
|
|---|----|
|
||||||
|\\(\\text{false}\\)=\\([\\text{drop}]\\)| {{< codelines "Coq" "dawn/Dawn.v" 41 42 >}}
|
|\(\text{false}\)=\([\text{drop}]\)| {{< codelines "Coq" "dawn/Dawn.v" 41 42 >}}
|
||||||
|\\(\\text{true}\\)=\\([\\text{swap} \\ \\text{drop}]\\)| {{< codelines "Coq" "dawn/Dawn.v" 44 45 >}}
|
|\(\text{true}\)=\([\text{swap} \ \text{drop}]\)| {{< codelines "Coq" "dawn/Dawn.v" 44 45 >}}
|
||||||
|
|
||||||
Let's try prove that these two work as intended.
|
Let's try prove that these two work as intended.
|
||||||
|
|
||||||
|
@ -234,38 +234,38 @@ I invite you to take a look at the "shape" of the proof:
|
||||||
|
|
||||||
* After the initial use of `intros`, which brings the variables `v`, `v`, and `vs` into
|
* After the initial use of `intros`, which brings the variables `v`, `v`, and `vs` into
|
||||||
scope, we start by applying `Sem_e_comp`. Intuitively, this makes sense - at the top level,
|
scope, we start by applying `Sem_e_comp`. Intuitively, this makes sense - at the top level,
|
||||||
our expression, \\(\\text{false}\\ \\text{apply}\\),
|
our expression, \(\text{false}\ \text{apply}\),
|
||||||
is a composition of two other expressions, \\(\\text{false}\\) and \\(\\text{apply}\\).
|
is a composition of two other expressions, \(\text{false}\) and \(\text{apply}\).
|
||||||
Because of this, we need to use the rule from our semantics that corresponds to composition.
|
Because of this, we need to use the rule from our semantics that corresponds to composition.
|
||||||
* The composition rule requires that we describe the individual effects on the stack of the
|
* The composition rule requires that we describe the individual effects on the stack of the
|
||||||
two constituent expressions (recall that the first expression takes us from the initial stack `v1`
|
two constituent expressions (recall that the first expression takes us from the initial stack `v1`
|
||||||
to some intermediate stack `v2`, and the second expression takes us from that stack `v2` to the
|
to some intermediate stack `v2`, and the second expression takes us from that stack `v2` to the
|
||||||
final stack `v3`). Thus, we have two "bullet points":
|
final stack `v3`). Thus, we have two "bullet points":
|
||||||
* The first expression, \\(\\text{false}\\), is just a quoted expression. Thus, the rule
|
* The first expression, \(\text{false}\), is just a quoted expression. Thus, the rule
|
||||||
`Sem_e_quote` applies, and the contents of the quote are puhsed onto the stack.
|
`Sem_e_quote` applies, and the contents of the quote are puhsed onto the stack.
|
||||||
* The second expression, \\(\\text{apply}\\), is an intrinsic, so we need to use the rule
|
* The second expression, \(\text{apply}\), is an intrinsic, so we need to use the rule
|
||||||
`Sem_e_int`, which handles the intrinsic case. This, in turn, requires that we show
|
`Sem_e_int`, which handles the intrinsic case. This, in turn, requires that we show
|
||||||
the effect of the intrinsic itself; the `apply` intrinsic evaluates the quoted expression
|
the effect of the intrinsic itself; the `apply` intrinsic evaluates the quoted expression
|
||||||
on the stack.
|
on the stack.
|
||||||
The quoted expression contains the body of false, or \\(\\text{drop}\\). This is
|
The quoted expression contains the body of false, or \(\text{drop}\). This is
|
||||||
once again an intrinsic, so we use `Sem_e_int`; the intrinsic in question is \\(\\text{drop}\\),
|
once again an intrinsic, so we use `Sem_e_int`; the intrinsic in question is \(\text{drop}\),
|
||||||
so the `Sem_drop` rule takes care of that.
|
so the `Sem_drop` rule takes care of that.
|
||||||
|
|
||||||
Following these steps, we arrive at the fact that evaluating `false` on the stack simply drops the top
|
Following these steps, we arrive at the fact that evaluating `false` on the stack simply drops the top
|
||||||
element, as specified. The proof for \\(\\text{true}\\) is very similar in spirit:
|
element, as specified. The proof for \(\text{true}\) is very similar in spirit:
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 55 63 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 55 63 >}}
|
||||||
|
|
||||||
We can also formalize the \\(\\text{or}\\) operator:
|
We can also formalize the \(\text{or}\) operator:
|
||||||
|
|
||||||
{{< foldtable >}}
|
{{< foldtable >}}
|
||||||
|UCC Spec| Coq encoding |
|
|UCC Spec| Coq encoding |
|
||||||
|---|----|
|
|---|----|
|
||||||
|\\(\\text{or}\\)=\\(\\text{clone}\\ \\text{apply}\\)| {{< codelines "Coq" "dawn/Dawn.v" 65 65 >}}
|
|\(\text{or}\)=\(\text{clone}\ \text{apply}\)| {{< codelines "Coq" "dawn/Dawn.v" 65 65 >}}
|
||||||
|
|
||||||
We can write two top-level proofs about how this works: the first says that \\(\\text{or}\\),
|
We can write two top-level proofs about how this works: the first says that \(\text{or}\),
|
||||||
when the first argument is \\(\\text{false}\\), just returns the second argument (this is in agreement
|
when the first argument is \(\text{false}\), just returns the second argument (this is in agreement
|
||||||
with the truth table, since \\(\\text{false}\\) is the identity element of \\(\\text{or}\\)).
|
with the truth table, since \(\text{false}\) is the identity element of \(\text{or}\)).
|
||||||
The proof proceeds much like before:
|
The proof proceeds much like before:
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 67 73 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 67 73 >}}
|
||||||
|
@ -276,19 +276,19 @@ Because of this, in this proof writing `apply Sem_apply...` is the same
|
||||||
as `apply Sem_apply. apply Sem_e_int`. Since the `Sem_e_int` rule is used a lot, this makes for a
|
as `apply Sem_apply. apply Sem_e_int`. Since the `Sem_e_int` rule is used a lot, this makes for a
|
||||||
very convenient shorthand.
|
very convenient shorthand.
|
||||||
|
|
||||||
Similarly, we prove that \\(\\text{or}\\) applied to \\(\\text{true}\\) always returns \\(\\text{true}\\).
|
Similarly, we prove that \(\text{or}\) applied to \(\text{true}\) always returns \(\text{true}\).
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 75 83 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 75 83 >}}
|
||||||
|
|
||||||
Finally, the specific facts (like \\(\\text{false}\\ \\text{or}\\ \\text{false}\\) evaluating to \\(\\text{false}\\))
|
Finally, the specific facts (like \(\text{false}\ \text{or}\ \text{false}\) evaluating to \(\text{false}\))
|
||||||
can be expressed using our two new proofs, `or_false_v` and `or_true`.
|
can be expressed using our two new proofs, `or_false_v` and `or_true`.
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 85 88 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 85 88 >}}
|
||||||
|
|
||||||
### Derived Expressions
|
### Derived Expressions
|
||||||
#### Quotes
|
#### Quotes
|
||||||
The UCC specification defines \\(\\text{quote}_n\\) to make it more convenient to quote
|
The UCC specification defines \(\text{quote}_n\) to make it more convenient to quote
|
||||||
multiple terms. For example, \\(\\text{quote}_2\\) composes and quotes the first two values
|
multiple terms. For example, \(\text{quote}_2\) composes and quotes the first two values
|
||||||
on the stack. This is defined in terms of other UCC expressions as follows:
|
on the stack. This is defined in terms of other UCC expressions as follows:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
|
@ -300,11 +300,11 @@ We can write this in Coq as follows:
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 90 94 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 90 94 >}}
|
||||||
|
|
||||||
This definition diverges slightly from the one given in the UCC specification; particularly,
|
This definition diverges slightly from the one given in the UCC specification; particularly,
|
||||||
UCC's spec mentions that \\(\\text{quote}_n\\) is only defined for \\(n \\geq 1\\).However,
|
UCC's spec mentions that \(\text{quote}_n\) is only defined for \(n \geq 1\).However,
|
||||||
this means that in our code, we'd have to somehow handle the error that would arise if the
|
this means that in our code, we'd have to somehow handle the error that would arise if the
|
||||||
term \\(\\text{quote}\_0\\) is used. Instead, I defined `quote_n n` to simply mean
|
term \(\text{quote}_0\) is used. Instead, I defined `quote_n n` to simply mean
|
||||||
\\(\\text{quote}\_{n+1}\\); thus, in Coq, no matter what `n` we use, we will have a valid
|
\(\text{quote}_{n+1}\); thus, in Coq, no matter what `n` we use, we will have a valid
|
||||||
expression, since `quote_n 0` will simply correspond to \\(\\text{quote}_1 = \\text{quote}\\).
|
expression, since `quote_n 0` will simply correspond to \(\text{quote}_1 = \text{quote}\).
|
||||||
|
|
||||||
We can now attempt to prove that this definition is correct by ensuring that the examples given
|
We can now attempt to prove that this definition is correct by ensuring that the examples given
|
||||||
in the specification are valid. We may thus write,
|
in the specification are valid. We may thus write,
|
||||||
|
@ -315,7 +315,7 @@ We used a new tactic here, `repeat`, but overall, the structure of the proof is
|
||||||
the definition of `quote_n` consists of many intrinsics, and we apply the corresponding rules
|
the definition of `quote_n` consists of many intrinsics, and we apply the corresponding rules
|
||||||
one-by-one until we arrive at the final stack. Writing this proof was kind of boring, since
|
one-by-one until we arrive at the final stack. Writing this proof was kind of boring, since
|
||||||
I just had to see which intrinsic is being used in each step, and then write a line of `apply`
|
I just had to see which intrinsic is being used in each step, and then write a line of `apply`
|
||||||
code to handle that intrinsic. This gets worse for \\(\\text{quote}_3\\):
|
code to handle that intrinsic. This gets worse for \(\text{quote}_3\):
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 108 122 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 108 122 >}}
|
||||||
|
|
||||||
|
@ -325,10 +325,10 @@ to write proofs like this itself. Here's what I came up with:
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 124 136 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 124 136 >}}
|
||||||
|
|
||||||
You don't have to understand the details, but in brief, this checks what kind of proof
|
You don't have to understand the details, but in brief, this checks what kind of proof
|
||||||
we're asking Coq to do (for instance, if we're trying to prove that a \\(\\text{swap}\\)
|
we're asking Coq to do (for instance, if we're trying to prove that a \(\text{swap}\)
|
||||||
instruction has a particular effect), and tries to apply a corresponding semantic rule.
|
instruction has a particular effect), and tries to apply a corresponding semantic rule.
|
||||||
Thus, it will try `Sem_swap` if the expression is \\(\\text{swap}\\),
|
Thus, it will try `Sem_swap` if the expression is \(\text{swap}\),
|
||||||
`Sem_clone` if the expression is \\(\\text{clone}\\), and so on. Then, the two proofs become:
|
`Sem_clone` if the expression is \(\text{clone}\), and so on. Then, the two proofs become:
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 138 144 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 138 144 >}}
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ Or, in Coq,
|
||||||
|
|
||||||
{{< codelines "Coq" "dawn/Dawn.v" 148 149 >}}
|
{{< codelines "Coq" "dawn/Dawn.v" 148 149 >}}
|
||||||
|
|
||||||
This is the trick to how \\(\\text{rotate}_n\\) works: it creates a quote of \\(n\\) reordered and composed
|
This is the trick to how \(\text{rotate}_n\) works: it creates a quote of \(n\) reordered and composed
|
||||||
values on the stack, and then evaluates that quote. Since evaluating each value
|
values on the stack, and then evaluates that quote. Since evaluating each value
|
||||||
just places it on the stack, these values end up back on the stack, in the same order that they
|
just places it on the stack, these values end up back on the stack, in the same order that they
|
||||||
were in the quote. When writing the proof, `solve_basic ()` gets us almost all the way to the
|
were in the quote. When writing the proof, `solve_basic ()` gets us almost all the way to the
|
||||||
|
@ -357,7 +357,7 @@ placed back on the stack.
|
||||||
|
|
||||||
### `e_comp` is Associative
|
### `e_comp` is Associative
|
||||||
When composing three expressions, which way of inserting parentheses is correct?
|
When composing three expressions, which way of inserting parentheses is correct?
|
||||||
Is it \\((e_1\\ e_2)\\ e_3\\)? Or is it \\(e_1\\ (e_2\\ e_3)\\)? Well, both!
|
Is it \((e_1\ e_2)\ e_3\)? Or is it \(e_1\ (e_2\ e_3)\)? Well, both!
|
||||||
Expression composition is associative, which means that the order of the parentheses
|
Expression composition is associative, which means that the order of the parentheses
|
||||||
doesn't matter. We state this in the following theorem, which says that the two
|
doesn't matter. We state this in the following theorem, which says that the two
|
||||||
ways of writing the composition, if they evaluate to anything, evaluate to the same thing.
|
ways of writing the composition, if they evaluate to anything, evaluate to the same thing.
|
||||||
|
@ -372,8 +372,8 @@ says:
|
||||||
|
|
||||||
I think that UCC is definitely getting there: formally defining the semantics outlined
|
I think that UCC is definitely getting there: formally defining the semantics outlined
|
||||||
on the page was quite straightforward. We can now have complete confidence in the behavior
|
on the page was quite straightforward. We can now have complete confidence in the behavior
|
||||||
of \\(\\text{true}\\), \\(\\text{false}\\), \\(\\text{or}\\), \\(\\text{quote}_n\\) and
|
of \(\text{true}\), \(\text{false}\), \(\text{or}\), \(\text{quote}_n\) and
|
||||||
\\(\\text{rotate}_n\\). The proof of associativity is also enough to possibly argue for simplifying
|
\(\text{rotate}_n\). The proof of associativity is also enough to possibly argue for simplifying
|
||||||
the core calculus' syntax even more. All of this we got from an official source, with only
|
the core calculus' syntax even more. All of this we got from an official source, with only
|
||||||
a little bit of tweaking to get from the written description of the language to code! I'm
|
a little bit of tweaking to get from the written description of the language to code! I'm
|
||||||
looking forward to reading the next post about the _multistack_ concatenative calculus.
|
looking forward to reading the next post about the _multistack_ concatenative calculus.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user