A 'let' phrase can be used in two different ways:
let <definitions> in <expression>
let <definitions> in <statement>
However, a 'do' phrase can only be used in this one way:
do <statements> in <expression>
You tried to put 'mx := _mx' after a do, and this is an assignment statement, not an expression.
(Hence the error message "not an expression".)
The reason I don't allow
do <statements> in <statement>
is that I couldn't think of a reason why anybody would write this.
Because this can be replaced by
( <statements> ; <statement> )
It's a compound statement, which is found in almost every imperative programming language.
Thanks for posting this, it is valuable for me to see what kinds of errors people encounter when using Curv.
I can address this problem in one of two ways.
1. I can extend 'do' to be more consistent with 'let', and allow 'do <statements> in <statement>'.
Perhaps more internal consistency will lead to fewer surprises.
2. Or, I can issue a better error message. Something like this:
ERROR: Phrase following 'do...in' is not an expression.
You wrote: 'do <statement-list> in <statement>'. This isn't supported.
You should replace this with '(<statement-list>; <statement>)'.
What do you think?
Perhaps a bigger issue is that the documentation could be improved on the style and idioms you use when writing Curv code. More comments below.
Curv already has a function called 'max'. It takes a list of numbers as an argument, and returns the maximum value. It does the same thing as both maxV and maxX in your program.
Here's a REPL session to illustrate. You can type 'curv' in the shell, with no arguments, and try this code yourself.
curv> max [1,2]
2
curv> max [1,2,3]
3
curv> a = [1,2,3]
curv> a
[1,2,3]
curv> max a
3
The way 'max' works could be surprising to people who are only familiar with languages like Javascript and Python, so I should explicitly document the thinking and coding style behind functions like 'max'.
If max didn't exist and I wanted to implement it using imperative style, I would write:
max v =
do
local mx = -inf;
for (x in v)
if (x > mx) mx := x;
in mx;
In other words, the entire body of the function is a do...in... expression. Within a 'do', I use 'local' to define local variables, and (stmt; stmt; stmt) is a compound statement. The nice thing about 'local' is that you can freely intermix local statements with other statements, without creating a new level of nesting (as is required when you use 'let').
This is another thing that could be more clearly documented.