Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add closures to Stan language #29

Closed
VMatthijs opened this issue Jan 11, 2019 · 11 comments
Closed

Add closures to Stan language #29

VMatthijs opened this issue Jan 11, 2019 · 11 comments
Labels
feature New feature or request good first issue Good for newcomers

Comments

@VMatthijs
Copy link
Member

The point of this feature is to add lexical closures to the Stan language.
For a possible use case, see e.g. this thread .

It should be relatively straightforward to add these in to stanc3. The AST already treats function definitions as statements that could theoretically occur anywhere in the program. It is the parser that currently disallows that. The point of this issue will be to modify the parser to allow function definitions anywhere and to deprecate the functions block. A challenge would be to change the logic in the semantic check to now check at the level of the whole program whether there is a function declaration which is missing a definition.

A strong requirement for the PR would be a decent set of good and bad test models to make sure sensible error messages are generated.

@VMatthijs VMatthijs added good first issue Good for newcomers feature New feature or request labels Jan 11, 2019
@bob-carpenter
Copy link
Contributor

I think it would make sense to get a design for this before opening an issue. Is there a proposed syntax you have in mind? It's hard to have design discussions in issue comments as there's no quoting or LaTeX support.

Is the idea here that a function currently declared in the functions block would just be declared in the data block because it wouldn't need any closure variables? And just to kick off the syntax discussion, I think that'd look like this:

data {
  (real, real):real hypoteneuse = (real x1, real x2).sqrt(x1^2 + x2^2);
  ...

where we'd do type inference to deduce the return type of the function on the right-hand side to make sure it matched the declaration on the left-hand side. I'm not at all wedded to any of this syntax, by the way---it's just similar to what C++11 does for binding and what I've done in the manual for describing function types.

@VMatthijs
Copy link
Member Author

VMatthijs commented Jan 11, 2019

Ah, for the closures (before we add higher-order functions and lambdas etc which you seem to be describing a syntax for), I imagined we could just keep the current syntax, e.g.:

transformed data {
  real x = 42;
  void an_example_of_a_closure() { // here we define it
    print(x);
  }
  x = 17;
  an_example_of_a_closure(); // here we use it, still prints 42
}

Of course, the semantic check to make sure the return type matches the declaration needs to happen, but the semantic check is currently written in such a way that that should still be true if function definitions occur elsewhere in code than in the functions block.

In my mind they should be allowed anywhere in the program where a statement is currently allowed.
For example, we should also be able to have locally defined functions like

transformed data {
  real x = 42;
  {
    void an_example_of_a_closure() { // here we define it
      print(x);
    }
    an_example_of_a_closure(); // here we use it
  }
  an_example_of_a_closure(); // error here as it is no longer in scope.
}

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 14, 2019 via email

@VMatthijs
Copy link
Member Author

Yes, that's what I was thinking. This could be quite useful and wouldn't be super hard to implement, I hope, as we could just use C++'s closures for code generation.

The higher-order functions and lambdas seem like a separate issue. (Even though they often appear together with closures in languages.) There, we'd definitely need to think carefully about a spec.

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 16, 2019

Yes, that's what I was thinking. This could be quite useful and wouldn't be super hard to implement, I hope, as we could just use C++'s closures for code generation.

Right. That's been on the table since we opened up C++11.

The higher-order functions and lambdas seem like a separate issue. (Even though they often appear together with closures in languages.) There, we'd definitely need to think carefully about a spec.

I think they're very closely related because we can think of a function definition with static lexical scope, such as the following to capture y,

real foo(real x) { return x^3 - y; }

as syntactic sugar for for the more explicit closure form

(real):real foo = (real x).{ return x^3 - y; }

This way, higher-order functions are an orthogonal concern

@VMatthijs
Copy link
Member Author

VMatthijs commented Jan 18, 2019

Ah, right! I see what you mean. You could also theoretically keep the three notions of closures (i.e. functions which store the values of variables from their enclosing scope), lambdas (i.e. anonymous functions) and higher-order functions (functions which take functions as arguments) separate. I can imagine having a language which has any of these features independently of the others. Personally, my feeling is that closures and higher-order functions would be very useful for Stan, while I am not dead set on having lambdas (seeing that I cannot imagine people using functional programming so heavily in Stan that it becomes cumbersome to name functions, especially as we do not have type inference so people will still need to type the types of the arguments on the lambdas).

Personally, I was thinking about the notations
(real) -> real foo = fun (real x) -> { return x^3 - y; }
for function types and lambdas.
That is to be somewhat consistent with common notations.

Notations I've seen for function types (let's do a multi-argument function for generality):
(real, vector) -> int (SML/NJ, OCaml, Coq, Rust)
(real, vector) => int (Haskell, Agda, Scala)
Func<(real, vector), int> (C#)
Function<(real, vector), int> (Java)
function<int(real, vector)> (C++)

Notations that I've seen for lambdas:
fun (x, z) -> x^3 - y (OCaml, Coq)
(x, z) => { return x^3 - y} (Scala)
(x, z) -> { return x^3 - y} (Java)
[]( real x, vector z) { return x^3 - y} (C++)
\(x, z) -> x^3 - y (Haskell, Agda)
fn (x,z) -> { x^3 - y (Rust)
fn [x,z] -> (- (^ x 3) y) (Clojure)

I'm sure there are many more relevant ones, but these are the first that come to mind.

Generally, I have a preference for self-explanatory syntax that is easy to Google, e.g. fun rather than just using some symbol like []. When it comes to the types, I am a bit torn between something like -> and Fun or Func as the latter two can become hard to read when you start nesting them, like for the signature of ODE solvers.
`
I'd like to decide on a syntax for higher order types before we release the new compiler, so the type error messages for functions can already use the function type syntax.

We should probably treat lambdas à la Church rather than Curry (i.e. type annotations on the variable binders) as that would make type checking much easier.

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 18, 2019 via email

@seantalts
Copy link
Member

I think you're right that this belongs somewhere else. I'd propose we use the design-docs repo (and maybe rename it to RFCs to follow Rust?) to post proposals for additions to the Stan language and allow users to comment.

@seantalts
Copy link
Member

I'm going to close this issue for now; @VMatthijs if you or @bob-carpenter (or whoever) want to make a proposal for closure / lambda syntax additions to Stan, please add an RFC for it in the design-docs repo (instructions and template live in that repo).

@nhuurre
Copy link
Collaborator

nhuurre commented Feb 4, 2020

The closure design-doc was accepted a while ago. Should we reopen this?

@seantalts
Copy link
Member

seantalts commented Feb 4, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

4 participants