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

Troubles with Traits

14 replies
kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.

This is my first time using traits, but I cannot seem to achieve what I
want. I want a trait "Emitter" I can use use to emit messages with
println() and with org.slf4j.Logger. Currently this looks like

trait Emitter {
var logger:Logger
def emit(message:String) = {
logger.info(message)
println(message)
}
}

but when I try to use it like

object Archive extends Emitter {
val properties = Properties.getScriptProperties(); // must call
before getLogger() - EK
logger = Properties.getLogger(getClass())

src\Archive.scala:31: error: object creation impossible, since variable
logger in trait Emitter of type org.slf4j.Logger is not defined
(Note that variables need to be initialized to be defined)
object Archive extends Emitter {
^
one error found
Compilation failed, exitValue = 1

Is there some way to do what I want?

Cheers, Eric

d_m
Joined: 2010-11-11,
User offline. Last seen 35 weeks 2 days ago.
Re: Troubles with Traits

On Mon, Dec 05, 2011 at 11:34:17AM -0800, Eric Kolotyluk wrote:
> trait Emitter {
> var logger:Logger
> def emit(message:String) = {
> logger.info(message)
> println(message)
> }
> }
>
> but when I try to use it like
>
> object Archive extends Emitter {
> val properties = Properties.getScriptProperties(); // must call
> before getLogger() - EK
> logger = Properties.getLogger(getClass())

I think you probably want to make logger be defined with "val" not
"var"; you'd also want to put "val" before logger in your Archive
object.

Also, since you're not using the result of getScriptProperties() you
might as well not save the result (to make it more obvious what's going
on).

andreak
Joined: 2009-04-24,
User offline. Last seen 2 years 22 weeks ago.
Re: Troubles with Traits
On 12/05/2011 08:34 PM, Eric Kolotyluk wrote:
4EDD1CB9 [dot] 9080407 [at] gmail [dot] com" type="cite">This is my first time using traits, but I cannot seem to achieve what I want. I want a trait "Emitter" I can use use to emit messages with println() and with org.slf4j.Logger. Currently this looks like

trait Emitter {
  var logger:Logger
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}

but when I try to use it like

object Archive extends Emitter {
  val properties = Properties.getScriptProperties();    // must call before getLogger() - EK
  logger = Properties.getLogger(getClass())

src\Archive.scala:31: error: object creation impossible, since variable logger in trait Emitter of type org.slf4j.Logger is not defined
(Note that variables need to be initialized to be defined)
object Archive extends Emitter {
       ^
one error found
Compilation failed, exitValue = 1

Is there some way to do what I want?

In Emitter, try to initialize logger:
trait Emitter {
  var logger:Logger = null
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}

If you really want logger to be an abstract var, you'll need a setter as well in the subclass:

object Archive extends Emitter {
var logger = null
def logger_=(v: Logger):Unit = { logger = v}
}
-- 
Andreas Joseph Krogh  - mob: +47 909 56 963
Senior Software Developer / CTO - OfficeNet AS - http://www.officenet.no
Public key: http://home.officenet.no/~andreak/public_key.asc
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Troubles with Traits

On Mon, Dec 5, 2011 at 17:34, Eric Kolotyluk wrote:
> This is my first time using traits, but I cannot seem to achieve what I
> want. I want a trait "Emitter" I can use use to emit messages with println()
> and with org.slf4j.Logger. Currently this looks like
>
> trait Emitter {
>  var logger:Logger
>  def emit(message:String) = {
>    logger.info(message)
>    println(message)
>  }
> }

This has declared "logger" as abstract. If you want it unitialized,
write it like this:

var logger: Logger = _

However, it's best to declare it as

def logger: Logger // abstact

And then write this in Archive:

override val logger = Properties.getLogger(getClass()) // maybe lazy
val if it logging might not happen at all

You can override a def with a val (but not the other way around). The
def is appropriate for the trait definition, because all the trait
wants is a getter.

kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits

On 2011-12-05 11:42 AM, Erik Osheim wrote:
> On Mon, Dec 05, 2011 at 11:34:17AM -0800, Eric Kolotyluk wrote:
>> trait Emitter {
>> var logger:Logger
>> def emit(message:String) = {
>> logger.info(message)
>> println(message)
>> }
>> }
>>
>> but when I try to use it like
>>
>> object Archive extends Emitter {
>> val properties = Properties.getScriptProperties(); // must call
>> before getLogger() - EK
>> logger = Properties.getLogger(getClass())
> I think you probably want to make logger be defined with "val" not
> "var"; you'd also want to put "val" before logger in your Archive
> object.
>
> Also, since you're not using the result of getScriptProperties() you
> might as well not save the result (to make it more obvious what's going
> on).
>

kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits
On 2011-12-05 11:50 AM, Andreas Joseph Krogh wrote:
4EDD207A [dot] 2070200 [at] officenet [dot] no" type="cite"> On 12/05/2011 08:34 PM, Eric Kolotyluk wrote:
4EDD1CB9 [dot] 9080407 [at] gmail [dot] com" type="cite">This is my first time using traits, but I cannot seem to achieve what I want. I want a trait "Emitter" I can use use to emit messages with println() and with org.slf4j.Logger. Currently this looks like

trait Emitter {
  var logger:Logger
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}

but when I try to use it like

object Archive extends Emitter {
  val properties = Properties.getScriptProperties();    // must call before getLogger() - EK
  logger = Properties.getLogger(getClass())

src\Archive.scala:31: error: object creation impossible, since variable logger in trait Emitter of type org.slf4j.Logger is not defined
(Note that variables need to be initialized to be defined)
object Archive extends Emitter {
       ^
one error found
Compilation failed, exitValue = 1

Is there some way to do what I want?

In Emitter, try to initialize logger:
trait Emitter {
  var logger:Logger = null
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}
OK, now I feel stupid. That works perfectly - thanks :-) Where do I send the beer to Andreas?

While this works perfectly well, is this the most Scala-like way to do things, or is there a more elegant way to do this sort of thing?
4EDD207A [dot] 2070200 [at] officenet [dot] no" type="cite">
If you really want logger to be an abstract var, you'll need a setter as well in the subclass:

object Archive extends Emitter {
var logger = null
def logger_=(v: Logger):Unit = { logger = v}
}

I'm not sure what advantage this has?

4EDD207A [dot] 2070200 [at] officenet [dot] no" type="cite">
-- 
Andreas Joseph Krogh andreak [at] officenet [dot] no" rel="nofollow"><andreak@officenet.no> - mob: +47 909 56 963
Senior Software Developer / CTO - OfficeNet AS - http://www.officenet.no
Public key: http://home.officenet.no/~andreak/public_key.asc
andreak
Joined: 2009-04-24,
User offline. Last seen 2 years 22 weeks ago.
Re: Troubles with Traits
On 12/05/2011 09:28 PM, Eric Kolotyluk wrote:
4EDD2962 [dot] 1000808 [at] gmail [dot] com" type="cite"> On 2011-12-05 11:50 AM, Andreas Joseph Krogh wrote:
4EDD207A [dot] 2070200 [at] officenet [dot] no" type="cite"> On 12/05/2011 08:34 PM, Eric Kolotyluk wrote:
4EDD1CB9 [dot] 9080407 [at] gmail [dot] com" type="cite">This is my first time using traits, but I cannot seem to achieve what I want. I want a trait "Emitter" I can use use to emit messages with println() and with org.slf4j.Logger. Currently this looks like

trait Emitter {
  var logger:Logger
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}

but when I try to use it like

object Archive extends Emitter {
  val properties = Properties.getScriptProperties();    // must call before getLogger() - EK
  logger = Properties.getLogger(getClass())

src\Archive.scala:31: error: object creation impossible, since variable logger in trait Emitter of type org.slf4j.Logger is not defined
(Note that variables need to be initialized to be defined)
object Archive extends Emitter {
       ^
one error found
Compilation failed, exitValue = 1

Is there some way to do what I want?

In Emitter, try to initialize logger:
trait Emitter {
  var logger:Logger = null
  def emit(message:String) = {
    logger.info(message)
    println(message)
  }
}
OK, now I feel stupid. That works perfectly - thanks :-) Where do I send the beer to Andreas?

Send it to yourself each time you accomplish new cool stuff in Scala-land:-)

4EDD2962 [dot] 1000808 [at] gmail [dot] com" type="cite"> While this works perfectly well, is this the most Scala-like way to do things, or is there a more elegant way to do this sort of thing?
4EDD207A [dot] 2070200 [at] officenet [dot] no" type="cite">
If you really want logger to be an abstract var, you'll need a setter as well in the subclass:

object Archive extends Emitter {
var logger = null
def logger_=(v: Logger):Unit = { logger = v}
}

I'm not sure what advantage this has?

Having an abstract var in a trait makes absolutely no sense to me. There's no advantage unless you actually want "write-access" to the variable, and not just a getter (a def, like Daniel suggested).

If all you're after is to have (read) access to "logger" from Emitter, but delegate the initialization/implementation to the subclass, Daniel's suggestion of having a def in Emitter and a val in subclass is the "scala-way".
-- 
Andreas Joseph Krogh  - mob: +47 909 56 963
Senior Software Developer / CTO - OfficeNet AS - http://www.officenet.no
Public key: http://home.officenet.no/~andreak/public_key.asc
kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits

On 2011-12-05 12:15 PM, Daniel Sobral wrote:
> On Mon, Dec 5, 2011 at 17:34, Eric Kolotyluk wrote:
>> This is my first time using traits, but I cannot seem to achieve what I
>> want. I want a trait "Emitter" I can use use to emit messages with println()
>> and with org.slf4j.Logger. Currently this looks like
>>
>> trait Emitter {
>> var logger:Logger
>> def emit(message:String) = {
>> logger.info(message)
>> println(message)
>> }
>> }
> This has declared "logger" as abstract. If you want it unitialized,
> write it like this:
>
> var logger: Logger = _

var logger: Logger = null // also works fine

>
> However, it's best to declare it as
>
> def logger: Logger // abstact
>
> And then write this in Archive:
>
> override val logger = Properties.getLogger(getClass()) // maybe lazy
> val if it logging might not happen at all

OK, I can understand how that works, and it does seem more consistent
with how traits are documented.

I tested with 'override lazy val logger =
Properties.getLogger(getClass())' and it works great, as it is likely
logging might not happen.

>
> You can override a def with a val (but not the other way around). The
> def is appropriate for the trait definition, because all the trait
> wants is a getter.
Thanks for the cool knowledge. This was not apparent from the
documentation I was reading, but seems to be a nice pattern.

Cheers, Eric

kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits

On 2011-12-05 12:37 PM, Eric Kolotyluk wrote:
> On 2011-12-05 12:15 PM, Daniel Sobral wrote:
>> On Mon, Dec 5, 2011 at 17:34, Eric
>> Kolotyluk wrote:
>>> This is my first time using traits, but I cannot seem to achieve what I
>>> want. I want a trait "Emitter" I can use use to emit messages with
>>> println()
>>> and with org.slf4j.Logger. Currently this looks like
>>>
>>> trait Emitter {
>>> var logger:Logger
>>> def emit(message:String) = {
>>> logger.info(message)
>>> println(message)
>>> }
>>> }
>> This has declared "logger" as abstract. If you want it unitialized,
>> write it like this:
>>
>> var logger: Logger = _
>
> var logger: Logger = null // also works fine
>
>>
>> However, it's best to declare it as
>>
>> def logger: Logger // abstact
>>
>> And then write this in Archive:
>>
>> override val logger = Properties.getLogger(getClass()) // maybe lazy
>> val if it logging might not happen at all
>
> OK, I can understand how that works, and it does seem more consistent
> with how traits are documented.
>
> I tested with 'override lazy val logger =
> Properties.getLogger(getClass())' and it works great, as it is likely
> logging might not happen.

OK, this does not work as I expected. My logs show

[2011-12-05 13:01:23,890] INFO [main] -
Emitter$class.emit(Emitter.scala:41) - creating archive
Intersystem\archive\2011-12-05 21-01-23.889.7z
[2011-12-05 13:01:25,713] INFO [main] -
Emitter$class.emit(Emitter.scala:41) - added Intersystem\dump\2011-12-05
21-01-23.545

Whereas what I wanted was

[2011-12-05 13:01:23,890] INFO [main] -
Archive$class.apply(Archive.scala:48) - creating archive
Intersystem\archive\2011-12-05 21-01-23.889.7z
[2011-12-05 13:01:25,713] INFO [main] -
Archive$class.apply(Archive.scala:52) - added
Intersystem\dump\2011-12-05 21-01-23.545

How does

object Archive extends Emitter {
val properties = Properties.getScriptProperties(); // must call
before getLogger() - EK
override lazy val logger = Properties.getLogger(getClass())

pick up the logger for Emitter instead of Archive? How can I get Emitter
to use the logger for Archive?

Cheers, Eric
>
>>
>> You can override a def with a val (but not the other way around). The
>> def is appropriate for the trait definition, because all the trait
>> wants is a getter.
> Thanks for the cool knowledge. This was not apparent from the
> documentation I was reading, but seems to be a nice pattern.
>
> Cheers, Eric

Florian Hars 3
Joined: 2011-05-08,
User offline. Last seen 42 years 45 weeks ago.
Re: Troubles with Traits

Am 05.12.2011 21:37, schrieb Eric Kolotyluk:
> On 2011-12-05 12:15 PM, Daniel Sobral wrote:
>> var logger: Logger = _
>
> var logger: Logger = null // also works fine

It compiles to the same code. The difference is that the first version
makes it more clear that you see it as uninitialized vs. initialized
with the special value null (which you should try to avoid anyway).

But of course both versions are not "fine", as they leave you vulnerable
to null pointer excepions if you extend your trait and forget to
initialize logger (which can take quite some time to diagnose if the
logger is intended to log only exceptional behaviour, and then it will
be doubly hard to debug the problem). The version recommended by Daniel
using def and override val will not even compile in that case, which
is the point of a statically typed language.

- Florian.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Troubles with Traits

On Mon, Dec 5, 2011 at 19:12, Eric Kolotyluk wrote:
>
> OK, this does not work as I expected. My logs show
>
> [2011-12-05 13:01:23,890]  INFO [main] -
> Emitter$class.emit(Emitter.scala:41) - creating archive
> Intersystem\archive\2011-12-05 21-01-23.889.7z
> [2011-12-05 13:01:25,713]  INFO [main] -
> Emitter$class.emit(Emitter.scala:41) - added Intersystem\dump\2011-12-05
> 21-01-23.545
>
> Whereas what I wanted was
>
> [2011-12-05 13:01:23,890]  INFO [main] -
> Archive$class.apply(Archive.scala:48) - creating archive
> Intersystem\archive\2011-12-05 21-01-23.889.7z
> [2011-12-05 13:01:25,713]  INFO [main] -
> Archive$class.apply(Archive.scala:52) - added Intersystem\dump\2011-12-05
> 21-01-23.545
>
> How does
>
>
> object Archive extends Emitter {
>  val properties = Properties.getScriptProperties();    // must call before
> getLogger() - EK
>  override lazy val logger = Properties.getLogger(getClass())
>
> pick up the logger for Emitter instead of Archive? How can I get Emitter to
> use the logger for Archive?

And "object" is a not a class. Try replacing "object" with "class"
there, and then writing:

object Archive extends Archive // object Archive is of class Archive

kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits
On 2011-12-06 6:18 AM, Daniel Sobral wrote:
zczPdviqnagJarBw [at] mail [dot] gmail [dot] com" type="cite">
On Mon, Dec 5, 2011 at 19:12, Eric Kolotyluk  wrote:
OK, this does not work as I expected. My logs show

[2011-12-05 13:01:23,890]  INFO [main] -
Emitter$class.emit(Emitter.scala:41) - creating archive
Intersystem\archive\2011-12-05 21-01-23.889.7z
[2011-12-05 13:01:25,713]  INFO [main] -
Emitter$class.emit(Emitter.scala:41) - added Intersystem\dump\2011-12-05
21-01-23.545

Whereas what I wanted was

[2011-12-05 13:01:23,890]  INFO [main] -
Archive$class.apply(Archive.scala:48) - creating archive
Intersystem\archive\2011-12-05 21-01-23.889.7z
[2011-12-05 13:01:25,713]  INFO [main] -
Archive$class.apply(Archive.scala:52) - added Intersystem\dump\2011-12-05
21-01-23.545

How does


object Archive extends Emitter {
 val properties = Properties.getScriptProperties();    // must call before
getLogger() - EK
 override lazy val logger = Properties.getLogger(getClass())

pick up the logger for Emitter instead of Archive? How can I get Emitter to
use the logger for Archive?
And "object" is a not a class. Try replacing "object" with "class"
there, and then writing:

object Archive extends Archive // object Archive is of class Archive


Here is what I have, but it does not fix the problem

class Restore extends Emitter {
  val properties = Properties.getScriptProperties();    // must call before getLogger() - EK
  override lazy val logger = Properties.getLogger(getClass())
  . . .
}

object Restore extends Restore // object Restore extends class Restore

[2011-12-06 08:24:31,438]  INFO [main] - Emitter$class.emitInfo(Emitter.scala:42) - extracted Intersystem\dump\2011-12-06 07-48-03.697\hibernate.sql
[2011-12-06 08:24:31,439]  INFO [main] - Emitter$class.emitInfo(Emitter.scala:42) - restoring Intersystem\dump\2011-12-06 07-48-03.697\hibernate.sql

Maybe it is not possible make it do what I want?

Cheers, Eric

bmjsmith
Joined: 2010-03-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Troubles with Traits
On 6 December 2011 16:32, Eric Kolotyluk <eric.kolotyluk@gmail.com> wrote:

Maybe it is not possible make it do what I want?

Cheers, Eric


Hi Eric
You might want to take a look at the Grizzled slf4j wrapper.
Specifically:
https://github.com/bmc/grizzled-slf4j/blob/master/src/main/scala/grizzled/slf4j/slf4j.scala
If I understand correctly, the "Logging" trait seems to do what you want and should either be a useful example or you should be able to just mix it in wherever you wish to use it (though you will need to include slf4j and logback or some other backend as dependencies).
I'm not sure why your specific example isn't working but you shouldn't need to override the logger definition in classes extending the trait - dynamic dispatch should mean the class returned is the one of the subclass I thought.  Maybe there's something lurking in "properties" which seems to be your equivalent of a LogFactory that causes this to behave differently?  Or maybe you mean "properties.getLogger" not "Properties.getLogger"?
Cheers
Brian

kolotyluk
Joined: 2010-06-04,
User offline. Last seen 5 weeks 17 hours ago.
Re: Troubles with Traits
On 2011-12-06 10:15 AM, Brian Smith wrote:
CAE8w+W10j9Ywreu0YCHSevpWT720BbSMTNSJFr8UGq9kX3fJ3Q [at] mail [dot] gmail [dot] com" type="cite">On 6 December 2011 16:32, Eric Kolotyluk <eric [dot] kolotyluk [at] gmail [dot] com" rel="nofollow">eric.kolotyluk@gmail.com> wrote:

Maybe it is not possible make it do what I want?

Cheers, Eric


Hi Eric
You might want to take a look at the Grizzled slf4j wrapper.
Specifically:
https://github.com/bmc/grizzled-slf4j/blob/master/src/main/scala/grizzled/slf4j/slf4j.scala
If I understand correctly, the "Logging" trait seems to do what you want and should either be a useful example or you should be able to just mix it in wherever you wish to use it (though you will need to include slf4j and logback or some other backend as dependencies).

OK, I took a look at it, but the solution does not seem obvious.

CAE8w+W10j9Ywreu0YCHSevpWT720BbSMTNSJFr8UGq9kX3fJ3Q [at] mail [dot] gmail [dot] com" type="cite">
I'm not sure why your specific example isn't working but you shouldn't need to override the logger definition in classes extending the trait - dynamic dispatch should mean the class returned is the one of the subclass I thought.  Maybe there's something lurking in "properties" which seems to be your equivalent of a LogFactory that causes this to behave differently?  Or maybe you mean "properties.getLogger" not "Properties.getLogger"?

I really do mean Properties.getLogger() as that is Java code I wrote to get a logger as it has to be able to work as a static method. Basically, getting the logger this way ensures that the .properties file with all the logging properties is read first and the logging system is configured properly before getting a new logger. Also, ensures the logger is a singleton by caching them in a HashMap.

I am using a log4j back-end with slf4j.

I guess the other way of doing this would be to configure a special console logger and log Info or higher to the command like and log more verbose stuff to the file logger. This would eliminate the need for my Emitter trait.

Cheers, Eric

CAE8w+W10j9Ywreu0YCHSevpWT720BbSMTNSJFr8UGq9kX3fJ3Q [at] mail [dot] gmail [dot] com" type="cite">
Cheers
Brian

bmjsmith
Joined: 2010-03-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Troubles with Traits

Hi Eric

I really do mean Properties.getLogger() as that is Java code I wrote to get a logger as it has to be able to work as a static method. Basically, getting the logger this way ensures that the .properties file with all the logging properties is read first and the logging system is configured properly before getting a new logger. Also, ensures the logger is a singleton by caching them in a HashMap.

You shouldn't need to do this, log4j will take care of caching and configuring for you.  Subsequent requests to the factory for the same named logger will get the same logger object.  (Under the hood there'll be one logger object per name but if that has no specific configuration it will just link to the first one above it in the hierarchy that does).
There are a few different ways to make log4j find it's configuration without needing to force it programmatically - the simplest is to start the jvm with -Dlog4j.configuration=log4j.properties (or log4j.xml) and make sure that file is on the classpath.  There's a description here of the Default Initialization Procedure.
If your Properties code is only doing the management of a logger singleton and forcing configuration load, you can likely do away with it completely - it might be the reason you're getting the wrong logger object, and I can't replicate your problem using the default initialization for log4j.
I've uploaded a simple example to github which seems to do what you want. 
I guess the other way of doing this would be to configure a special console logger and log Info or higher to the command like and log more verbose stuff to the file logger. This would eliminate the need for my Emitter trait.

You can just log to the appropriate levels as normal and through configuration attach a LevelRangeFilter on your console appender to make sure that only INFO and above show there.  This avoids needing to have special loggers or to remember to use them.  To do this though, you need to use xml to configure log4j, for some reason filters aren't supported in properties files.
Hope this helps
Brian

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