1

I'm trying to port parts of a Haskell library for datatype-generic programming to Scala. Here's the problem I've run into:

I've defined a trait, Generic, with some container-type parameter:

trait Generic[G[_]] {
     // Some function declarations go here
 }

Now I have an abstract class, Collect, with three type parameters, and a function declaration (it signifies a type than can collect all subvalues of type B into a container of type F[_] from some structure of type A):

abstract class Collect[F[_],B,A] {
  def collect_ : A => F[B]
}

In order to make it extend Generic, the first two type parameters F[_] and B are given, and A is curried (this effect is simulated using type lambdas):

class CollectC[F[_],B] extends Generic[({type C[A] = Collect[F,B,A]})#C] {
    // Function definitions go here
}

The problem is that I need the last class definition to be implicit, because later on in my code I will need to be able to write functions like

class GUnit[G[_]](implicit gg: Generic[G]) {
    // Some definitions
}

When I simply prepend implicit to the class definition, I get the an error saying implicit classes must accept exactly one primary constructor parameter. Has anyone encountered a similar problem? Is there a known way to work around it? I don't currently see how I could refactor my code while keeping the same functionality, so any advice is welcome. Thanks in advance!

Rrr
  • 13
  • 5

1 Answers1

5

Implicit classes don't work that way. They are a shorthand for implicit conversions. For instance implicit class Foo(i: Int) is equal to class Foo(i: Int); implicit def Foo(i: Int) = new Foo(i). So it only works with classes that have exactly one parameter in their constructor. It would not make sense for most 0 parameter (type-)classes.

The title of your question also seems to suggest that you think the compilation error is talking about type parameters of the type constructor, but I hope the above paragraph also makes clear that it is actually talking about value parameters of the value constructor.

For what (I think) you are trying to do, you will have to provide an implicit instance of CollectC yourself. I suggest putting it in the companion object of Collect. But you can choose an alternative solution if that fits your needs better.

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Generic[G[_]] {
  // Some function declarations go here
}

abstract class Collect[F[_],B,A] {
  def collect_ : A => F[B]
}

object Collect {
  implicit def mkCollectC[F[_],B]: CollectC[F,B] = new CollectC[F,B]
}

class CollectC[F[_],B] extends Generic[({type C[A] = Collect[F,B,A]})#C] {
  // Function definitions go here
}

// Exiting paste mode, now interpreting.

warning: there were four feature warnings; for details, enable `:setting -feature' or `:replay -feature'
defined trait Generic
defined class Collect
defined object Collect
defined class CollectC

scala> implicitly[Generic[({type C[X] = Collect[List,Int,X]})#C]]
res0: Generic[[X]Collect[[+A]List[A],Int,X]] = CollectC@12e8fb82
Community
  • 1
  • 1
Jasper-M
  • 12,460
  • 1
  • 19
  • 34