- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
null option - initialization order
Tue, 2011-08-23, 17:22
For example:
scala> class A { val x = Some(1); println (x) }
defined class A
scala> class B extends A { override val x = Some(2); println(x) }
defined class B
scala> new B
null
Some(2)
res1: B = B@48b89bc5
scala>
I understand Paul’s explanation here https://github.com/paulp/scala-faq/wiki/Initialization-Order - initializing fields only once - but this kind of side-effect from a client derived class onto a base library class is bad… it gives birth to monsters like this null Option…
Yes, this is also possible, but it’s not an excuse:
scala> val x : Option[Int] = null
x: Option[Int] = null
is there nothing we can do to make this behavior more intuitive? Just a not in the style guide?
thanks,
Razie
Tue, 2011-08-23, 23:47
#2
Re: null option - initialization order
On Tue, Aug 23, 2011 at 12:40 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
You should really *not* override a val. If it is a val, then you are
kind of guaranteeing the return value is that one. To change it in a
subclass is a violation of the liskov principle.
Is overriding a method and changing its behavior a violation?
The simplest way to achieve the same thing is this:
class A { def x = Some(1); println(x) }
class B extends A { override def x = Some(2); println(x) }
You can even override A's def x with a val x.
This will only cause trouble with frameworks that search for fields
through reflection. In that case, you'd better just go with a var
instead, and possibly make it protected and add a public getter if
needed.
On Tue, Aug 23, 2011 at 13:21, Razvan Cojocaru <pub [at] razie [dot] com> wrote:
> For example:
>
>
>
> scala> class A { val x = Some(1); println (x) }
>
> defined class A
>
>
>
> scala> class B extends A { override val x = Some(2); println(x) }
>
> defined class B
>
>
>
> scala> new B
>
> null
>
> Some(2)
>
> res1: B = B@48b89bc5
>
>
>
> scala>
>
>
>
>
>
> I understand Paul’s explanation here
> https://github.com/paulp/scala-faq/wiki/Initialization-Order -
> initializing fields only once - but this kind of side-effect from a client
> derived class onto a base library class is bad… it gives birth to monsters
> like this null Option…
>
>
>
> Yes, this is also possible, but it’s not an excuse:
>
>
>
> scala> val x : Option[Int] = null
>
> x: Option[Int] = null
>
>
>
> is there nothing we can do to make this behavior more intuitive? Just a not
> in the style guide?
>
>
>
> thanks,
>
> Razie
>
>
>
>
>
>
>
>
--
Daniel C. Sobral
I travel to the future all the time.
Wed, 2011-08-24, 00:17
#3
Re: null option - initialization order
On 08/24/2011 02:40 AM, Daniel Sobral wrote:
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
JfwwdM0qUmLTbXvyaZvEAAdmx8Oo7ni5u4g [at] mail [dot] gmail [dot] com" type="cite">Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 00:27
#4
Re: null option - initialization order
What?
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 02:40 AM, Daniel Sobral wrote:Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 00:37
#5
Re: null option - initialization order
On 08/24/2011 09:25 AM, Naftoli Gugenheim wrote:
There are many legitimate use-cases for overriding a val. Suggest revision.
y4WZk+Q [at] mail [dot] gmail [dot] com" type="cite"> What?
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com" rel="nofollow">tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 02:40 AM, Daniel Sobral wrote:Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
There are many legitimate use-cases for overriding a val. Suggest revision.
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 00:47
#6
Re: null option - initialization order
I think Daniel's point was that if the superclass assigned one value to the val, then code that accepts an object with the superclass type would assume it has the value specified in the superclass. If then a subclass assigns a different value to it, that breaks the code's assumption. As opposed to where the superclass only defines an abstract val; then there's no assumption to break.
I disagreed. As implied by the quote I posted, Liskov doesn't say that subclasses can't behave differently then the superclass; only that they must follow its contract. Not all code is a contract. Types certainly are, and I suppose anything documented explicitly as being a contract is; but actual implementation is not.
On Tue, Aug 23, 2011 at 7:26 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On Tue, Aug 23, 2011 at 7:26 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 09:25 AM, Naftoli Gugenheim wrote:What?
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 02:40 AM, Daniel Sobral wrote:Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
There are many legitimate use-cases for overriding a val. Suggest revision.
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 00:57
#7
Re: null option - initialization order
Sure. I just put all that aside to point out that there is at
least one legitimate use-case for overriding a val. I chose that
particular use-case because it is used specifically to *adhere* to
the liskov substitution rule rather than break it. For example, to
implement equals properly:
trait TypeSafeEquals[A] { def equal: A => A => Boolean }
sealed trait StrictIdentity[A] { val value: A; def ===(a: A)(implicit e: TypeSafeEqual[A]) = e.equal(value)(a) }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
On 08/24/2011 09:38 AM, Naftoli Gugenheim wrote:
trait TypeSafeEquals[A] { def equal: A => A => Boolean }
sealed trait StrictIdentity[A] { val value: A; def ===(a: A)(implicit e: TypeSafeEqual[A]) = e.equal(value)(a) }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
On 08/24/2011 09:38 AM, Naftoli Gugenheim wrote:
CANpg8PCg1CZaVwJuoRpRAAHFBG-q5zSh-F7EZrrjBcU9xKyqDw [at] mail [dot] gmail [dot] com" type="cite"> I think Daniel's point was that if the superclass assigned one value to the val, then code that accepts an object with the superclass type would assume it has the value specified in the superclass. If then a subclass assigns a different value to it, that breaks the code's assumption. As opposed to where the superclass only defines an abstract val; then there's no assumption to break. I disagreed. As implied by the quote I posted, Liskov doesn't say that subclasses can't behave differently then the superclass; only that they must follow its contract. Not all code is a contract. Types certainly are, and I suppose anything documented explicitly as being a contract is; but actual implementation is not.
On Tue, Aug 23, 2011 at 7:26 PM, Tony Morris <tonymorris [at] gmail [dot] com" rel="nofollow">tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 09:25 AM, Naftoli Gugenheim wrote:What?
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com" target="_blank" rel="nofollow">tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 02:40 AM, Daniel Sobral wrote:Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
There are many legitimate use-cases for overriding a val. Suggest revision.
-- Tony Morris http://tmorris.net/
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 01:07
#8
Re: null option - initialization order
But you're overriding an abstract val, about which there was no doubt.
On Tue, Aug 23, 2011 at 7:46 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On Tue, Aug 23, 2011 at 7:46 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
Sure. I just put all that aside to point out that there is at least one legitimate use-case for overriding a val. I chose that particular use-case because it is used specifically to *adhere* to the liskov substitution rule rather than break it. For example, to implement equals properly:
trait TypeSafeEquals[A] { def equal: A => A => Boolean }
sealed trait StrictIdentity[A] { val value: A; def ===(a: A)(implicit e: TypeSafeEqual[A]) = e.equal(value)(a) }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
On 08/24/2011 09:38 AM, Naftoli Gugenheim wrote:I think Daniel's point was that if the superclass assigned one value to the val, then code that accepts an object with the superclass type would assume it has the value specified in the superclass. If then a subclass assigns a different value to it, that breaks the code's assumption. As opposed to where the superclass only defines an abstract val; then there's no assumption to break. I disagreed. As implied by the quote I posted, Liskov doesn't say that subclasses can't behave differently then the superclass; only that they must follow its contract. Not all code is a contract. Types certainly are, and I suppose anything documented explicitly as being a contract is; but actual implementation is not.
On Tue, Aug 23, 2011 at 7:26 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 09:25 AM, Naftoli Gugenheim wrote:What?
On Tue, Aug 23, 2011 at 7:15 PM, Tony Morris <tonymorris [at] gmail [dot] com> wrote:
On 08/24/2011 02:40 AM, Daniel Sobral wrote:Bzzt.You should really *not* override a val. If it is a val, then you are kind of guaranteeing the return value is that one. To change it in a subclass is a violation of the liskov principle.
sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new StrictIdentity[A] { val value = a } }
-- Tony Morris http://tmorris.net/
There are many legitimate use-cases for overriding a val. Suggest revision.
-- Tony Morris http://tmorris.net/
-- Tony Morris http://tmorris.net/
Wed, 2011-08-24, 14:47
#9
Re: null option - initialization order
On Tue, Aug 23, 2011 at 19:43, Naftoli Gugenheim wrote:
>
>
> On Tue, Aug 23, 2011 at 12:40 PM, Daniel Sobral wrote:
>>
>> You should really *not* override a val. If it is a val, then you are
>> kind of guaranteeing the return value is that one. To change it in a
>> subclass is a violation of the liskov principle.
>
> Is overriding a method and changing its behavior a violation?
Sure, in this sense:
class A {
def lightState = // true if on
}
class B extends A {
override def lightState = // false if on
}
That changed the behavior. But note that a *val* gives more guarantees
than a *def*. In particular, you are guaranteed that two calls to a
val will always return the same value for an instance.
Wed, 2011-08-24, 14:57
#10
Re: null option - initialization order
On Tue, Aug 23, 2011 at 20:15, Tony Morris wrote:
> On 08/24/2011 02:40 AM, Daniel Sobral wrote:
>
> You should really *not* override a val. If it is a val, then you are
> kind of guaranteeing the return value is that one. To change it in a
> subclass is a violation of the liskov principle.
>
> Bzzt.
No Bzzt.
> sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity {
> implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new
> StrictIdentity[A] { val value = a } }
You should have declared the abstract "value" to be a def, though you
might well want to provide the guarantees of a "val" here.
BUT, in this case you are overriding an abstract member, which is a
very different thing than what was being done.
Wed, 2011-08-24, 17:57
#11
Re: null option - initialization order
On Wed, Aug 24, 2011 at 9:48 AM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
I think the issue for the original poster was that the current behavior is unintuitive, not whether or not it is good design to override a val.
Perhaps the compiler could warn about overridden vals?
Best,Kjetil
> sealed trait StrictIdentity[A] { val value: A }; object StrictIdentity { > implicit def ToStrictIdentity[A](a: A): StrictIdentity[A] = new
> StrictIdentity[A] { val value = a } }
You should have declared the abstract "value" to be a def, though you
might well want to provide the guarantees of a "val" here.
I think the issue for the original poster was that the current behavior is unintuitive, not whether or not it is good design to override a val.
Perhaps the compiler could warn about overridden vals?
Best,Kjetil









You should really *not* override a val. If it is a val, then you are
kind of guaranteeing the return value is that one. To change it in a
subclass is a violation of the liskov principle.
The simplest way to achieve the same thing is this:
class A { def x = Some(1); println(x) }
class B extends A { override def x = Some(2); println(x) }
You can even override A's def x with a val x.
This will only cause trouble with frameworks that search for fields
through reflection. In that case, you'd better just go with a var
instead, and possibly make it protected and add a public getter if
needed.
On Tue, Aug 23, 2011 at 13:21, Razvan Cojocaru wrote:
> For example:
>
>
>
> scala> class A { val x = Some(1); println (x) }
>
> defined class A
>
>
>
> scala> class B extends A { override val x = Some(2); println(x) }
>
> defined class B
>
>
>
> scala> new B
>
> null
>
> Some(2)
>
> res1: B = B@48b89bc5
>
>
>
> scala>
>
>
>
>
>
> I understand Paul’s explanation here
> https://github.com/paulp/scala-faq/wiki/Initialization-Order -
> initializing fields only once - but this kind of side-effect from a client
> derived class onto a base library class is bad… it gives birth to monsters
> like this null Option…
>
>
>
> Yes, this is also possible, but it’s not an excuse:
>
>
>
> scala> val x : Option[Int] = null
>
> x: Option[Int] = null
>
>
>
> is there nothing we can do to make this behavior more intuitive? Just a not
> in the style guide?
>
>
>
> thanks,
>
> Razie
>
>
>
>
>
>
>
>