DelayedInit should be syntax.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

DelayedInit should be syntax.

Kris Nuttycombe-3
Hi, all,

A couple of days ago, the news wound its way to me via Twitter about
this commit: https://lampsvn.epfl.ch/trac/scala/changeset/23794

Now, this looks like a wonderful thing and a terrible thing all
wrapped into one. The wonderful thing is that we now have a means to
specify that initialization of a trait can be delayed, and that as a
consequence that Application can be made to work nicely, and so on.

The terrible thing is that this is implemented by the compiler being
aware of a special "marker trait." I think this is awful, for a couple
of reasons.

First, and foremost, traits are in the user namespace. Having a
compiler change its behavior based upon something in the user
namespace seems like exactly the wrong way to do things - the compiler
should know about syntax, and should be able to (optionally or
pluggably) react to annotations. But it should *not* be looking for
some indication from the user namespace for its behavior! Java made
this mistake with java.io.Serializable, and Scala has rightfully
relegated this behavior to the domain of annotation processing. So,
why is Scala now about to make the same mistake with DelayedInit?

Second, with the possible exception of LowPriorityImplicits (which has
the same problem) this approach is, as far as I can tell, unique in
Scala. It's a special case that modifies how a a trait (and all
derived traits) are handled by the compiler, with significant
implications for order of operations in user code. Nothing else in the
trait/class namespace behaves in this fashion.

Thus, I would like to propose that the DelayedInit functionality
should instead be implemented by introducing either a new annotation
that can be applied to traits, or (preferably) new syntax. I suggest
"late trait" as a designator for traits that must be processed by the
compiler in this fashion.

.Thanks,

Kris Nuttycombe
dpp
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

dpp

+1

Very well reasoned!

On Dec 18, 2010 3:11 PM, "Kris Nuttycombe" <[hidden email]> wrote:
> Hi, all,
>
> A couple of days ago, the news wound its way to me via Twitter about
> this commit: https://lampsvn.epfl.ch/trac/scala/changeset/23794
>
> Now, this looks like a wonderful thing and a terrible thing all
> wrapped into one. The wonderful thing is that we now have a means to
> specify that initialization of a trait can be delayed, and that as a
> consequence that Application can be made to work nicely, and so on.
>
> The terrible thing is that this is implemented by the compiler being
> aware of a special "marker trait." I think this is awful, for a couple
> of reasons.
>
> First, and foremost, traits are in the user namespace. Having a
> compiler change its behavior based upon something in the user
> namespace seems like exactly the wrong way to do things - the compiler
> should know about syntax, and should be able to (optionally or
> pluggably) react to annotations. But it should *not* be looking for
> some indication from the user namespace for its behavior! Java made
> this mistake with java.io.Serializable, and Scala has rightfully
> relegated this behavior to the domain of annotation processing. So,
> why is Scala now about to make the same mistake with DelayedInit?
>
> Second, with the possible exception of LowPriorityImplicits (which has
> the same problem) this approach is, as far as I can tell, unique in
> Scala. It's a special case that modifies how a a trait (and all
> derived traits) are handled by the compiler, with significant
> implications for order of operations in user code. Nothing else in the
> trait/class namespace behaves in this fashion.
>
> Thus, I would like to propose that the DelayedInit functionality
> should instead be implemented by introducing either a new annotation
> that can be applied to traits, or (preferably) new syntax. I suggest
> "late trait" as a designator for traits that must be processed by the
> compiler in this fashion.
>
> .Thanks,
>
> Kris Nuttycombe
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Paul Phillips-3
In reply to this post by Kris Nuttycombe-3
On Sat, Dec 18, 2010 at 04:11:19PM -0700, Kris Nuttycombe wrote:
> Java made this mistake with java.io.Serializable, and Scala has
> rightfully relegated this behavior to the domain of annotation
> processing. So, why is Scala now about to make the same mistake with
> DelayedInit?

Here's another commit it seems you missed:

http://lampsvn.epfl.ch/trac/scala/changeset/23627
'Deprecated the @serializable annotation, introduce a new trait
"scala.Serializable" which has to be extended instead (cross-platform).'

--
Paul Phillips      | All men are frauds.  The only difference between
Imperfectionist    | them is that some admit it.  I myself deny it.
Empiricist         |     -- H. L. Mencken
up hill, pi pals!  |----------* http://www.improving.org/paulp/ *----------
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Paul Phillips-3
In reply to this post by Kris Nuttycombe-3
On Sat, Dec 18, 2010 at 04:11:19PM -0700, Kris Nuttycombe wrote:
> Second, with the possible exception of LowPriorityImplicits (which has
> the same problem) this approach is, as far as I can tell, unique in
> Scala. It's a special case that modifies how a a trait (and all
> derived traits) are handled by the compiler, with significant
> implications for order of operations in user code. Nothing else in the
> trait/class namespace behaves in this fashion.

...including LowPriorityImplicits.  That's only handled specially for
bootstrapping reasons which I'm fairly sure you don't mean.  The "low
priority" part is uniform behavior: implicits defined in a subclass have
higher priority than those in a superclass.  It only exists to be a
superclass of Predef and therefore lower priority.

--
Paul Phillips      | If this is raisin, make toast with it.
Everyman           |
Empiricist         |
ha! spill, pupil   |----------* http://www.improving.org/paulp/ *----------
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Paul Phillips-3
In reply to this post by Kris Nuttycombe-3
On Sat, Dec 18, 2010 at 04:11:19PM -0700, Kris Nuttycombe wrote:
> Nothing else in the trait/class namespace behaves in this fashion.

Another one, besides Serializable and DelayedInit, is Singleton.

--
Paul Phillips      | Atheists dig the best foxholes.
Imperfectionist    |
Empiricist         |
slap pi uphill!    |----------* http://www.improving.org/paulp/ *----------
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Paul Phillips-3
In reply to this post by Kris Nuttycombe-3
Oh, and NotNull.

--
Paul Phillips      | Simplicity and elegance are unpopular because
Protagonist        | they require hard work and discipline to achieve
Empiricist         | and education to be appreciated.
i'll ship a pulp   |     -- Dijkstra
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kevin Wright-3
In reply to this post by Kris Nuttycombe-3


On 18 Dec 2010 23:30, "Paul Phillips" <[hidden email]> wrote:
>
> Oh, and NotNull.
>

How about Throwable & sons? Do they also count as having special compiler awareness?

> --
> Paul Phillips      | Simplicity and elegance are unpopular because
> Protagonist        | they require hard work and discipline to achieve
> Empiricist         | and education to be appreciated.
> i'll ship a pulp   |     -- Dijkstra

Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Paul Phillips-3
On Sat, Dec 18, 2010 at 11:41:12PM +0000, Kevin Wright wrote:
> How about Throwable & sons? Do they also count as having special
> compiler awareness?

Well, it isn't a trait, and the special handling is dictated by the JVM.
I'll say no.

I'm not sure where the boundaries of "special compiler awareness" stop,
but there are quite a few more which broadly meet this test: FunctionN,
PartialFunction, ProductN for starters.

--
Paul Phillips      | It is hard to believe that a man is
Caged Spirit       | telling the truth when you know that you
Empiricist         | would lie if you were in his place.
i pull his palp!   |     -- H. L. Mencken
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
Well, I see FunctionN, PartialFunction and ProductN as significantly
different, because these are traits that are applied to classes
generated by the compiler in response to syntax. The special handling
of apply(), unapply(), update() and x_= perhaps do fall into a similar
category, although the compiler doesn't alter the semantics of methods
with those names; it simply provides additional syntactic sugar that
can be used to interact with methods bearing those names, so again I
think it's a different case.

NotNull and Singleton (and Serializable, bleah) as traits... well, I
guess I think that those are mistakes too. It just seems misguided to
me to have a compiler be aware of special values in the user namespace
to begin with. In each of these cases, if these are concerns that the
compiler needs to be able to take into account, then I think syntax or
annotations are the way to go - syntax if the behavior must be rigidly
defined (such as with DelayedInit), annotations for things like
Serializable where one might want to apply pluggable semantics.
NotNull seems like it would probably fall on the syntax side of the
line, though there's a reasonable argument that could probably be made
for an annotation. Singleton I don't know well enough to comment on.

Kris

Apologies for my incorrect statement about LowPriorityImplicits - I
was parroting something somebody on Twitter had mentioned without
thinking too much about it.

On Sat, Dec 18, 2010 at 5:49 PM, Paul Phillips <[hidden email]> wrote:

> On Sat, Dec 18, 2010 at 11:41:12PM +0000, Kevin Wright wrote:
>> How about Throwable & sons? Do they also count as having special
>> compiler awareness?
>
> Well, it isn't a trait, and the special handling is dictated by the JVM.
> I'll say no.
>
> I'm not sure where the boundaries of "special compiler awareness" stop,
> but there are quite a few more which broadly meet this test: FunctionN,
> PartialFunction, ProductN for starters.
>
> --
> Paul Phillips      | It is hard to believe that a man is
> Caged Spirit       | telling the truth when you know that you
> Empiricist         | would lie if you were in his place.
> i pull his palp!   |     -- H. L. Mencken
>
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Martin Odersky
As others have argued it is not so uncommon to attach special behavior to classes inheriting from traits. Serializable is an example that's even inherited from Java. Singleton, NotNull are other examples. I think DelayedInit is pretty consistent with that tradition. Here's how I would specify it:

The initialization code of a template that follows the superclass
constructor invocation and the mixin-evaluation of the template's base
classes is passed to a special hook, which is inaccessible from user
code. Normally, that hook simply executes the code that is passed to
it. But templates inheriting the `scala.DelayedInit` trait
can override the hook by re-implementing the `delayedInit`
method.

Why do we need traits and syntax does not work? The problem is that
we need a change in the end user code that inherits from a given framework.

DelayedInit
   |
Application
   |
MyProgram

The fact that Application extends DelayedInit means that MyProgram
gets its initializer run in the main method. If we chose an annotation-based version we'd have to write:

@delayedInit object MyProgram extends Application

That's pretty yucky, and it also would not help the 10's of thousands Application objects that have already been written. Alternatively, attach special code generation magic to an @application annotation.

@application object MyProgram

However, I challenge everyone to come up with a definition of what @application does that is as concise as the delayed init rule I have given above. Also, it would again inroduce a new idiom for applications. And, finally, there are other applications of DelayedInit independent of Application. For instance, I really think Actors should be written like this:

  new Actor {
    <setup code>
    react {
       case P1 => A1
       ...
       case Pn => An
     }
  }

The problem here is that the <setup code> should already run in a forked thread, so it cannot be part of the regular initialization code of the Actor.

Cheers

 -- Martin

Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
On Sun, Dec 19, 2010 at 1:57 AM, martin odersky <[hidden email]> wrote:

> As others have argued it is not so uncommon to attach special behavior to
> classes inheriting from traits. Serializable is an example that's even
> inherited from Java. Singleton, NotNull are other examples. I think
> DelayedInit is pretty consistent with that tradition. Here's how I would
> specify it:
>
> The initialization code of a template that follows the superclass
> constructor invocation and the mixin-evaluation of the template's base
> classes is passed to a special hook, which is inaccessible from user
> code. Normally, that hook simply executes the code that is passed to
> it. But templates inheriting the `scala.DelayedInit` trait
> can override the hook by re-implementing the `delayedInit`
> method.
>
> Why do we need traits and syntax does not work? The problem is that
> we need a change in the end user code that inherits from a given framework.
>
> DelayedInit
>    |
> Application
>    |
> MyProgram
>
> The fact that Application extends DelayedInit means that MyProgram
> gets its initializer run in the main method. If we chose an annotation-based
> version we'd have to write:
>
> @delayedInit object MyProgram extends Application

Here's how I conceive of it:

late trait Application {...}

object MyProgram extends Application {
}

The semantics are the same as for DelayedInit; that is,
late-initialization becomes a heritable feature of the trait itself.
However, you wouldn't have the confusing distinction that inheriting
some traits causes the *compiler* to behave differently than if you
inherited other traits that are superficially the same. I guess it
violates my sense of least surprise that extending a specific trait in
user code causes the compiler to order operations differently than it
does for every other trait.

If I declare a class, Foo extends Bar with Baz, I don't expect the
compiler to do something different than when I declare a class Foo
extends Bar with Baz with DelayedInit. Things that are in the same
namespace should behave the same way, but here they do not.

Kris

> That's pretty yucky, and it also would not help the 10's of thousands
> Application objects that have already been written. Alternatively, attach
> special code generation magic to an @application annotation.
>
> @application object MyProgram
>
> However, I challenge everyone to come up with a definition of what
> @application does that is as concise as the delayed init rule I have given
> above. Also, it would again inroduce a new idiom for applications. And,
> finally, there are other applications of DelayedInit independent of
> Application. For instance, I really think Actors should be written like
> this:
>
>   new Actor {
>     <setup code>
>     react {
>        case P1 => A1
>        ...
>        case Pn => An
>      }
>   }
>
> The problem here is that the <setup code> should already run in a forked
> thread, so it cannot be part of the regular initialization code of the
> Actor.
>
> Cheers
>
>  -- Martin
>
>
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
In reply to this post by Kris Nuttycombe-3
On Sun, Dec 19, 2010 at 12:12 AM, Archontophoenix Quar
<[hidden email]> wrote:

> While you're enumerating types the compiler is aware of, don't forget the
> primitive types. Hard to get very far without those.
>
> And I'm not sure quite where the issue with things that affect the behavior
> of the compiler comes from. The visibility of annotations works the same as
> visibility of types, no?
>
> It seems to me that types are generally superior to annotations for
> requesting special effects from the compiler, because:
>
> . In a language like Scala with a clearly specified rules for inheritance
> and linearization of inherited types, the behavior of type composition is
> clear. I've never seen any written rules regarding the heritability or
> composability of annotations, which creeps me out whenever I use them.
>
> . There are, as you mention, already a number of types with special meaning
> to the compiler. Having a second orthogonal mechanism to achieve similar
> ends is redundant as well as confusing.
>
> I haven't seen the arguments that favor annotations over types for special
> effects, so I'd be grateful if you could point me to them, so that I can
> understand what I've missed. Also, if you've found anything that says
> annotations are better-behaved than I've understood so far, I'm willing to
> change my thinking on this.

The reason that one might use annotations instead of types is that
annotation processing is something I see as potentially pluggable; the
job of the compiler with respect to annotations is to reify the
information that that annotations specify so that an annotation
processor can then modify the generated classfiles, or perhaps the
behavior of the runtime system in accordance with the reified
information.

Is initialization order really a feature of something's type? I think
of type as defining contracts for substitutability and an ordered
relation of specificity, not order of operations. Now it does all
three.

Kris

> A
>
> On Sun, Dec 19, 2010 at 6:32 AM, Kris Nuttycombe <[hidden email]>
> wrote:
>>
>> Well, I see FunctionN, PartialFunction and ProductN as significantly
>> different, because these are traits that are applied to classes
>> generated by the compiler in response to syntax. The special handling
>> of apply(), unapply(), update() and x_= perhaps do fall into a similar
>> category, although the compiler doesn't alter the semantics of methods
>> with those names; it simply provides additional syntactic sugar that
>> can be used to interact with methods bearing those names, so again I
>> think it's a different case.
>>
>> NotNull and Singleton (and Serializable, bleah) as traits... well, I
>> guess I think that those are mistakes too. It just seems misguided to
>> me to have a compiler be aware of special values in the user namespace
>> to begin with. In each of these cases, if these are concerns that the
>> compiler needs to be able to take into account, then I think syntax or
>> annotations are the way to go - syntax if the behavior must be rigidly
>> defined (such as with DelayedInit), annotations for things like
>> Serializable where one might want to apply pluggable semantics.
>> NotNull seems like it would probably fall on the syntax side of the
>> line, though there's a reasonable argument that could probably be made
>> for an annotation. Singleton I don't know well enough to comment on.
>>
>> Kris
>>
>> Apologies for my incorrect statement about LowPriorityImplicits - I
>> was parroting something somebody on Twitter had mentioned without
>> thinking too much about it.
>>
>> On Sat, Dec 18, 2010 at 5:49 PM, Paul Phillips <[hidden email]>
>> wrote:
>> > On Sat, Dec 18, 2010 at 11:41:12PM +0000, Kevin Wright wrote:
>> >> How about Throwable & sons? Do they also count as having special
>> >> compiler awareness?
>> >
>> > Well, it isn't a trait, and the special handling is dictated by the JVM.
>> > I'll say no.
>> >
>> > I'm not sure where the boundaries of "special compiler awareness" stop,
>> > but there are quite a few more which broadly meet this test: FunctionN,
>> > PartialFunction, ProductN for starters.
>> >
>> > --
>> > Paul Phillips      | It is hard to believe that a man is
>> > Caged Spirit       | telling the truth when you know that you
>> > Empiricist         | would lie if you were in his place.
>> > i pull his palp!   |     -- H. L. Mencken
>> >
>
>
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Naftoli Gugenheim
In reply to this post by Kris Nuttycombe-3
Could you elaborate on your reasoning? I'll indicate what I'm missing below.

On Sat, Dec 18, 2010 at 6:11 PM, Kris Nuttycombe <[hidden email]> wrote:
(1. Introduction)
Hi, all,

A couple of days ago, the news wound its way to me via Twitter about
this commit: https://lampsvn.epfl.ch/trac/scala/changeset/23794

(2. The good) 
Now, this looks like a wonderful thing and a terrible thing all
wrapped into one. The wonderful thing is that we now have a means to
specify that initialization of a trait can be delayed, and that as a
consequence that Application can be made to work nicely, and so on.

(3. Statement that marker traits are bad)
The terrible thing is that this is implemented by the compiler being
aware of a special "marker trait." I think this is awful, for a couple
of reasons.

(4. First argument how they are bad) 
(a)
First, and foremost, traits are in the user namespace.
(b) 
Having a compiler change its behavior based upon something in the user
namespace seems like exactly the wrong way to do things
(For what reason??)
(c)
- the compiler should know about syntax, and should be able to (optionally or
pluggably) react to annotations.
(d) 
But it should *not* be looking for some indication from the user namespace for its behavior!
(e) 
Java made this mistake with java.io.Serializable, and Scala has rightfully
relegated this behavior to the domain of annotation processing.
(f) 
So, why is Scala now about to make the same mistake with DelayedInit?
(So, step 3 basically seems to amount to "it seems like the wrong thing to do and should not be doing it" without saying why. Can you explain why it's wrong for the compiler behavior to be affected by user space? Is it just a vague feeling? Or?)

(4. Observation that this is somewhat of a change in direction)
Second, with the possible exception of LowPriorityImplicits (which has
the same problem) this approach is, as far as I can tell, unique in
Scala. It's a special case that modifies how a a trait (and all
derived traits) are handled by the compiler, with significant
implications for order of operations in user code. Nothing else in the
trait/class namespace behaves in this fashion.

(5. Alternative suggestion) 
Thus, I would like to propose that the DelayedInit functionality
should instead be implemented by introducing either a new annotation
that can be applied to traits, or (preferably) new syntax. I suggest
"late trait" as a designator for traits that must be processed by the
compiler in this fashion.

.Thanks,

Kris Nuttycombe

Please don't take this the wrong way. Personally I lean towards your conclusion. But conclusions need to be based on logical reasons. I'm sure you have plenty, but due to how obvious it is to you, you neglected to express them clearly enough. :)

Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Jon Pretty-2
Hi Naftoli, Kris,

Naftoli Gugenheim wrote:
> (So, step 3 basically seems to amount to "it seems like the wrong thing
> to do and should not be doing it" without saying why. Can you explain
> why it's wrong for the compiler behavior to be affected by user space?
> Is it just a vague feeling? Or?)

I think the reason this vague feeling of wrongness exists is that users have a model in
their heads for traits and how they work.  When you're learning a language, you learn a
model.  Maybe for traits you learn what it means to see a trait mixed in to a class, and you
learn that you can look at the source for a trait, and you know what it does.

This is a nice, simple and - of course - naive view of the world.  The exceptional traits
mentioned in this thread are sufficiently few that you could go a long way without ever
encountering them in anger, so you could live a long time without the simple trait model in
your head ever being challenged.

If there were many more occasions where the compiler gives special treatment to certain
traits, the user's model of how traits work might be different, more readily accommodating
special compiler interactions.

At the same time, annotations do indicate some special compiler interaction, more often than
not.  Thus, they might seem like a more natural solution in this case.

Having said all that, I don't feel strongly one way or the other, but I do think discussing
it is valuable.

Cheers,
Jon

--
Jon Pretty
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
In reply to this post by Naftoli Gugenheim
On Sun, Dec 19, 2010 at 4:11 PM, Jon Pretty <[hidden email]> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Hi Naftoli, Kris,
>
> Naftoli Gugenheim wrote:
>> (So, step 3 basically seems to amount to "it seems like the wrong thing
>> to do and should not be doing it" without saying why. Can you explain
>> why it's wrong for the compiler behavior to be affected by user space?
>> Is it just a vague feeling? Or?)
>
> I think the reason this vague feeling of wrongness exists is that users have a model in
> their heads for traits and how they work.  When you're learning a language, you learn a
> model.  Maybe for traits you learn what it means to see a trait mixed in to a class, and you
> learn that you can look at the source for a trait, and you know what it does.
>
> This is a nice, simple and - of course - naive view of the world.  The exceptional traits
> mentioned in this thread are sufficiently few that you could go a long way without ever
> encountering them in anger, so you could live a long time without the simple trait model in
> your head ever being challenged.
>
> If there were many more occasions where the compiler gives special treatment to certain
> traits, the user's model of how traits work might be different, more readily accommodating
> special compiler interactions.
>
> At the same time, annotations do indicate some special compiler interaction, more often than
> not.  Thus, they might seem like a more natural solution in this case.
>
> Having said all that, I don't feel strongly one way or the other, but I do think discussing
> it is valuable.

Thanks, Jon, you've stated eloquently why this feels bad to me as I
was struggling to come up with a sufficient response to Naftoli's
question. This feels bad to me because it increases complexity (not
all traits behave the same way when extended) in a way that seems
avoidable. I worry that the choice to implement this functionality as
a magic trait is a shortcut to avoid having to add a (contextual)
keyword to the language and having to work it into the parser and the
AST and so on, at the cost of additional complexity in the mental
model that users have of traits. I really believe that Scala is,
contrary to the voices of its detractors, an extremely simple and
consistent language and I worry that inconsistencies such as this
start to give merit to their claims that it is too complex.

Consistency is the essential thing; if case classes were defined by
writing "class Foo(bar: Bar, baz: Baz) extends CaseClass" then I'm
sure I'd never have blinked at DelayedInit. But case classes are
defined with special syntax; from my perspective, late initialized
traits should be as well.

Kris
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
In reply to this post by Naftoli Gugenheim
On Sun, Dec 19, 2010 at 4:11 PM, Jon Pretty <[hidden email]> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Hi Naftoli, Kris,
>
> Naftoli Gugenheim wrote:
>> (So, step 3 basically seems to amount to "it seems like the wrong thing
>> to do and should not be doing it" without saying why. Can you explain
>> why it's wrong for the compiler behavior to be affected by user space?
>> Is it just a vague feeling? Or?)
>
> I think the reason this vague feeling of wrongness exists is that users have a model in
> their heads for traits and how they work.  When you're learning a language, you learn a
> model.  Maybe for traits you learn what it means to see a trait mixed in to a class, and you
> learn that you can look at the source for a trait, and you know what it does.
>
> This is a nice, simple and - of course - naive view of the world.  The exceptional traits
> mentioned in this thread are sufficiently few that you could go a long way without ever
> encountering them in anger, so you could live a long time without the simple trait model in
> your head ever being challenged.

Oh, and one more note - Jon is quite right about going a long way
without ever encountering the exceptional traits - I've been using
Scala professionally, at three different companies, for almost three
years and had never encountered it, but have certainly encountered
issues relating to trait member initialization order!

Kris
Reply | Threaded
Open this post in threaded view
|

Re: changing semantics should be explicit

Roland Kuhn-2
In reply to this post by Kris Nuttycombe-3
Am 20.12.2010 00:30, schrieb Kris Nuttycombe:

> On Sun, Dec 19, 2010 at 4:11 PM, Jon Pretty<[hidden email]>  wrote:
>>
>> At the same time, annotations do indicate some special compiler interaction, more often than
>> not.  Thus, they might seem like a more natural solution in this case.
>>
>> Having said all that, I don't feel strongly one way or the other, but I do think discussing
>> it is valuable.
> Thanks, Jon, you've stated eloquently why this feels bad to me as I
> was struggling to come up with a sufficient response to Naftoli's
> question. This feels bad to me because it increases complexity (not
> all traits behave the same way when extended) in a way that seems
> avoidable. I worry that the choice to implement this functionality as
> a magic trait is a shortcut to avoid having to add a (contextual)
> keyword to the language and having to work it into the parser and the
> AST and so on, at the cost of additional complexity in the mental
> model that users have of traits. I really believe that Scala is,
> contrary to the voices of its detractors, an extremely simple and
> consistent language and I worry that inconsistencies such as this
> start to give merit to their claims that it is too complex.
>
> Consistency is the essential thing; if case classes were defined by
> writing "class Foo(bar: Bar, baz: Baz) extends CaseClass" then I'm
> sure I'd never have blinked at DelayedInit. But case classes are
> defined with special syntax; from my perspective, late initialized
> traits should be as well.
One interesting point I want to highlight: Martin's change introduces a
change in semantics for existing code, which is not visible anywhere in
client code. Whether the internal mechanism be a marker trait or special
syntax, the user will be faced with some special behavior for just that
special trait. Unless I am mistaken, your suggestions for change all
entail inheritance of the "specialness", so in the end it does not
matter: inheriting from Application changes constructor semantics.

In my very humble opinion (not having designed any of the live languages
out there) it would be much better to make this semantic change explicit
in the affected code.

Regards,

Roland
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Grzegorz Balcerek
In reply to this post by Kris Nuttycombe-3
From: "Kris Nuttycombe" <[hidden email]>
> that can be applied to traits, or (preferably) new syntax. I suggest
> "late trait" as a designator for traits that must be processed by the
> compiler in this fashion.

Did you consider "lazy" instead of "late" ?
"lazy" is an existing keyword with the meaning already related to the
initialization order.

Regards,
Grzegorz Balcerek

Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Daniel Sobral
This is also something which crossed my mind. We can prefix a constructor with "private" (or any other visibility scope), so I wonder if it wouldn't be best to have constructors prefixed with "lazy" to indicate delayed initialization.

But, then again, there's a semantic question here... Objects have lazy initialization already -- they are only initialized before first use. What is being spoken of here is something altogether different: the constructor is _not_ run to initialize the instance, instead, it is passed by-name to a method in it.

On Mon, Dec 20, 2010 at 11:30, Grzegorz Balcerek <[hidden email]> wrote:
From: "Kris Nuttycombe" <[hidden email]>

that can be applied to traits, or (preferably) new syntax. I suggest
"late trait" as a designator for traits that must be processed by the
compiler in this fashion.

Did you consider "lazy" instead of "late" ?
"lazy" is an existing keyword with the meaning already related to the initialization order.

Regards,
Grzegorz Balcerek




--
Daniel C. Sobral

I travel to the future all the time.
Reply | Threaded
Open this post in threaded view
|

Re: DelayedInit should be syntax.

Kris Nuttycombe-3
On Mon, Dec 20, 2010 at 10:27 AM, Daniel Sobral <[hidden email]> wrote:

> This is also something which crossed my mind. We can prefix a constructor
> with "private" (or any other visibility scope), so I wonder if it wouldn't
> be best to have constructors prefixed with "lazy" to indicate delayed
> initialization.
>
> But, then again, there's a semantic question here... Objects have lazy
> initialization already -- they are only initialized before first use. What
> is being spoken of here is something altogether different: the constructor
> is _not_ run to initialize the instance, instead, it is passed by-name to a
> method in it.

I agree; what we're talking about here is something entirely different
from object construction, although the syntax appears the same.
Perhaps that's a separate consistency issue?

This makes me think - if statements in the body of a class or trait
extending DelayedInit define vars or vals on the instance, the
assignment to those variables must occur after construction. If a
superclass of the DelayedInit class does not itself derive from
DelayedInit, and calls a method that is overridden in the subclass and
which references variables defined in the delayed constructor of the
subclass, I suppose that there's a possibility to get NPEs. That isn't
much different than the current situation though, except in reverse,
so it's probably not a big deal. But it does, for the first time, give
us a means whereby vals are not immediately assigned, with the
possibility that one could get the same semantics as exist in Java for
variables declared final without an assignment that then have their
values assigned in the constructor.

Kris