enso
enso copied to clipboard
Unexpected block behaviour with multiline operators
I ran into a problem when constructing a big JS Object - I wanted to split the construction into several lines and ran into a problem that some of the fields I was adding were missing.
Below I show a simplified repro showing the reconstruction of the issue:
from Standard.Base import all
main =
# Approximation how I found it
json = JS_Object.from_pairs <|
[["type", "Postgres_Connection"], ["libraryName", "Standard.Database"]] +
[["host", "H"], ["port", 1234], ["database_name", "DB"]] +
(if False then [["schema", "det.sch"]] else []) +
(if True then [["credentials", "cred"]] else [])
IO.println json
# But it seems it can be simplified
v1 = [1] +
[2] +
[3] +
[4]
IO.println v1
v2 = [1] +
[2]
IO.println v2
v3 = [1] +
[2] +
[3]
IO.println v3
This yields:
{"type":"Postgres_Connection","libraryName":"Standard.Database","credentials":"cred"}
[1, 4]
[1, 2]
[1, 3]
We can see that only the first and last line of the addition is considered and everything in between is ignored.
Intuitively, we'd expect it to print:
{"type":"Postgres_Connection","libraryName":"Standard.Database","host":"H","port":1234,"database_name":"DB","credentials":"cred"}
[1, 2, 3, 4]
[1, 2]
[1, 2, 3]
After a while, I realized that this is correct according to Enso spec. The thing is that the few indented lines were creating a new block. The block was being executed and only returned the last expression, which was then passed to the top-most +
.
So for
v1 = [1] +
[2] +
[3] +
[4]
The block
[2] +
[3] +
[4]
computed [2].+
(which is an unevaluated function) and [3].+
and then returned [4]
that was passed as second argument to [1] + _
, yielding [1, 4]
.
My confusion stemmed from the fact that I forgot that to use multiline operators in Enso, the operator must be at the beginning of each line, not at the end like I did in the example above. Indeed, once I rewrote the example above to:
from Standard.Base import all
main =
# Approximation how I found it
json = JS_Object.from_pairs <|
[["type", "Postgres_Connection"], ["libraryName", "Standard.Database"]]
+ [["host", "H"], ["port", 1234], ["database_name", "DB"]]
+ (if False then [["schema", "det.sch"]] else [])
+ (if True then [["credentials", "cred"]] else [])
IO.println json
# But it seems it can be simplified
v1 = [1]
+ [2]
+ [3]
+ [4]
IO.println v1
v2 = [1]
+ [2]
IO.println v2
v3 = [1]
+ [2]
+ [3]
IO.println v3
That indeed prints the result as expected above.
We could say that this is by-design and ignore it, but I thought that since it caused my quite a big confusion, it means it will be even more confusing for any new users. Fortunately, the multiline syntax is not yet being used much in the GUI yet. But still I think that we should figure out some way to deal with such problems.
The problem here is that the block is created, discarding intermediate values, in a way that a user may not exactly expect to have a block. Reading the code above, intuitively looks like a valid +
concatenation, not a block with side-effecting values.
I guess one way to deal with such situations is by implementing #5430 or #5431