def for require in traits

32 views
Skip to first unread message

metzben

unread,
Nov 22, 2010, 2:01:49 PM11/22/10
to Iowa Scala Enthusiasts
I don't know if you guys have run into this but I got bit by this
pretty good last week.
(my context was akka actor composition but it applies across the
board)

When requiring a member in a trait...

trait BadFoo {
val tryingToMakeAwesome: String //<-- bad
}

where you're going to mixin the implementation later ->

trait ImplBadFoo extends BadFoo {
val tryingToMakeAwesome = "no awesome for you"
}

use def instead of val because..
When it's a val it creates a field and that field isn't assigned when
you call it in BadFoo

soooo... do this instead

trait Foo {
def makeAwesome: String
}

class FooImpl extends Foo {
val makeAwesome = "AWESOME"
}

(Thanks to @viktorklang for helping on this one)

Also, I'm thinking that lazy val would work as well...

regards,
Ben

Zach Cox

unread,
Nov 22, 2010, 2:14:16 PM11/22/10
to iowa-scala-...@googlegroups.com
The repl doesn't seem to like lazy vals with no initialization in traits:

scala> trait A { lazy val a: String }
<console>:1: error: lazy values may not be abstract
trait A { lazy val a: String }


Is this what was causing the problem?

scala> trait A { val a: String; val b: String = a }
defined trait A

scala> trait B extends A
defined trait B

scala> class C extends B
<console>:7: error: class C needs to be abstract, since value a in
trait A of type String is not defined
class C extends B


Did you try using a val at first so it would be cached, and not create
a new thing every time it was accessed?

Benjamin Metz

unread,
Nov 22, 2010, 3:46:25 PM11/22/10
to iowa-scala-...@googlegroups.com
Yeah sorry...
Its always hard to provide context in trivial examples.  My bad.  

This is better...
//id is going to be provided someplace else
trait BadFoo { 
   val id: String 
   val selfID = id 
}

//id is provided via class param... and compiles just fine
class MyBadFooService(acctID: String) extends BadFoo { 
    val id = acctID 
}

val service = new MyBadFooService("123") 

scala> service.selfID
res4: String = null

//this works 
trait BetterFoo { val id: String; def selfID = id }

class MyBetterFooService(acctID: String) extends BetterFoo { 
    val id = acctID 
}

val service = new MyBetterFooService("123") 

scala> service.selfID
res3: String = 123

My context was a bit more gnarly... an akka supervisor hierarchy composed as a service :)
Anyway, my new rule is:

Always use def when requiring members in traits.  Don't use val/var.  
I think what threw me for a loop is the interchangeable-ness of def and val (Still getting used to the special cases) 

Just wanted to pass that on, because its a bit tougher to trace in a large system of service.

Zach Cox

unread,
Nov 22, 2010, 3:51:17 PM11/22/10
to iowa-scala-...@googlegroups.com
Ahhh OK so this is the problem:

scala> trait A { val a: String; val b = a }
defined trait A

scala> class B extends A { val a: String = "a" }
defined class B

scala> new B().b
res1: String = null


I guess that makes sense, because a is technically null when b is initialized?

It looks like lazy val will actually work:

scala> trait A { val a: String; lazy val b = a }
defined trait A

scala> class B extends A { val a: String = "a" }
defined class B

scala> new B().b
res0: String = a

metzben

unread,
Nov 22, 2010, 3:53:14 PM11/22/10
to Iowa Scala Enthusiasts
And what I meant about lazy vals... (They work as well).

scala> trait GoodFoo { val id: String; lazy val selfID =
id }
defined trait GoodFoo

scala> class MyGoodFooService(acctID: String) extends GoodFoo { val id
= acctID }
defined class MyGoodFooService

scala> val service = new
MyGoodFooService("123")
service: MyGoodFooService = MyGoodFooService@67eaf25d

scala>
service.selfID
res5: String = 123

regards,
Ben


On Nov 22, 2:46 pm, Benjamin Metz <vonforstme...@gmail.com> wrote:
> Yeah sorry...
> Its always hard to provide context in trivial examples.  My bad.
>
> This is better...
> //id is going to be provided someplace else
> trait BadFoo {
>    val id: String
>    val selfID = id
>
> }
>
> //id is provided via class param... and compiles just fine
> class MyBadFooService(acctID: String) extends BadFoo {
>     val id = acctID
>
> }
>
> val service = new MyBadFooService("123")
>
> scala> service.selfID
> res4: String = null
>
> //this works
> trait BetterFoo { val id: String; def selfID = id }
>
> class MyBetterFooService(acctID: String) extends BetterFoo {
>     val id = acctID
>
> }
>
> val service = new MyBetterFooService("123")
>
> scala> service.selfID
> res3: String = 123
>
> My context was a bit more gnarly... an akka supervisor hierarchy composed as
> a service :)
> Anyway, my new rule is:
>
> *Always use def when requiring members in traits.*  Don't use val/var.
> I think what threw me for a loop is the interchangeable-ness of def and val
> (Still getting used to the special cases)
>
> Just wanted to pass that on, because its a bit tougher to trace in a large
> system of service.
>
>
>
>
>
>
>
> On Mon, Nov 22, 2010 at 1:14 PM, Zach Cox <zcox...@gmail.com> wrote:
> > The repl doesn't seem to like lazy vals with no initialization in traits:
>
> > scala> trait A { lazy val a: String }
> > <console>:1: error: lazy values may not be abstract
> >       trait A { lazy val a: String }
>
> > Is this what was causing the problem?
>
> > scala> trait A { val a: String; val b: String = a }
> > defined trait A
>
> > scala> trait B extends A
> > defined trait B
>
> > scala> class C extends B
> > <console>:7: error: class C needs to be abstract, since value a in
> > trait A of type String is not defined
> >       class C extends B
>
> > Did you try using a val at first so it would be cached, and not create
> > a new thing every time it was accessed?
>

metzben

unread,
Nov 22, 2010, 3:56:40 PM11/22/10
to Iowa Scala Enthusiasts
lol... right. Sorry I missed your post :)
Reply all
Reply to author
Forward
0 new messages