Civet icon indicating copy to clipboard operation
Civet copied to clipboard

try/for/if/do expressions don't support return/break/continue/yield

Open edemaine opened this issue 2 years ago • 5 comments
trafficstars

compiled :=
  try
    require('@danielx/civet').compile str, options
  catch err
    return "error"

transpiles to

const compiled = (() => {
  try {
    return require("@danielx/civet").compile(str, options);
  } catch (err) {
    return "error";
  }
})();

I would have expected the return in the catch block to escape the entire function, not just the IIFE.

This is a general issue with IIFEs, including for expressions. I'm not sure what the best solution is. For now I guess we could throw an error if there's a return inside the try block...

edemaine avatar Jan 11 '23 17:01 edemaine

CoffeeScript's solution is to raise a parse error but I think we can do better.

const returnSymbol = Symbol("return")

const compiled = (() => {
  try {
    return {[returnSymbol]:
      require("@danielx/civet").compile(str, options)
    };
  } catch (err) {
    return "error";
  }
})();

if (typeof compiled === "object" && returnSymbol in compiled) return compiled[returnSymbol]

Something along those lines.

STRd6 avatar Jan 11 '23 17:01 STRd6

Agreed! Another option would be to use exceptions (again 😅 ):

try {
  const compiled = (() => {
    try {
      return require("@danielx/civet").compile(str, options);
    } catch (err) {
      throw new ReturnError("error");
    }
  })();
} catch (e) {
  if (e instanceof ReturnError) return e;
  throw e;
}

But I think I prefer your solution so we don't mess with the source of exceptions...

edemaine avatar Jan 11 '23 17:01 edemaine

Also related break label inside of nested iteration iifes (contingent on our eventual label support).

STRd6 avatar Jan 11 '23 22:01 STRd6

Another solution might be not using IFFEs for this at all. Code in the original post can be compiled into something like

let compiled$temp;

try {
  compiled$temp = require("@danielx/civet").compile(str, options);
} catch (err) {
  return "error";
}
const compiled = compiled$temp;

juh9870 avatar Mar 26 '23 16:03 juh9870

@juh9870 Yes, this approach is covered in #381.

Reading about Janet, I realize that this is a special case of a more general feature. See Janet's prompt, return, and signals in general (which is how I believe prompt and return are implemented). We could imagine exposing this to the user as well, and building this functionality on top of it:

out := ::here f()
function f()
  break ::here = 5
---
class PromptHere {}
const out = (function(){
  try {
    return f()
  } catch (e) {
    if (e instanceof PromptHere) {
      return e.value
    } else {
      throw e;
    }
  }
})()
function f() {
  throw new PromptHere(5)
}

edemaine avatar Apr 14 '23 17:04 edemaine