2
votes

I'm new to Scala programming, so please bear with me if it's a silly question. I was expecting the following code to work, since a has a mkString method.

val a = "abc".toCharArray

case class A[T: {def mkString(): String}](i: T) {
  val s = i.mkString
}

A(a)

But it gives the following error:

Error:(3, 16) AnyRef{def mkString(): String} does not take type parameters case class A[T: {def mkString(): String}](i: T)

Error:(4, 14) value mkString is not a member of type parameter T val s = i.mkString

I'm using Scala 2.11.6.

Any help will be really appreciated! Thank you !

2

2 Answers

1
votes

At least three mistakes there:

  1. It should be <:, not :. The structural type is not a typeclass.
  2. def mkString(): String is not the same as def mkString: String.
  3. raw JVM arrays know nothing about convenient scala methods like mkString. You need a WrappedArray for that.

This here works:

import scala.language.reflectiveCalls
val a = "abc".toCharArray

import collection.mutable.WrappedArray

case class A[T <: { def mkString: String }](i: T){
  val s = i.mkString
}

A(a: WrappedArray[Char])
2
votes

The type bounds [A : B] is a shorthand way of saying there exists an instance of the typeclass B for type A, so these are equivalent:

def foo[A : B]: C

def foo[A](implicit b: B[A]): C

The syntax you're probably looking for is [A <: B], which means A must be a subclass of B.

But then you have the issue that mkString isn't actually in the Array class; it's defined in ArrayOps (with an implicit conversion from Array) as described in the documentation:

This member is added by an implicit conversion from Array[T] to ArrayOps[T] performed by method genericArrayOps in scala.Predef.

You can work around that with:

val a = "abc".toCharArray

case class A[T](i: T)(implicit conv: T => {def mkString: String}) {
  val s = conv(i).mkString
}

A(a)