Mercerenies / gdlisp

Lisp on the Godot platform
GNU General Public License v3.0
140 stars 1 forks source link

Coffeescript-style @ on constructor arguments #54

Closed Mercerenies closed 2 years ago

Mercerenies commented 2 years ago

Coffeescript has a neat little feature where you can declare arguments prefixed with an @ and they'll get assigned to relevant instance variables. I think this could be really useful in GDLisp constructors. i.e. my suggestion is to allow

(defclass Foo (Reference)
  (defvar x)
  (defn _init (@x)))

to be equivalent to

(defclass Foo (Reference)
  (defvar x)
  (defn _init (x)
    (set @x x)))

Consider whether we want to restrict this behavior to constructor functions or try to extend it to all GDLisp functions. It can be theoretically useful in other functions, but at the same time it complicates lambda list parsing for all functions instead of just constructor functions (which, right now, use simple lambda lists, since constructors are instance methods and don't support optional/rest arguments)

Mercerenies commented 2 years ago

Oh, hey, this issue is still open.

I implemented this a few hours after opening the issue, over in 811de09981638e1569f45e071f4a6f3f60383234. This issue is closed now. Bye bye.

Mercerenies commented 2 years ago

Oops! I misunderstood. @ is implemented, but not the sugar on constructor args. Re-opening :)

Mercerenies commented 2 years ago

Done as of 3f5f74e

(defclass Foo (Reference) main
  (defvars x y z)
  (defn _init (@x @y @z)))

The above code compiles to

extends Reference
func _init(x_0, y_1, z_2):
    self.x = x_0
    self.y = y_1
    self.z = z_2
var x
var y
var z
static func run():
    return null

Important note: There are several things fighting for control over the initializer right now. These things happen in the following order.

  1. The superclass' constructor is called. This happens even if there's no explicit super call.
  2. Any variables which have initializers declared directly on the defvar but whose initializers are too large to compile to GDScript expressions are initialized.
  3. If the class is a lambda class, then any closure variables are initialized.
  4. Any @foobar arguments (i.e. this issue) are assigned to their argument value.
  5. The actual body of the constructor is executed.

The reasoning for this is as follows.

For a display of all of these in order, see the test called closure_and_init_args_lambda_class_test_2.