0

I would like to add an implicit parameter to a class with a private constructor. Here as a simplified example:

class A[T] private(a:Int){ 
  def this()=this(0)
}

If I would like to apply Pimp my library pattern to T with Ordered[T], I would need to use (the deprecated) view bound like so:

class A[T <% Ordered[T]] private(a:Int){ 
  def this()=this(0)
}

And this works. However, to avoid the deprecated syntactic sugar I would like to pass the implicit parameter to the class. Unfortunately, this is where I'm probably doing something wrong:

class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){ 
  def this()=this(0)
}

For the above code, the compiler generates the following error:

error: No implicit view available from T => Ordered[T].
       def this()=this(0)

While if I try to pass the implicit parameter directly like so:

class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){ 
  def this()=this(0)(conv)
}

I get this:

error: not found: value conv
       def this()=this(0)(conv)

How does one pass an implicit parameter in this case?

EDIT: After some more experimentation it seems that redefining the constructor with implicit parameter is the problem. Not the fact that the constructor is private.

Krle
  • 70
  • 7

2 Answers2

3

I found an answer, it seems that I need to explicitly define the implicit parameter for the parameterless constructor, e.g.:

class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){ 
  def this()(implicit conv:T=>Ordered[T])=this(0)
}

I apologize for spamming SO, in any case I will accept any answer that provides a more in depth explanation.

Krle
  • 70
  • 7
  • 1
    You don't need to apologize, answering your own questions is perfectly acceptable. – Alexey Romanov Oct 19 '16 at 16:08
  • @Krle That's a pretty big code smell in my book. Indenting code properly for one would be a nice place to start, but I wouldn't do that. Instead, I'd move this overloaded constructor to a companion apply method with the same implicit, that looks slightly cleaner. – flavian Oct 22 '16 at 13:57
  • @flavian Thanks, I'll do that. Could you elaborate on why, though? Both for the indentation and companion object... – Krle Oct 26 '16 at 12:49
3

Scala provides ordering in 2 flavours, one via inheritance with Ordered and the other one which is actually a lot more appropriate in here is via context bounds using the Ordering typeclass.

Your approach is not actually idiomatic, and if you had something that actually used the implicit you provided, you would get an ambiguous implicit exception at compile time, because both the constructor and the method define the same implicit.

What I would do is:

class A[T : Ordering] private(a: Int)

This is actually shorthand syntax for:

class A[T] private(a: Int)(implicit ev: Ordering[T])

You can then use this argument either explicitly or implicitly. If you define it with the shorthand T : Ordering syntax.

class A[T : Ordering] private(a: Int) {
  def revSorter(list: List[T]): List[T] = {
    list.sorted(implicitly[Ordering[T]].reverse)
  }
}

If you define it with the "explicit" syntax:

class A[T] private(a: Int)(implicit ev: Ordering[T]) {
      def revSorter(list: List[T]): List[T] = {
        list.sorted(ev.reverse)
      }
    }
Samar
  • 2,091
  • 1
  • 15
  • 18
flavian
  • 28,161
  • 11
  • 65
  • 105
  • Yes, I agree about the context bounds, but I guess the main point was about how to redefine the constructor with implicit params. I should have used a generic class B in the example, rather than Ordered[T]. – Krle Oct 20 '16 at 08:09