Constructor methods!

162 views
Skip to first unread message

Bob Nystrom

unread,
Jul 10, 2015, 12:39:48 PM7/10/15
to wren-lang
Way back in the day, Wren did not have a "new" keyword. Instead, it was more purely object-oriented in that constructors were just methods on metaclasses (i.e. "static" methods), like they are in Ruby and Smalltalk.

In the process of getting constructor inheritance working right, I took a shortcut that involved treating constructor invocations specially, which led to the "new" keyword. I never liked it, but I didn't have any good alternative ideas.

I finally had a realization on how to address the difficulty.

The main problem is that construction involves two methods.

  1. You have a method on the class that allocates a new instance. This has to be on the class so you can do SomeClass.new(). It can't be an instance method since you don't have an instance yet!

  2. You have a method on the instance that initializes the new instance. This has to be an instance method so that you can assign fields, access this, call superclass constructors, etc.
The tricky bit is that all of the constructor arguments need to get forwarded from the allocator method to the initializer. If you call SomeClass.new("a", "b", "c"), those "a", "b", "c" arguments need to make it through the allocator function into the initializer.

I didn't have any clean ideas for doing that in the compiler. Then, while I was falling asleep one night, I realized this is actually trivial. Inside the allocator body for the above example, the stack looks like this:

0: SomeClass
1: "a"
2: "b"
3: "c"

The class object is always the first slot, and the arguments are always after it. So I added a new opcode, OP_CONSTRUCT. It takes a class in slot zero, creates an instance of it, and stores that back in slot zero:

0: instance of SomeClass
1: "a"
2: "b"
3: "c"

That's exactly the setup we need to invoke the initializer. So, when you declare a constructor, you are defining the initializer method and we can synthesize a constructor method on the metaclass for you. Its bytecode is just:

OP_CONSTRUCT
OP_CALL <symbol for initializer>

Simple!

The syntax is pretty straightforward. Inside a class definition, if you prefix a method name with "this", it makes it a constructor:

class Vector {
  this new(x, y) {
    _x = x
    _y = y
  }

  this polar(theta, radius) {
    _x = theta.cos * radius
    _y = theta.sin * radius
  }
}

This does a bunch of nice things.
  • It gets rid of a reserved word and simplifies the grammar.

  • It avoids weird grammar edge cases around the precedence of the expression following new. What does new a.b.c mean?

  • It lets you define "named" constructors. You can define constructors like Vector.polar(). This is a lot clearer than always having to use "new".

  • It encapsulates construction within the class.
The last one is the biggest win for me. It lets a class do things like caching, lazy initialization, etc. without the caller knowing. Whether or not a class method is a constructor is an implementation detail of the method and doesn't affect its calling convention.

The main downsides are:
  • It's a bit more code to implement. While the patch is huge, it's almost all test and doc changes. The actual implement is pretty straightforward, but it's a bit more code.

  • It's less familiar to users coming from C++/Java/C#/etc. I care a lot about familiarity, so this is a real cost. But I think the benefits outweigh it.

  • It totally breaks all of your existing Wren code. Sorry. :( The fix is pretty mechanical, and fortunately "the world's Wren code" is a pretty small thing today, but sorry about this.
I'm really excited about this, but it's possible I'm off base here. I'd love some feedback before we decide what to do with it. The pull request is here:


What do you think?

- bob



Michel Hermier

unread,
Jul 11, 2015, 4:01:57 AM7/11/15
to wren...@googlegroups.com
Hi,

It took me quite some time to read and formulate my thoughts on the patch.

While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.
I mean if 'class Foo { new {} }' populates Foo and its metaclass with a method named 'new', the user can't properly redefine metaclass 'new' without creating an ambiguity in the compiler. So we need a way to distinguish 'automagic new' of 'new as a regular method'. This means that we need to to have 'new new () {}' as a constructor to allow 'new () {}' as a regular method. And all this defeat the interest of using 'new' as the keyword constructor.

In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:

class Foo {
  static new (args...) {
  }

  new (args...) {
  }
}

since new is no longer a keyword.

So in the end to sum up, 'this' as a keyword in front of a method declaration only make it auto construct the static part which is a nice syntaxic sugar in the end.

For the technicall part, if possible I would like an extra change to the compiler so that constructor functions are not specials as they are now. If you look at the compiler currently constructors are specials in the sence that they allways return 'this'. I really don't like that exception. It can be solve by changing a little the effect of the CONSTRUCT opcode so it transform the stack to:

     0: instance of SomeClass
     1: instance of SomeClass
     2: "a"
     3: "b"
     4: "c"

And the assembly would look like:
     CONSTRUCT 3
     CALL 3, 'new(_,_,_)'
     RETURN

It involves moving the arguments and making an extra copy, but when performin CALL we then can discard its result and return the instance of SomeClass left on the stack.
In addition since this requires CONSTRUCT to have the number of items to copy, so it must/can be made to work with the top of the stack instead of the bottom. This also make it more like and optimised/inlined version of what one would write for the *generic object factory* one would write like (in pseudo code):

     static new(args ...) {
        var instance = super.new() // or this.instantiate() (depending if meta class inheritance is available)
        instance.new(args...)
        return instance
     }

Maybe we can contract it even more with:

   CONSTRUCT 3, 'new(_,_,_)'
   RETURN

Where CONSTRUCT stack result would be:
   0: instance of SomeClass
   1: result of 'new(_,_,_)' call

But I'm not sure which one would be better, since internally it should do the same operations as described earlier.

About the changes you pushed to branch, I wonder if in the conversion process you didn't lost "new Object" forgeting to add new to Object metaclass (I didn't took the time to ensure this, I'm still experimenting the 'native code has frames' change, with mitiged results because of -Os inlining when it wants)

Cheers,
  Michel
--
You received this message because you are subscribed to the Google Groups "Wren" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wren-lang+...@googlegroups.com.
To post to this group, send email to wren...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wren-lang/CAMu6JH93AwHsXJxqbdYA5Fz3M6sqL_x1XiF0acCvR2ZQnJVKMQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Thorbjørn Lindeijer

unread,
Jul 12, 2015, 8:51:49 AM7/12/15
to wren...@googlegroups.com
On Sat, Jul 11, 2015, at 10:01, Michel Hermier wrote:
While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.
 
Hmm, I'm afraid I can't follow this. What is "auto construction of static metaclass 'new'"? Also, why would having the default "new" be a nameless static method pose a problem for custom factories?
 
I mean if 'class Foo { new {} }' populates Foo and its metaclass with a method named 'new', the user can't properly redefine metaclass 'new' without creating an ambiguity in the compiler. So we need a way to distinguish 'automagic new' of 'new as a regular method'. This means that we need to to have 'new new () {}' as a constructor to allow 'new () {}' as a regular method. And all this defeat the interest of using 'new' as the keyword constructor.
 
In my proposal, "class Foo { new {} }" is simply invalid, because it tries to create a nameless constructor method without parameter list. Did you mean "class Foo { new() {} }"? That would declare an empty constructor method on the class, which can then be instantiated with "Foo()". However, this would already be the case by default. I do not understand why there would be a need to dinstinguish between the "automagic new" (you mean default constructor?) or "new as a regular method". Wouldn't it just be confusing to use "new" as a regular method? Possibly it's even invalid since you can't name a method the same as a keyword.
 
In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:
 
class Foo {
  static new (args...) {
  }
 
  new (args...) {
  }
}
 
since new is no longer a keyword.
 
With making factories transparent, I guess you mean you can't dintinguish between when an actual constructor is called or when some regular static method is called that in turn instantiates something. You don't need "new" to be not a keyword for this:
 
class FooInstance {}
class Foo {
  static () { FooInstance() }
}
 
Here, class FooInstance relies on the default "new () {}" constructor, and Foo creates instances of FooInstance, but you call it the same way as if you are instantiating a "Foo":
 
var foo = Foo()
 
 
So in the end to sum up, 'this' as a keyword in front of a method declaration only make it auto construct the static part which is a nice syntaxic sugar in the end.
 
For the technicall part, if possible I would like an extra change to the compiler so that constructor functions are not specials as they are now. If you look at the compiler currently constructors are specials in the sence that they allways return 'this'. I really don't like that exception. It can be solve by changing a little the effect of the CONSTRUCT opcode so it transform the stack to:
 
     0: instance of SomeClass
     1: instance of SomeClass
     2: "a"
     3: "b"
     4: "c"
 
And the assembly would look like:
     CONSTRUCT 3
     CALL 3, 'new(_,_,_)'
     RETURN
 
It involves moving the arguments and making an extra copy, but when performin CALL we then can discard its result and return the instance of SomeClass left on the stack.
In addition since this requires CONSTRUCT to have the number of items to copy, so it must/can be made to work with the top of the stack instead of the bottom. This also make it more like and optimised/inlined version of what one would write for the *generic object factory* one would write like (in pseudo code):
 
     static new(args ...) {
        var instance = super.new() // or this.instantiate() (depending if meta class inheritance is available)
        instance.new(args...)
        return instance
     }
 
While I do not fully understand where you're going with this pseudo code, I believe it could look like this when "new" was a keyword creating a static constructor method:
 
     static (args ...) {
        var instance = super() // or this.instantiate() (depending if meta class inheritance is available)
        instance(args...)
        return instance
     }
 
The whole point is just that a nameless method is callable directly from the class (in case of static) or instance name. This should just make the code shorter to write and more pleasant to read. This should not be a factor in whether you can or can't write a generic object factory.
 
Regards,
Bjørn

Michel Hermier

unread,
Jul 12, 2015, 12:52:46 PM7/12/15
to wren...@googlegroups.com


Le dimanche 12 juillet 2015 14:51:49 UTC+2, Thorbjørn Lindeijer a écrit :
On Sat, Jul 11, 2015, at 10:01, Michel Hermier wrote:
While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.
 
Hmm, I'm afraid I can't follow this. What is "auto construction of static metaclass 'new'"? Also, why would having the default "new" be a nameless static method pose a problem for custom factories?

When hitting the constructor declaration the compiler will produce 2 methods in the new compiler. A static version that is auto constructed that create the instance, and call the non static one with the provided body. So with new syntax when compiler see:

class Foo {
   this new (args...) {
      /*code*/
   }
}

it produces something like:

class Foo {
   static new (args...) { instantiate().new(args...) }
   new (args...) {
      /*code*/
      return this
   }
}

So it auto create a "static new" method.

If old syntax is used how do you distinguish when the compiler should produce or not the static "new" method. If it is produced unconditionnaly, it will fails when user will want to override it to use a custom instance allocator. By making the compiler more intelligent we can circumvent it, by making the production of it at the end of the class declaration if a user defined version exist.

But if we enable super class inheritance one day (which is something I really want to see), we will loose the custom factory since "new (args...) { }" will unconditionnaly override "static new (args...) { }".

So I think like Bob, we need a new way to define that a function is a constructor with a given name. The only thing is that 'this' might not be the more appropriate keyword, but I agree that reducing the number of keyyword is a great thing.

 
I mean if 'class Foo { new {} }' populates Foo and its metaclass with a method named 'new', the user can't properly redefine metaclass 'new' without creating an ambiguity in the compiler. So we need a way to distinguish 'automagic new' of 'new as a regular method'. This means that we need to to have 'new new () {}' as a constructor to allow 'new () {}' as a regular method. And all this defeat the interest of using 'new' as the keyword constructor.
 
In my proposal, "class Foo { new {} }" is simply invalid, because it tries to create a nameless constructor method without parameter list. Did you mean "class Foo { new() {} }"? That would declare an empty constructor method on the class, which can then be instantiated with "Foo()". However, this would already be the case by default. I do not understand why there would be a need to dinstinguish between the "automagic new" (you mean default constructor?) or "new as a regular method". Wouldn't it just be confusing to use "new" as a regular method? Possibly it's even invalid since you can't name a method the same as a keyword.
 
In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:
 
class Foo {
  static new (args...) {
  }
 
  new (args...) {
  }
}
 
since new is no longer a keyword.
 
With making factories transparent, I guess you mean you can't dintinguish between when an actual constructor is called or when some regular static method is called that in turn instantiates something. You don't need "new" to be not a keyword for this:
 
class FooInstance {}
class Foo {
  static () { FooInstance() }
}
 
Here, class FooInstance relies on the default "new () {}" constructor, and Foo creates instances of FooInstance, but you call it the same way as if you are instantiating a "Foo":
 
var foo = Foo()

Unless I missed something unamed method is an absolute NO NO by langage construction. It introduce the unsolvable ambiguity "foo(args...)" means the usual "foo(args...)" or "foo.(args...)".

What I meant is that if user do:
   Foo.new(args...)

It should works either if Foo is declared with:

  class Foo {
    this new (args...) {
      /*code*/
    }
  }

or


  class Foo {
    static new (args...) {
      var instance = MyFooAllocator.allocate()
      instance.new(args...)
      return instance
    }
    new (args...) {
      /*code*/
    }
  }

So the user use the same construct for both and don't deal will instance creation strategies. In the facts we allow overriding the 'new' operator with this trick.

In other language like java, because you can't override the 'new' operator, to deal with that problem you have to go to the factory strategy, meaning that the user needs to learn about thing that it should not be aware of.
 
 
 
So in the end to sum up, 'this' as a keyword in front of a method declaration only make it auto construct the static part which is a nice syntaxic sugar in the end.
 
For the technicall part, if possible I would like an extra change to the compiler so that constructor functions are not specials as they are now. If you look at the compiler currently constructors are specials in the sence that they allways return 'this'. I really don't like that exception. It can be solve by changing a little the effect of the CONSTRUCT opcode so it transform the stack to:
 
     0: instance of SomeClass
     1: instance of SomeClass
     2: "a"
     3: "b"
     4: "c"
 
And the assembly would look like:
     CONSTRUCT 3
     CALL 3, 'new(_,_,_)'
     RETURN
 
It involves moving the arguments and making an extra copy, but when performin CALL we then can discard its result and return the instance of SomeClass left on the stack.
In addition since this requires CONSTRUCT to have the number of items to copy, so it must/can be made to work with the top of the stack instead of the bottom. This also make it more like and optimised/inlined version of what one would write for the *generic object factory* one would write like (in pseudo code):
 
     static new(args ...) {
        var instance = super.new() // or this.instantiate() (depending if meta class inheritance is available)
        instance.new(args...)
        return instance
     }
 
While I do not fully understand where you're going with this pseudo code, I believe it could look like this when "new" was a keyword creating a static constructor method:
 
     static (args ...) {
        var instance = super() // or this.instantiate() (depending if meta class inheritance is available)
        instance(args...)
        return instance
     }
 
The whole point is just that a nameless method is callable directly from the class (in case of static) or instance name. This should just make the code shorter to write and more pleasant to read. This should not be a factor in whether you can or can't write a generic object factory.

While I agree, since syntax won't allow this ever, and to be *consistant* with common user knownledge (class as an operator 'new' in what ever form), "Foo.new(args)" seems the only valid choice. ( and surprisingly ;) it match Smalltalk constructor style which is a good point for me)

Cheers,
   Michel



Bob Nystrom

unread,
Jul 12, 2015, 1:42:14 PM7/12/15
to wren-lang
On Sat, Jul 11, 2015 at 1:01 AM, Michel Hermier <michel....@gmail.com> wrote:
Hi,

It took me quite some time to read and formulate my thoughts on the patch.

While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.

That's right. If you want to have a static method named new that is not a constructor, that get's harder to specify. I think it's important to enable this because I want to fully encapsulate "factories" inside the class. A user shouldn't know whether a method they invoke on a class is a "real" constructor or some other kind of factory method.
 
In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:

class Foo {
  static new (args...) {
  }

  new (args...) {
  }
}

Just to make it clearer, in this proposal, it would be something like:

class Foo {
  // Looks like a constructor, but returns a cached instance.
  static new(name) {
    if (__cache == null) __cache = {}

    if (__cache.containsKey(name)) {
      return __cache[name]
    }

    return __cache[name] = Foo.realNew(name)
  }

  this realNew(name) {
    _name = name
  }
}

This would let you do Foo.new(name), which looks like a normal constructor call, but which is actually just calling a static method. If we make new a reserved word, we'd have to come up with some special way to enable this.

For the technicall part, if possible I would like an extra change to the compiler so that constructor functions are not specials as they are now. If you look at the compiler currently constructors are specials in the sence that they allways return 'this'. I really don't like that exception.

Why don't you like this? It doesn't add much complexity to the compiler as far as I can tell.
 
It can be solve by changing a little the effect of the CONSTRUCT opcode so it transform the stack to:

     0: instance of SomeClass
     1: instance of SomeClass
     2: "a"
     3: "b"
     4: "c"

And the assembly would look like:
     CONSTRUCT 3
     CALL 3, 'new(_,_,_)'
     RETURN

It involves moving the arguments and making an extra copy, but when performin CALL we then can discard its result and return the instance of SomeClass left on the stack.
In addition since this requires CONSTRUCT to have the number of items to copy, so it must/can be made to work with the top of the stack instead of the bottom.

This seems like more complexity than the current implementation. What's the reason to motivate this?
 
This also make it more like and optimised/inlined version of what one would write for the *generic object factory* one would write like (in pseudo code):

     static new(args ...) {
        var instance = super.new() // or this.instantiate() (depending if meta class inheritance is available)
        instance.new(args...)
        return instance
     }

My hunch is that we don't want metaclass inheritance. From talking to a few old Smalltalkers, they generally consider it a mistake there. Also, Java, C++, C#, etc. have semantics more similar to not allowing metaclass inheritance (in other words, static methods aren't inherited), and it seems to work well there.
 
About the changes you pushed to branch, I wonder if in the conversion process you didn't lost "new Object" forgeting to add new to Object metaclass (I didn't took the time to ensure this, I'm still experimenting the 'native code has frames' change, with mitiged results because of -Os inlining when it wants)

You're correct, this is gone now. I think that's probably a feature more than a bug. I don't know if it's useful to be able to construct instances of Object so I just disabled that for now. If we do find a use for that, it's easy to give it a constructor.

Cheers!

- bob

Michel Hermier

unread,
Jul 12, 2015, 7:29:11 PM7/12/15
to wren...@googlegroups.com
Le 12/07/2015 19:41, Bob Nystrom a écrit :


On Sat, Jul 11, 2015 at 1:01 AM, Michel Hermier <michel....@gmail.com> wrote:
Hi,

It took me quite some time to read and formulate my thoughts on the patch.

While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.

That's right. If you want to have a static method named new that is not a constructor, that get's harder to specify. I think it's important to enable this because I want to fully encapsulate "factories" inside the class. A user shouldn't know whether a method they invoke on a class is a "real" constructor or some other kind of factory method.
 
In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:

class Foo {
  static new (args...) {
  }

  new (args...) {
  }
}

Just to make it clearer, in this proposal, it would be something like:

class Foo {
  // Looks like a constructor, but returns a cached instance.
  static new(name) {
    if (__cache == null) __cache = {}

    if (__cache.containsKey(name)) {
      return __cache[name]
    }

    return __cache[name] = Foo.realNew(name)
  }

  this realNew(name) {
    _name = name
  }
}

This would let you do Foo.new(name), which looks like a normal constructor call, but which is actually just calling a static method. If we make new a reserved word, we'd have to come up with some special way to enable this.

True, I didn't thought that it would imply to forbid the symbol to be used as this to create the instance, we need a different constructor signature.



For the technicall part, if possible I would like an extra change to the compiler so that constructor functions are not specials as they are now. If you look at the compiler currently constructors are specials in the sence that they allways return 'this'. I really don't like that exception.

Why don't you like this? It doesn't add much complexity to the compiler as far as I can tell.
 
It can be solve by changing a little the effect of the CONSTRUCT opcode so it transform the stack to:

     0: instance of SomeClass
     1: instance of SomeClass
     2: "a"
     3: "b"
     4: "c"

And the assembly would look like:
     CONSTRUCT 3
     CALL 3, 'new(_,_,_)'
     RETURN

It involves moving the arguments and making an extra copy, but when performin CALL we then can discard its result and return the instance of SomeClass left on the stack.
In addition since this requires CONSTRUCT to have the number of items to copy, so it must/can be made to work with the top of the stack instead of the bottom.

This seems like more complexity than the current implementation. What's the reason to motivate this?
Simplify the compiler, and make the instance part of the constructor a regular function. Seeing the argument 'isConstructor' passed in the compiler is making me say we are doing something bad here. Why would the constructor need to return 'this' it has to be a regular function. This would also the constructor to return a marker for its subclass:

class Foo {
   this new { __id = __id != null ? __id + 1 : 0 }
}

class Bar is Foo {
   this new {
     if (super == 0) {IO.print("I'm the root of the universe") }
   }
}

I agree the example is a little artificial, but maybe we can do something of this feature someday. Or for something maybe more credible, a collision of named constructor/setter that might need/want to return an interesting value and not this (idea stoken from smalltalk where there is only methods and constructor (as this) don't exists and are regular methods potentially returning anything)


 
This also make it more like and optimised/inlined version of what one would write for the *generic object factory* one would write like (in pseudo code):

     static new(args ...) {
        var instance = super.new() // or this.instantiate() (depending if meta class inheritance is available)
        instance.new(args...)
        return instance
     }

My hunch is that we don't want metaclass inheritance. From talking to a few old Smalltalkers, they generally consider it a mistake there. Also, Java, C++, C#, etc. have semantics more similar to not allowing metaclass inheritance (in other words, static methods aren't inherited), and it seems to work well there.
Well they might be a mistake, but it sounds logicall for some parts, else when writting stating methods and you need to call super static methods. Ohh wait in static 'this' is an in stance of Class, so we can use 'supertype', maybe it is enough to do the trick for most use case. I'm felling it was a great idea I introduced it ;)

Instantiation is the common place where class needs to be remebered, and is generaly why metaclass inheritance is introduced I guess (so that super.new return an instance of the correct class). Not having them adds a little annoyance in that one as to rewrite 'static new' calls to use custom allocations strategies of parent class.
Ex1: Rethink of your example with a subclass of Foo that want to be in the cache.
Ex2: In a *real* usage, an abstract resource loader providing factory throught Ressource.new(path) and a dedicated ones ImageRessource.new(path), SoundRessource.new(path)... (I can explain more on this if you don't see what I mean).
I hope these are pretty rare to justify the needed extra code. But I agree enabling metaclass inheritance might be worse because __vars become class vars and not real static ones, making more problems than it solves.

Maybe a work around would be to add the 'using' keyword more or less like in C++, but sounds very hard to implement.

 
About the changes you pushed to branch, I wonder if in the conversion process you didn't lost "new Object" forgeting to add new to Object metaclass (I didn't took the time to ensure this, I'm still experimenting the 'native code has frames' change, with mitiged results because of -Os inlining when it wants)

You're correct, this is gone now. I think that's probably a feature more than a bug. I don't know if it's useful to be able to construct instances of Object so I just disabled that for now. If we do find a use for that, it's easy to give it a constructor.

There is one pretty used, the unique object that act as a marker for something and that is not possibly null (like tombstone in our map implementation).

Cheers,
   Michel

Michel Hermier

unread,
Jul 13, 2015, 4:13:58 PM7/13/15
to wren...@googlegroups.com


Le dimanche 12 juillet 2015 19:42:14 UTC+2, Bob Nystrom a écrit :


On Sat, Jul 11, 2015 at 1:01 AM, Michel Hermier <michel....@gmail.com> wrote:
Hi,

It took me quite some time to read and formulate my thoughts on the patch.

While I really like Bjon idea of using 'new' as the constructor keyword  (https://github.com/munificent/wren/pull/283#issuecomment-120517433), I think it will expose lots of problems regarding the auto construction of static metaclass 'new' and the possibility of the user to implement custom factories.

That's right. If you want to have a static method named new that is not a constructor, that get's harder to specify. I think it's important to enable this because I want to fully encapsulate "factories" inside the class. A user shouldn't know whether a method they invoke on a class is a "real" constructor or some other kind of factory method.
 
In the end, while surprising at first, I think that Bob idea is better. By removing 'new' as a keyword, the 'new new' sentence is avoided (that sounds stupid to my ears) to make autoconstruct. While allowing the user to create factories transparently using something like:

class Foo {
  static new (args...) {
  }

  new (args...) {
  }
}

Just to make it clearer, in this proposal, it would be something like:

class Foo {
  // Looks like a constructor, but returns a cached instance.
  static new(name) {
    if (__cache == null) __cache = {}

    if (__cache.containsKey(name)) {
      return __cache[name]
    }

    return __cache[name] = Foo.realNew(name)
  }

  this realNew(name) {
    _name = name
  }
}

This would let you do Foo.new(name), which looks like a normal constructor call, but which is actually just calling a static method. If we make new a reserved word, we'd have to come up with some special way to enable this.

I rethought about it and it is needed to be able to write something like:

class Foo {
  static new (args...) { .... }
  new (args...) { /*what ever but probably: this.realNew(args...)*/ }
  this realNew (args...) { ... }
}

class Bar {
  this new (args...) {}
}

class Baz is (Time.isMonday ? Foo : Bar) {
  this new (args...) {
    super(args...)
  }
}

so that the parent can be easily replaced, as shown here.

But that said that doesn't mean that 'new Class(args)' can't be a syntax sugar for 'Class.new(args...)' (some other languages are doing that I think)


Bob Nystrom

unread,
Jul 15, 2015, 11:06:30 AM7/15/15
to wren-lang
On Sun, Jul 12, 2015 at 4:29 PM, Michel Hermier <michel....@gmail.com> wrote:
This seems like more complexity than the current implementation. What's the reason to motivate this?
Simplify the compiler, and make the instance part of the constructor a regular function. Seeing the argument 'isConstructor' passed in the compiler is making me say we are doing something bad here.

Ah, true. It does get a bit of special casing. But my feeling is that that's less extra code in the compiler than the transformation you suggest above would be, though I haven't tried it out to see.
 
Why would the constructor need to return 'this' it has to be a regular function. This would also the constructor to return a marker for its subclass:

class Foo {
   this new { __id = __id != null ? __id + 1 : 0 }
}

class Bar is Foo {
   this new {
     if (super == 0) {IO.print("I'm the root of the universe") }
   }
}

Assuming we don't add mixins (pdf), then inside a class, you always statically knows its superclass. That means you (the program author) know what super resolves to. It's statically bound. So there isn't currently any need to be able to dynamically detect if there is a superclass method or not. You can just know by looking at the code.
 


My hunch is that we don't want metaclass inheritance. From talking to a few old Smalltalkers, they generally consider it a mistake there. Also, Java, C++, C#, etc. have semantics more similar to not allowing metaclass inheritance (in other words, static methods aren't inherited), and it seems to work well there.
Well they might be a mistake, but it sounds logicall for some parts, else when writting stating methods and you need to call super static methods. Ohh wait in static 'this' is an in stance of Class, so we can use 'supertype', maybe it is enough to do the trick for most use case. I'm felling it was a great idea I introduced it ;)

Right now super() isn't allowed in static methods at all. I specifically made that a compile error. Though, after thinking about it recently, I realized that's probably not needed. Like you say, it should just resolve to calling a method on Class.


Instantiation is the common place where class needs to be remebered, and is generaly why metaclass inheritance is introduced I guess (so that super.new return an instance of the correct class). Not having them adds a little annoyance in that one as to rewrite 'static new' calls to use custom allocations strategies of parent class.
Ex1: Rethink of your example with a subclass of Foo that want to be in the cache.

Yeah, there are definitely a few things you lose by not being able to inherit and override metaclass methods. You end up having to reorganize your code a bit. So here, you'd do something like:

class Foo {
  // Looks like a constructor, but returns a cached instance.
  static new(name) { createIfUncached(name, Foo) }

  this realNew(name) {
    _name = name
  }

  static createIfUncached(name, clas) {
    if (__cache == null) __cache = {}

    if (__cache.containsKey(name)) {
      return __cache[name]
    }

    return __cache[name] = clas.realNew(name)
  }
}

class Bar is Foo {
  static new(name) { createIfUncached(name, Bar) }

  this realNew(name) {
    super(name)
  }
}

It's a little tedious, but I think it's worth it to avoid the more common case where you do not want static methods to be inherited.

 
Ex2: In a *real* usage, an abstract resource loader providing factory throught Ressource.new(path) and a dedicated ones ImageRessource.new(path), SoundRessource.new(path)... (I can explain more on this if you don't see what I mean).
I hope these are pretty rare to justify the needed extra code.

I think so too. You're right that there are cases where metaclass inheritance would be useful, but I think it's more common that you don't want it.
 
But I agree enabling metaclass inheritance might be worse because __vars become class vars and not real static ones, making more problems than it solves.

That too. It would mean unifying ObjClass and ObjInstance in the VM, which I think would add some complexity.
You're correct, this is gone now. I think that's probably a feature more than a bug. I don't know if it's useful to be able to construct instances of Object so I just disabled that for now. If we do find a use for that, it's easy to give it a constructor.

There is one pretty used, the unique object that act as a marker for something and that is not possibly null (like tombstone in our map implementation).

True, though you can always define your own specific class for those cases, like:

class Tombstone {}

We certainly can add support for Object.new() again, I just thought I'd leave it out for now to see if we miss it.

Cheers!

- bob

Bob Nystrom

unread,
Jul 20, 2015, 10:33:54 AM7/20/15
to wren-lang
I'm itching to merge this into master, but I want to make sure you folks are tolerably happy with it before I do.

The impression I get is that "this" as the keyword for indicating a constructor isn't working for people. I have a few alternatives in mind:

constructor

Similar to PHP. Very clear. But also painfully long for something almost every class will use.

construct

Clear. Shorter. I think it reads well with the following method name:

class Foo {
  construct new() { ... } // "construct new ..."
  construct fromBar() { ... } // "construct from bar ..."
}

ctor

Very short, which is nice, but cryptic to anyone not familiar with it.

new

We want to make this usable as a method name, so we'd have to do something like a contextual keyword. Those are a bit fishy. Also leads to weird code like:

class Foo {
  new new() { ... }
}

init

Short and fairly intuitive. But reserves "init" which is a pretty commonly used identifier.

initialize

Kind of long. Familiar to Ruby programmers.

Right now, I'm leaning towards "construct" or just sticking with "this". Any other feedback before I make a choice? Of course, even after this lands, we can always change it later.

Cheers!

- bob


Thorbjørn Lindeijer

unread,
Jul 20, 2015, 10:39:55 AM7/20/15
to wren...@googlegroups.com
My 2 cents:
 
I only suggested 'new' in the context of dropping the actual method name. Indeed it looks silly otherwise, and I understand now that dropping method names is not possible.
 
I agree "construct" reads nicely.
 
- Bjørn
--
You received this message because you are subscribed to the Google Groups "Wren" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wren-lang+...@googlegroups.com.
To post to this group, send email to wren...@googlegroups.com.

Sven Bergström

unread,
Jul 20, 2015, 10:43:29 AM7/20/15
to wren...@googlegroups.com
As an onlooker - of the proposed suggestions, “construct” looks cleanest, least invasive and least confusing of both intent and timing.

:)

Michel Hermier

unread,
Jul 20, 2015, 11:41:10 AM7/20/15
to wren-lang

construct looks good. But we didn't explored the non keyword way, what do you think about using an operartor, something like:
    *new() {}
    +new() {}
    @new() {}
or something else.

What do you think of this ?

Michel Hermier

unread,
Jul 20, 2015, 11:47:25 AM7/20/15
to wren-lang

Replying to myself or even:
   @construct new
So it does look like a decorator...

Bob Nystrom

unread,
Jul 21, 2015, 9:42:36 AM7/21/15
to wren-lang
On Mon, Jul 20, 2015 at 8:41 AM, Michel Hermier <michel....@gmail.com> wrote:

construct looks good. But we didn't explored the non keyword way, what do you think about using an operartor, something like:
    *new() {}
    +new() {}
    @new() {}
or something else.

What do you think of this ?

I didn't mention that here but I did toy with that too.

My belief is that punctuation needs to be used very carefully in a language. Unless you're using it in a way that's very well established (arithmetic, C's operators, etc.) it's completely unintuitive. Unlike a word that conveys it's own meaning in English, a punctuation character is just an opaque blob.

If you've never written a line of Wren code in your life, you can probably still roughly guess what this does:

class Person {
  construct new(name) {
    _name = name
  }
}

The "class" and "construct" go a long way. (Of course, the "_name = name" part is confusing.)

But this doesn't give you much intuition:

class Person {
  *new(name) {
    _name = name
  }
}

"Asterisk" or "star" don't really convey construction in any way. I do like that punctuation is terse and doesn't reserve a keyword, but I think in most cases, words are easier to learn and read.

Cheers!

- bob

Michel Hermier

unread,
Jul 21, 2015, 10:04:05 AM7/21/15
to wren...@googlegroups.com
You didn't commented about the decorator syntax I proposed after that, like:

class Foo {
  @construct new () { }
}

It really looks like an interesting way to do it while not *really* using a keyword while still beeing readable.
And that way if we introduce the decorators, it only makes it a special decorator (if we can't make it a real decorator).

Cheers,
  Michel
--
You received this message because you are subscribed to the Google Groups "Wren" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wren-lang+...@googlegroups.com.
To post to this group, send email to wren...@googlegroups.com.

Bob Nystrom

unread,
Jul 21, 2015, 10:19:34 AM7/21/15
to wren-lang
On Tue, Jul 21, 2015 at 7:04 AM, Michel Hermier <michel....@gmail.com> wrote:
You didn't commented about the decorator syntax I proposed after that, like:

class Foo {
  @construct new () { }
}

It really looks like an interesting way to do it while not *really* using a keyword while still beeing readable.
And that way if we introduce the decorators, it only makes it a special decorator (if we can't make it a real decorator).

It's definitely not a bad idea. Kotlin does something similar for annotations. Ceylon goes further and uses these annotations for things like shared (like static in Wren) and throws() clauses which are meaningful to the language.

However, I don't know if we need punctuation for that. Kotlin makes it optional in most cases, and Ceylon doesn't use any punctuation at all. I think it's possible to do decorators like you suggest without any leading @.

In many ways, construct is a method modifier just like static. So, if we did do @construct, we'd probably want @static too, but I think that would look too strange.

Cheers!

- bob

Bob Nystrom

unread,
Jul 21, 2015, 10:27:46 AM7/21/15
to wren-lang
Tiny update: This is merged into master now using "construct", but don't take that to mean I'm closing the discussion. We can always change things. :)

Cheers!

- bob

Michel Hermier

unread,
Jul 21, 2015, 10:29:45 AM7/21/15
to wren...@googlegroups.com
Well I though about that because of python syntax.

The only valid reason I see the '@' of an interest is if we allow decorators to takes arguments, to keep the compiler simple.
Else the compiler would get more complex requiring an AST to parse something like:

class Foo {
  construct(myCustomAllocator) {return null} new { /* Do something fancy*/ }
                                                 /* ^ In case of allocation failure return null */
}

I agree it is totally hypotetical but adding @ would greatly help disambiguishing the language so it stay LL1, if we allow this kind of decorator form.
--
You received this message because you are subscribed to the Google Groups "Wren" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wren-lang+...@googlegroups.com.
To post to this group, send email to wren...@googlegroups.com.

Michel Hermier

unread,
Jul 21, 2015, 10:45:20 AM7/21/15
to wren...@googlegroups.com
Even if we adopt the '@' (or what ever) we can allways make it a cheap exception in the compiler, so that 'construct' is interpreted as '@construct', so adopting it right now is *not* important.
I think the more important question is "do we want decorators? how implement them if feasable?"

Cheers,
  Michel
--
You received this message because you are subscribed to the Google Groups "Wren" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wren-lang+...@googlegroups.com.
To post to this group, send email to wren...@googlegroups.com.

Bob Nystrom

unread,
Aug 2, 2015, 1:51:45 PM8/2/15
to wren-lang
On Tue, Jul 21, 2015 at 7:45 AM, Michel Hermier <michel....@gmail.com> wrote:
Even if we adopt the '@' (or what ever) we can allways make it a cheap exception in the compiler, so that 'construct' is interpreted as '@construct', so adopting it right now is *not* important.
I think the more important question is "do we want decorators? how implement them if feasable?"

Right now at least, Wren doesn't have much in the way of metaprogramming, and I think of decorators as falling into that category.

I'm not opposed to metaprogramming in principle, but it's an area where I want to tread very carefully. It has deep implications on:
  • The VM's performance and complexity. Metaprogramming often requires keeping a lot of stuff around in memory that the compiler might otherwise be able to discard. It also means the VM can't assume as many things are immutable as it currently can.

  • How statically analyzable the language is. Wren isn't statically typed, but I would like to be able to perform static analysis on it, possibly even adding an optional type system similar to Dart or Erlang's dializer. Metaprogramming can very easily make that intractable.
Given those, my current "plan"—if you can call it that—is to put off metaprogramming features for as long as we can and see how much we need them.

Cheers!

- bob

Reply all
Reply to author
Forward
0 new messages