6
votes

The type class pattern in Scala involves defining a trait such as:

trait Show[T] {
    def show(obj: T): String
}

Then you can define instantiations of this type class as such:

object Show {
    implicit val string = new Show[String] {
        def show(obj: String): String = obj
    }
    implicit object BooleanShow extends Show[Boolean] {
        def show(obj: Boolean): String = obj.toString
    }
}

The advantage of defining these instantiations for basic types in the companion object is that they are automatically in scope whenever the type class is concerned (roughly).

Functionally it would appear defining the instantiation as an implicit val or an implicit object does not change much.

Is there a difference? Is one way better than the other?

3
One difference is in exception stack trace in case typeclass can be involved. In case of object the name will be clearer.cchantep
@applicius I feel like that was answer worthy. Of course, I would also like to hear if there are other differences that anyone else can think of.jedesah
There is one typo in your code segment. It should be implicit val instead of val implicit.bmaderbacher
@bmaderbacher thx! fixed.jedesah
Except this fact that with val anonymous class is used, with readability issue in stack trace, I don't see anything more.cchantep

3 Answers

1
votes

There is actually more than the type names between val and object.

You know, object in Scala is something like a singleton in Java.
Maybe you thought that both string and BooleanShow are in an object not a class so they have no difference, but that's not true.

They are val and object no matter what.

Try this in Scala REPL.

trait Show[T] {
    def show(obj: T): String
}

object Show {
    println("!! Show created")

    implicit val string = new Show[String] {
        println("!! string created")
        def show(obj: String): String = obj
    }

    implicit object BooleanShow extends Show[Boolean] {
        println("!!BooleanShow created")
        def show(obj: Boolean): String = obj.toString
    }
}

If only the definition is done, then no printlns are executed afterwards, since Show is a singleton in effect. It's not created yet.

Next, execute Show in Scala REPL.

scala> Show
!! Show created
!! string created
res0: Show.type = Show$@35afff3b

You see, printlns in Show and Show.string were called, but the one in Show.BooleanShow was not.

You can execute Show.BooleanShow next in Scala REPL.

scala> Show.BooleanShow
!!BooleanShow created
res1: Show.BooleanShow.type = Show$BooleanShow$@18e419c5

Show.BooleanShow was initialized at last. It is a singleton, so it is lazy.

Basically, your question is the same as val and object inside a scala class? except that your val and object are defined in an object, but the linked question tries to find differences val and object defined in a class and the method in val uses reflection (but yours uses overriding, so no reflection is involved). implicit basically does not make difference in what they are.

I think you already know the difference between class and object. Further information can be found in the linked question.

1
votes

Since they say always to use explicit types for implicits, prefer val over object.

Compare Why can't Scala find my typeclass instance defined implicitly in the companion object, when the typeclass is not in a dedicated source file? where it makes a difference.

Make it lazy if necessary.

Elaboration:

scala> trait T
defined trait T

scala> object X { implicitly[T] ; object O extends T }
<console>:8: error: could not find implicit value for parameter e: T
       object X { implicitly[T] ; object O extends T }
                            ^

scala> object X { implicitly[T] ; implicit object O extends T }
<console>:8: error: could not find implicit value for parameter e: T
       object X { implicitly[T] ; implicit object O extends T }
                            ^

scala> object X { implicitly[O.type] ; implicit object O extends T }
defined object X

scala> object X { implicitly[T] ; implicit object O extends T ; implicit def y = O }
<console>:8: error: could not find implicit value for parameter e: T
       object X { implicitly[T] ; implicit object O extends T ; implicit def y = O }
                            ^

scala> object X { implicitly[T] ; implicit object O extends T ; implicit def y: T = O }
defined object X

The inferred type of O is the singleton type O.type.

0
votes

With val x = new X { } you are creating an anonymous subclass of X, whereas with object x extends X you create a "proper" subclass. I would think that the overhead of an object is minimal, and as @applicius points out, it has proper name. Therefore I suggest to go for the object in this case.