titzer / virgil

A fast and lightweight native programming language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Lambda support

srackham opened this issue · comments

Virgil doesn't currently support lambdas, but support will be added soon.

I guess this means anonymous functions with closures?

For example:

def intSeq() -> () -> int {
    var i = 0;
    return () -> int {
        i = i + 1;
        return i;
    }
}

Yeah. Internally I decided to call the feature funcexpr (function expressions) and the AST node FuncExpr.

I added a bunch of tests to test/funcexpr and I added parser support, but I disabled that on purpose until I do a stable revision (i.e. check in the results of aeneas bootstrap as the next stable binary), because I don't want stable to support incomplete features.

Personally I kind of like the fat arrow => from JavaScript lambda syntax. I went down that route but it requires backtracking to reinterpret expressions when the => is hit. I wrestled with some alternative syntaxes before eventually just settling on using the def keyword to introduce a function expression.

//@execute = 112
def main() -> int {
	return (def () => 112)();
}

I'd like to further expand the => operator to allow it to denote that a method has a body that consists of a single expression and its return type is implicitly that expression's type.

@execute 33=33
class C(val: int) {
  def inc() => val++;
}
def main(a: int) => C.new(a).inc();

What is the reason for a separate lambda syntax, why not just have anonymous nested methods? This is the route taken by Go, it's more general and there's one less concept e.g. https://gobyexample.com/closures

Actually I think what I am proposing is really close to what Go does.

The fat arrow => is just a shorthand that is actually independent of whether you're declaring a method or creating a closure with an anonymous function (function expression).

E.g. a method:

def foo() => 0;
// is equivalent to:
def foo() -> int {
  return 0;
}

And a function expression:

var x = def() => 0;
// is equivalent to:
var x = def() -> int { return 0; }

Have a look at some of the examples in test/funcexpr, I think it will be clearer.

Have a look at some of the examples in test/funcexpr, I think it will be clearer.

Much clearer, thanks.

The test examples are all single-statement anonymous functions. Are multi-statement anonymous functions allowed? For example is this closure over a multi-statement anonymous function valid?

def intSeq() -> def() -> int {
    var i = 0;
    return def() -> int {
        i = i + 1;
        return i;
    }
}

Yes, that would be valid (in terms of syntax[1]), though I think I want to disallow closing over mutable locals and only allow closing over def locals, loop variables, and assigned-once variables.

[1] with a minor correction in the return type:

def intSeq() -> () -> int {
    var i = 0;
    return def() -> int {
        i = i + 1;
        return i;
    }
}

Java also doesn't allow closing over mutable locals. So you could, e.g. use an array.

def intSeq() -> () -> int {
    def v = [0];
    return def() -> int {
        return v[0]++;
    }
}
// or, shorter
def intSeq() -> () -> int {
    def v = [0];
    return def() => v[0]++;
}

as long as they allow closing over something in outer scopes, Virgil would be able to idiomatically express Knuth's so-called "Man or Boy test" as depicted here for other programming languages - https://rosettacode.org/wiki/Man_or_boy_test

(idiomatically, as opposed to simulating such lambda-closures with classes)

Yes, the idea is that you can indeed close over immutable variables in function scopes.