1

I am converting a large code which is concatenating string and enum values. The code was using Int as the enum type and I want to convert it to proper enums (preferably extends Enumeration, but trait + objects would do as well). I want the compiler either to perform the conversion from enum to a numeric value, or at least to give me a compiler error whenever I attempt to add an enum value to a string.

In spite of my effort following code still prints DEPTH=D0 because of the annoying default Scala String operator +:

import scala.language.implicitConversions

object Main extends App {
  implicit class StringPlus(s: String) {
    def + (v: Enumeration#Value): String = s + v.id.toString
  }
  implicit def str(v: Enumeration#Value): String = v.id.toString

  val b = true
  object D extends Enumeration {
    val D0 = Value(100)
  }
  val d = D.D0
  val s = "DEPTH=" + d
  println(s)
}

Is there a way to disable the default string + so that the situation above is handled or detected by the compiler? I am currently using Scala 2.12, but I can port to 2.13 if necessary.

Suma
  • 33,181
  • 16
  • 123
  • 191
  • 4
    The compiler will never go to an implicit if it doesn't have to. Better/easier to choose a different method name: `++()`. – jwvh Jan 10 '20 at 21:16
  • Yes, that would obviously work, however the code is a result of automated conversion from Javascript and it would be preferable not to have to manually adjust it, as any manual manipulation factor increases the risk of an error being introduced. – Suma Jan 10 '20 at 21:22
  • 3
    You can disable Predef and use your own String, but that sounds draconian. – som-snytt Jan 10 '20 at 21:23
  • Can I? Based on https://stackoverflow.com/questions/2664274/how-to-unimport-string-operator-in-scala I though I can prevent any + string, but not string + any. – Suma Jan 10 '20 at 21:24
  • 3
    Actually you can redefine String but the compiler still knows what a string literal is. You could use a custom interpolator `s""` instead of `""` to return your custom string thing. I had an experiment that made `""` syntax for `apply""` which would have given you the syntax above. – som-snytt Jan 10 '20 at 22:02
  • Yes, custom interpolator works as well, as it can return a type different from String and providing its own + is easy. The drawback is this still requires the source modification, but sometimes it might be preferable. I could require this "wrapped string" by the code consuming the result, detecting this way if the plain string was used instead. – Suma Jan 10 '20 at 22:07
  • 1
    Now I see how useful the applylator (apply interpolator) could have been. You could also write a Scalafix rule to convert plain str literals to interpolated. Oh well. At least you got a real answer. – som-snytt Jan 10 '20 at 22:09

2 Answers2

4

Consider ADT with overriden toString like so

sealed abstract class D(val value: Int) {
  override def toString: String = value.toString
}
case object D0 extends D(100)
case object D1 extends D(200)

val d = D0
val s = "DEPTH=" + d

which outputs

s: String = DEPTH=100
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
1

As Mario has shown, it is much easier to override toString than to change the built-in String +. The same approach can be used for Enumeration when necessary:

  abstract class MyEnumeration extends Enumeration {
    class MyVal(i: Int) extends Val(i, null) {
      override def toString = i.toString
    }
    def MyValue(i: Int) = new MyVal(i)
  }

  object D extends MyEnumeration {
    val D0 = MyValue(100)
  }

Suma
  • 33,181
  • 16
  • 123
  • 191