This page is no longer maintained — Please continue to the home page at www.scala-lang.org

lazy vals in Definitions

3 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

As we all know, it's a whole lot nicer when the symbols in Definitions
are defined with lazy vals rather than defs because they can be used in
pattern matching like:

case TypeRef(_, ArraySymbol, arg :: Nil) => ...

On the other hand there is some subset of the symbols therein which
cannot be changed in this way lest cycles develop. And I'm a little
mystified as to how to a) identify that subset and b) be sure that
changing any given one doesn't cause some hidden issue.

For instance: as an experiment just now I tried changing Array_apply,
Array_update, and Array_length to lazy vals. From the standpoint of the
compiler everything seems fine. Two tests fail, and I'd have been hard
pressed to guess it'd be the two tests below. Both fail with "cyclic
reference involving class Array" and all I see in common is a structural
type and a method call to it. If those two tests didn't exist the
change would have appeared safe (thus my reduced confidence in any such
change.)

I believe there is a past incident of my changing something along these
lines and breakage materializing: actually I only know about it by
inference from meeting notes, nobody told me directly. But assuming it
really happened I'd certainly like to avoid repeating it. Here are the
failing tests and the stack trace.

// t0586.scala
object RClose {
type ReflectCloseable = { def close(): Unit }
def withReflectCloseable[T <: ReflectCloseable, R](s: T)(action: T => R): R =
try {
action(s)
} finally {
s.close()
}
}

// t1131.scala
trait A { self: Any { def p: Any } =>
def f(b: => Unit) {}
f { p }
}

// scala.tools.nsc.symtab.Symbols$CyclicReference: illegal cyclic reference involving class Array
// at scala.tools.nsc.symtab.Symbols$TypeSymbol.tpe(Symbols.scala:1786)
// at scala.tools.nsc.symtab.BaseTypeSeqs$class.compoundBaseTypeSeq(BaseTypeSeqs.scala:189)
// at scala.tools.nsc.symtab.SymbolTable.compoundBaseTypeSeq(SymbolTable.scala:13)
// at scala.tools.nsc.symtab.Types$CompoundType.baseTypeSeq(Types.scala:1297)
// at scala.tools.nsc.symtab.Types$PolyType.baseTypeSeq(Types.scala:2079)
// at scala.tools.nsc.symtab.Symbols$Symbol.baseTypeSeqLength$1(Symbols.scala:962)
// at scala.tools.nsc.symtab.Symbols$Symbol.isLess(Symbols.scala:965)
// at scala.tools.nsc.symtab.Types$Type.baseTypeIndex(Types.scala:776)
// at scala.tools.nsc.symtab.Symbols$Symbol.isNonBottomSubClass(Symbols.scala:981)
// at scala.tools.nsc.symtab.Types$AsSeenFromMap.toPrefix$1(Types.scala:3335)

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: lazy vals in Definitions
Yes, initialization order is pretty tricky. My advice on Definitions would be: Don't try to change existsing definitions to lazy vals, because somebody (I think Iulian) already went over it, which means that there's most likely some reason why some definition is not a lazy val. On the other hand, every new thing that goes into Definitions should be a lazy val.

Cheers

 -- Martin


On Tue, Nov 2, 2010 at 7:41 PM, Paul Phillips <paulp@improving.org> wrote:
[SUMMARY: lazy vals in Definitions.scala! When? Why? Why Not?]

As we all know, it's a whole lot nicer when the symbols in Definitions
are defined with lazy vals rather than defs because they can be used in
pattern matching like:

 case TypeRef(_, ArraySymbol, arg :: Nil) => ...

On the other hand there is some subset of the symbols therein which
cannot be changed in this way lest cycles develop.  And I'm a little
mystified as to how to a) identify that subset and b) be sure that
changing any given one doesn't cause some hidden issue.

For instance: as an experiment just now I tried changing Array_apply,
Array_upd: Donte, and Array_length to lazy vals.  From the standpoint of the
compiler everything seems fine.  Two tests fail, and I'd have been hard
pressed to guess it'd be the two tests below.  Both fail with "cyclic
reference involving class Array" and all I see in common is a structural
type and a method call to it.  If those two tests didn't exist the
change would have appeared safe (thus my reduced confidence in any such
change.)

I believe there is a past incident of my changing something along these
lines and breakage materializing: actually I only know about it by
inference from meeting notes, nobody told me directly.  But assuming it
really happened I'd certainly like to avoid repeating it.  Here are the
failing tests and the stack trace.

// t0586.scala
object RClose {
 type ReflectCloseable = { def close(): Unit }
 def withReflectCloseable[T <: ReflectCloseable, R](s: T)(action: T => R): R =
   try {
     action(s)
   } finally {
     s.close()
   }
}

// t1131.scala
trait A { self: Any { def p: Any } =>
 def f(b: => Unit) {}
 f { p }
}

// scala.tools.nsc.symtab.Symbols$CyclicReference: illegal cyclic reference involving class Array
//  at scala.tools.nsc.symtab.Symbols$TypeSymbol.tpe(Symbols.scala:1786)
//  at scala.tools.nsc.symtab.BaseTypeSeqs$class.compoundBaseTypeSeq(BaseTypeSeqs.scala:189)
//  at scala.tools.nsc.symtab.SymbolTable.compoundBaseTypeSeq(SymbolTable.scala:13)
//  at scala.tools.nsc.symtab.Types$CompoundType.baseTypeSeq(Types.scala:1297)
//  at scala.tools.nsc.symtab.Types$PolyType.baseTypeSeq(Types.scala:2079)
//  at scala.tools.nsc.symtab.Symbols$Symbol.baseTypeSeqLength$1(Symbols.scala:962)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isLess(Symbols.scala:965)
//  at scala.tools.nsc.symtab.Types$Type.baseTypeIndex(Types.scala:776)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isNonBottomSubClass(Symbols.scala:981)
//  at scala.tools.nsc.symtab.Types$AsSeenFromMap.toPrefix$1(Types.scala:3335)

--
Paul Phillips      | Where there's smoke, there's mirrors!
Vivid              |
Empiricist         |
i'll ship a pulp   |----------* http://www.improving.org/paulp/ *----------

Iulian Dragos 2
Joined: 2009-02-10,
User offline. Last seen 42 years 45 weeks ago.
Re: lazy vals in Definitions


On Wed, Nov 3, 2010 at 11:22 AM, martin odersky <martin.odersky@epfl.ch> wrote:
Yes, initialization order is pretty tricky. My advice on Definitions would be: Don't try to change existsing definitions to lazy vals, because somebody (I think Iulian) already went over it, which means that there's most likely some reason why some definition is not a lazy val. On the other hand, every new thing that goes into Definitions should be a lazy val.

Indeed, when lazy values were added to Scala I tried to change all definitions on Definitions to be lazy. Unfortunately, that was impossible for some of them. I don't remember exactly what were the issues, it's been a few years ago.   

Cheers

 -- Martin


On Tue, Nov 2, 2010 at 7:41 PM, Paul Phillips <paulp@improving.org> wrote:
[SUMMARY: lazy vals in Definitions.scala! When? Why? Why Not?]

As we all know, it's a whole lot nicer when the symbols in Definitions
are defined with lazy vals rather than defs because they can be used in
pattern matching like:

 case TypeRef(_, ArraySymbol, arg :: Nil) => ...

On the other hand there is some subset of the symbols therein which
cannot be changed in this way lest cycles develop.  And I'm a little
mystified as to how to a) identify that subset and b) be sure that
changing any given one doesn't cause some hidden issue.

For instance: as an experiment just now I tried changing Array_apply,
Array_upd: Donte, and Array_length to lazy vals.  From the standpoint of the
compiler everything seems fine.  Two tests fail, and I'd have been hard
pressed to guess it'd be the two tests below.  Both fail with "cyclic
reference involving class Array" and all I see in common is a structural
type and a method call to it.  If those two tests didn't exist the
change would have appeared safe (thus my reduced confidence in any such
change.)

I believe there is a past incident of my changing something along these
lines and breakage materializing: actually I only know about it by
inference from meeting notes, nobody told me directly.  But assuming it
really happened I'd certainly like to avoid repeating it.  Here are the
failing tests and the stack trace.

// t0586.scala
object RClose {
 type ReflectCloseable = { def close(): Unit }
 def withReflectCloseable[T <: ReflectCloseable, R](s: T)(action: T => R): R =
   try {
     action(s)
   } finally {
     s.close()
   }
}

// t1131.scala
trait A { self: Any { def p: Any } =>
 def f(b: => Unit) {}
 f { p }
}

// scala.tools.nsc.symtab.Symbols$CyclicReference: illegal cyclic reference involving class Array
//  at scala.tools.nsc.symtab.Symbols$TypeSymbol.tpe(Symbols.scala:1786)
//  at scala.tools.nsc.symtab.BaseTypeSeqs$class.compoundBaseTypeSeq(BaseTypeSeqs.scala:189)
//  at scala.tools.nsc.symtab.SymbolTable.compoundBaseTypeSeq(SymbolTable.scala:13)
//  at scala.tools.nsc.symtab.Types$CompoundType.baseTypeSeq(Types.scala:1297)
//  at scala.tools.nsc.symtab.Types$PolyType.baseTypeSeq(Types.scala:2079)
//  at scala.tools.nsc.symtab.Symbols$Symbol.baseTypeSeqLength$1(Symbols.scala:962)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isLess(Symbols.scala:965)
//  at scala.tools.nsc.symtab.Types$Type.baseTypeIndex(Types.scala:776)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isNonBottomSubClass(Symbols.scala:981)
//  at scala.tools.nsc.symtab.Types$AsSeenFromMap.toPrefix$1(Types.scala:3335)

--
Paul Phillips      | Where there's smoke, there's mirrors!
Vivid              |
Empiricist         |
i'll ship a pulp   |----------* http://www.improving.org/paulp/ *----------




--
« Je déteste la montagne, ça cache le paysage »
Alphonse Allais
adriaanm
Joined: 2010-02-08,
User offline. Last seen 31 weeks 4 days ago.
Re: lazy vals in Definitions
from Implicits.scala:
      /** Is `sym' the standard conforms method in Predef?       *  Note: DON't replace this by sym == Predef_conforms, as Predef_conforms is a `def'        *  which does a member lookup (it can't be a lazy val because we might reload Predef       *  during resident compilations).         */      def isConformsMethod(sym: Symbol) =         sym.name == nme.conforms && sym.owner == PredefModule.moduleClass
cheers adriaan
On Wed, Nov 3, 2010 at 12:42 PM, iulian dragos <iulian.dragos@epfl.ch> wrote:


On Wed, Nov 3, 2010 at 11:22 AM, martin odersky <martin.odersky@epfl.ch> wrote:
Yes, initialization order is pretty tricky. My advice on Definitions would be: Don't try to change existsing definitions to lazy vals, because somebody (I think Iulian) already went over it, which means that there's most likely some reason why some definition is not a lazy val. On the other hand, every new thing that goes into Definitions should be a lazy val.

Indeed, when lazy values were added to Scala I tried to change all definitions on Definitions to be lazy. Unfortunately, that was impossible for some of them. I don't remember exactly what were the issues, it's been a few years ago.   

Cheers

 -- Martin


On Tue, Nov 2, 2010 at 7:41 PM, Paul Phillips <paulp@improving.org> wrote:
[SUMMARY: lazy vals in Definitions.scala! When? Why? Why Not?]

As we all know, it's a whole lot nicer when the symbols in Definitions
are defined with lazy vals rather than defs because they can be used in
pattern matching like:

 case TypeRef(_, ArraySymbol, arg :: Nil) => ...

On the other hand there is some subset of the symbols therein which
cannot be changed in this way lest cycles develop.  And I'm a little
mystified as to how to a) identify that subset and b) be sure that
changing any given one doesn't cause some hidden issue.

For instance: as an experiment just now I tried changing Array_apply,
Array_upd: Donte, and Array_length to lazy vals.  From the standpoint of the
compiler everything seems fine.  Two tests fail, and I'd have been hard
pressed to guess it'd be the two tests below.  Both fail with "cyclic
reference involving class Array" and all I see in common is a structural
type and a method call to it.  If those two tests didn't exist the
change would have appeared safe (thus my reduced confidence in any such
change.)

I believe there is a past incident of my changing something along these
lines and breakage materializing: actually I only know about it by
inference from meeting notes, nobody told me directly.  But assuming it
really happened I'd certainly like to avoid repeating it.  Here are the
failing tests and the stack trace.

// t0586.scala
object RClose {
 type ReflectCloseable = { def close(): Unit }
 def withReflectCloseable[T <: ReflectCloseable, R](s: T)(action: T => R): R =
   try {
     action(s)
   } finally {
     s.close()
   }
}

// t1131.scala
trait A { self: Any { def p: Any } =>
 def f(b: => Unit) {}
 f { p }
}

// scala.tools.nsc.symtab.Symbols$CyclicReference: illegal cyclic reference involving class Array
//  at scala.tools.nsc.symtab.Symbols$TypeSymbol.tpe(Symbols.scala:1786)
//  at scala.tools.nsc.symtab.BaseTypeSeqs$class.compoundBaseTypeSeq(BaseTypeSeqs.scala:189)
//  at scala.tools.nsc.symtab.SymbolTable.compoundBaseTypeSeq(SymbolTable.scala:13)
//  at scala.tools.nsc.symtab.Types$CompoundType.baseTypeSeq(Types.scala:1297)
//  at scala.tools.nsc.symtab.Types$PolyType.baseTypeSeq(Types.scala:2079)
//  at scala.tools.nsc.symtab.Symbols$Symbol.baseTypeSeqLength$1(Symbols.scala:962)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isLess(Symbols.scala:965)
//  at scala.tools.nsc.symtab.Types$Type.baseTypeIndex(Types.scala:776)
//  at scala.tools.nsc.symtab.Symbols$Symbol.isNonBottomSubClass(Symbols.scala:981)
//  at scala.tools.nsc.symtab.Types$AsSeenFromMap.toPrefix$1(Types.scala:3335)

--
Paul Phillips      | Where there's smoke, there's mirrors!
Vivid              |
Empiricist         |
i'll ship a pulp   |----------* http://www.improving.org/paulp/ *----------




--
« Je déteste la montagne, ça cache le paysage »
Alphonse Allais

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland