9 ms·
Constructors in Ceylon
- chriswarbo 11y agoClass hierarchies seem to me more and more like a shadow language[1]. I first realised this in Python, where: - Classes can be called like functions - Calling a class runs a function (the constructor, called __init__) - The constructor is implicitly passed a fresh object as an argument (usually called "self"; this is like an implicit use of "new") - The constructor implicitly returns an initialised object (a mutated "self") In a language with first-class functions, it's also pretty arbitrary to define "attributes" inside the constructor and "methods" outside, since methods are functions which are values (modulo the implicit binding stuff). Hence classes can be completely replaced by their constructors; or in other words, classes are a design pattern when writing functions. This is a realisation that's become mainstream due to Javascript, and that's the first thing I thought of when I saw the Ceylon code in this article. The details at the end, about execution order, partial constructors, inheritance, etc. all screamed "shadow language" to me: if you've already got functions(/methods), can't you just re-use them instead of defining a new, semantically distinct category? [1] http://gbracha.blogspot.co.uk/2014/09/a-domain-of-shadows.html http://gbracha.blogspot.co.uk/2014/09/a-domain-of-shadows.ht...
- couchand 11y agoIndeed, the Ceylon constructors even read like a regular old static factory method in any other OO language, another pointer to the functional underpinnings of objects.
- gavinking 11y agoTotally this!
- gavinking 11y agoSure. A class is certainly a kind of function: one which returns a closure over its own shared declarations. That's the way we conceptualize the notion of a class in Ceylon, and it's why the syntax for a class looks so much like the syntax for a function, and why there's no essential difference between a function defined inside a class (a "method") and a function declared toplevel, or inside another function, or whatever. Ditto for a value defined inside a class (an "attribute") and a value declared toplevel or inside another function. It's the same thing, as far as Ceylon is concerned. One of the design goals in Ceylon was to treat all these things uniformly, so that a class is, as much as possible, just like any other function. However, there is a big and surprisingly important difference between a class and a function that returns a record: with a class you get open recursion between members of the class. You don't get that with a function that assigns members to a record type - or, at least, if you do build the machinery you need to get that, you have essentially reinvented classes with a worse syntax. Open recursion seems like a small thing. But in fact, in practice, if you try taking it away from me, I will have to do some convoluted and nasty things to emulate it. Sure, there are plenty of simple classes where you can do without it, but there are surprisingly many classes where it's necessary, or at least very useful.
- vocal_bob 11y agoIt's not that's surprising. To support open recursion, a lot of functional languages consider differently top level functions and functions inside functions. That's why in JavaScript you have a murky global object with some weird scope rules that makes top level functions globally available.
- jules 11y agoWouldn't record literals with open recursion lead to a design with much less semantic duplication?
- gavinking 11y agoWell I guess it's not extremely clear to me how a "record literal with open recursion" isn't essentially the same thing as a "class".
- jules 11y agoA record literal is an expression that creates a value. A class would then be a function, as chriswarbo suggests.
- gavinking 11y agoI understand, but once you start taking into account other requirements, like the ability to functionally decompose construction of this record, you will wind up with something that looks painfully similar to a class in Ceylon.
- couchand 11y agoAs alluded above, the biggest problem with the constructor syntax in languages that borrow from C++ is that in the common case of a class with just one constructor, the parameters of that constructor aren't available in the body of the class, leading to awful code like the following: class Point { public float x; public float y; public Point(float x, float y) { this.x = x; this.y = y; } public String toString() { return "(" + x + ", " + y + ")"; } } This hurts. Fortunately, we've already made that pain go away in Ceylon. Well that seems like a laudable goal. Let's take a look at the example code, then, to see what the alternative is: class Color { shared Integer rgba; //default constructor shared new (Integer rgba) { assert (0 <= rgba <= #FFFFFFFF); this.rgba = rgba; } ... string => "Color { \ alpha=``hex(alpha)``, \ red=``hex(red)``, \ green=``hex(green)``, \ blue=``hex(blue)`` }"; } Maybe it's just me but it looks like the pain is still there?
- gavinking 11y agoNo, the alternative is this: class Point(shared Float x, shared Float y) { string => "(``x``, ``y``)"; } I think I made that really clear in the post. In the very example I started with, but perhaps I need to further clarify it somehow? UPDATE: FTR, I added the above example to my post, in the hope that it clarifies the point. Thanks for the feedback.
- couchand 11y agoThat seems to work until you want to add another constructor, or am I misreading the post?
- gavinking 11y agoRight, exactly. So there's two very distinct cases here: - the simple and overwhelmingly common case where there is only one way to instantiate a class, and therefore there is no reason to decouple the body of the class from the parameter list, and - the occasional but also important case where there are multiple ways to instantiate the class, each with different parameter list signatures, where we need to decouple the body of the class from the parameter list signatures. "Constructors" as supported in Java, C++, C#, and now also in Ceylon, are optimized for the "occasional" case. And I'm convinced that they're useful enough that we need them. But in the "overwhelmingly common" case they get in the way, and that's why they're not the usual thing you reach for first when writing a class in Ceylon.
- duaneb 11y agoI think the name of the constructor itself may have merit eventually, but is rather distracting from the function of the constructor itself. Much of the rest of the work seems to mirror what Scala did several years ago, and this seems to nearly replicate that. Considering the state of the scala codebase, this again might have merit, but they do not even mention it once. As such, I'm going to relegate this to the "branded language that will not survive outside advertised setting" category because it seems to prioritize positive comparisons over engineering use.
- gavinking 11y agoSo it seems to me that if you need multiple constructors then it's worth being able to give them names, in order to identify their purpose. I have never looked at how Scala handles constructors, beyond being dimly aware that Scala has them, so I don't see why I should be expected to mention Scala, when it had no influence at all on the design of this functionality. To be clear, I find it highly unlikely that Scala constructors are very similar to what I describe in this post, except perhaps on the most superficial level. I will take a look at Scala later today to confirm that, just in case I'm wrong. I'm not sure what argument is being made in the rest of your comment. Perhaps you could elaborate on what are your concerns?
- gavinking 11y agoYeah, OK, I just had a quick look, and Scala constructors seem to have almost nothing in common with the design I outline in this blog, other than being, well, constructors. Perhaps you didn't read the linked article?
- perneto 11y agoCould you outline the differences between Scala and Ceylon constructors, for readers not familiar with both?
- gavinking 11y agoYes of course. I'm at the gym right now, so give me a chance to get home, read up on Scala so I can be sure I'm not saying anything wrong, and then I'll reply. Is that OK?
- perneto 11y agoNow that there is an apparently superior alternative, do you plan to do something to eliminate the existing enum pattern explained at http://ceylon-lang.org/documentation/1.1/tour/types/#enumerated_instances http://ceylon-lang.org/documentation/1.1/tour/types/#enumera... ?
- gavinking 11y agoNo, because that pattern is still strictly more powerful. With enumerated classes I can have cases which are a class rather than just a singleton instance.
- kolev 11y agoCeylon is my favorite language and I can't wait for v1.2 to be out! I just wish it was catching up with JDK release a little faster and generally speed up the release cycle. Spread the word, it definitely needs more love!