Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement trait parameters (SIP) #640

Closed
odersky opened this issue Jun 12, 2015 · 5 comments
Closed

Implement trait parameters (SIP) #640

odersky opened this issue Jun 12, 2015 · 5 comments

Comments

@odersky
Copy link
Contributor

odersky commented Jun 12, 2015

We would like to allow parameters to traits. These replace early definitions, which are complicated and hard to get right. The syntax already allows this. Excerpting from current SyntaxSummary.txt (the one for Scala 2 is analogous):

TmplDef ::=  ([`case'] `class' | `trait') ClassDef 
ClassDef ::=  id [ClsTypeParamClause] [ConstrMods] ClsParamClauses TemplateOpt 
TemplateOpt ::=  [`extends' Template | [nl] TemplateBody]
Template ::=  ConstrApps [TemplateBody] | TemplateBody
ConstrApps ::=  ConstrApp {`with' ConstrApp}
ConstrApp  ::=  AnnotType {ArgumentExprs}  

Parent traits can now be introduced as a type or as a constructor which can take arguments. The order of initialization of traits is unaffected by parameter passing - as always, traits are initialized in linearization order. The following rules ensure that every parameterized trait is passed an argument list exactly when it is initialized:

  1. Only classes can pass arguments to parent traits. Traits themselves can pass arguments no neither classes nor traits.
  2. If a class C implements a parameterized trait T, and its superclass does not, then T must appear as a parent trait of C with arguments. By contrast, if the superclass of C also implements T, then C may not pass arguments to T.

For example, assume the declarations

trait T(x: A) 
trait U extends T

U may not pass arguments to T. On the other hand, a class implementing U must ensure that T obtains arguments for its parameters. So the following would be illegal:

class C extends U

We have to add the trait T as a direct parent of C. This can be done in one of two ways:

class C extends T(e) with U
class C extends U with T(e)

Both class definitions have the same linearization. T is in each case initialized before U since T is inherited by U.

The arguments to a trait are in each case evaluated immediately before the trait initializer is run (except for call-by-name arguments, which are always evaluated on demand).

This means that in the example above the expression e is evaluated before the initializer of either T or U is run. On the other hand, assuming the declarations

trait V(x2: B)
class D extends T(e1) with V(e2)

the evaluation order would be e1, initializer of T, e2, initializer of V.

@odersky odersky self-assigned this Jun 12, 2015
odersky added a commit to dotty-staging/dotty that referenced this issue Jun 12, 2015
Verify that the initilialization order described in scala#640 is correctly implemented.
odersky added a commit to dotty-staging/dotty that referenced this issue Jun 19, 2015
Verify that the initilialization order described in scala#640 is correctly implemented.
odersky added a commit to dotty-staging/dotty that referenced this issue Jun 19, 2015
Verify that the initilialization order described in scala#640 is correctly implemented.
@odersky odersky closed this as completed Jun 19, 2015
@SethTisue
Copy link
Member

Bread crumbs for future documenters:

  • It is notable that context bounds on traits work now (since trait parameters may be implicit), e.g. trait Foo[T : Ordering].
  • Users will be wondering what the remaining differences are between trait and abstract class, beyond the usual rule that you can only have one class parent.

@neonxray
Copy link

Current implementation can be circumvented by anonymous class:

trait A(val s: String) { println(s) }
trait B extends A { override val s = "B" }
new B {}
// It compiles and prints null here

Is it expected?

@bishabosha
Copy link
Member

@texasbruce looks like the override is actually the problem

@ByteEater-pl
Copy link

@SethTisue, to me it looks like the main difference is that traits can't have secondary constructors, cf. #14184.

@odersky
Copy link
Contributor Author

odersky commented Dec 29, 2021

Classes are the spine where everything else is initialized. You need a class, not a trait, to pass arguments to a super-trait.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants