DanielXMoore / Civet

A TypeScript superset that favors more types and less typing

Home Page:https://civet.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Top-level `comptime` block is always `undefined` in the REPL

bbrk24 opened this issue Β· comments

While testing a local change to comptime serialization:

$ ./dist/civet --comptime
Civet 0.7.1 REPL.  Enter a blank line to execute code.
🐱> comptime do
...   f := &
...   f.a = 1
...   f
... 
undefined
🐱> f := comptime do
...   f := &
...   f.a = 1
...   f
... 
undefined
🐱> f
... 
[Function (anonymous)] { a: 1 }
🐱> 

I would have expected the first one to also be [Function (anonymous)] { a: 1 }, but because top-level comptime blocks are deleted, there was nothing to evaluate and the result was undefined.

This is a general question I guess for what the REPL should do for top-level statements that can be expressionized. Currently we do not expressionize, but perhaps we should in the REPL? Compare these examples:

🐱> i .= 5
... while i < 10
...   i++
...
9
🐱> do
...   i .= 5
...   while i < 10
...     i++
...
9
🐱> function f
...   i .= 5
...   while i < 10
...     i++
... f()
...
[ 5, 6, 7, 8, 9 ]

Those 9 return values are kind of magical. I guess it's somehow what eval (or the Node equivalents that we use) defines as the last expression, which is different from how Civet defines it.

So I think it'd be nice to wrap the top level in an IIFE at the Civet level so the last Civet expression gets returned to the REPL. Kind of an extension of the code I added recently to handle top-level await in this way. Amusingly, I think the easy way to wrap in an IIFE is async do, because do is "too smart" and only uses braces if the block is top-level so won't be returned. So it'd be async do in all cases, and await the result, even if there aren't any top-level awaits (but just like we currently do if there were).

Actually, I think this might be an issue worth extending beyond the REPL. There are settings where we write scripts where the last expression is meaningful. eval is one example. My SVG Tiler software is another; though exports are encouraged, mapping files can also be the last expression of the file.

To support these use cases, I propose a "civet iife" or similar directive that wraps the whole thing in an IIFE, so that the last Civet expression gets returned; effectively, nothing is at the top level anymore, so a final do or comptime or whatever will get implicitly returned. IIFEs have other use cases (isolation of variables in nonstrict mode), which is why CoffeeScript does them by default too. So it could have other applications too. But in particular the REPL could use this, I think. (Not totally sure about top-level await. Maybe that could be part of it too? Like it could be an async IIFE wrapper in that case, for environments that don't support top-level await too, and you can await the resulting promise.) Thoughts?