3

Scala borrows Java String methods like toUpperCase/toLowerCase.

However, the way it does so is not very consistent:

  • Scala on JVM stick close to Java semantics, thus:
    • toUpperCase() is locale-sensitive and sticks to default locale (giving you infamous i → İ problem in Turkish locale)
    • to avoid that and keep locale-insensitive (en_US / C-like) process, you need to specifically do toUpperCase(Locale.ROOT)
  • Scala.JS does not implement a concept of Locale, thus:
    • toUpperCase() works in locale-insensitive manner
    • toUpperCase(Locale locale) method is effectively not available in ScalaJS

How do I implement locale-insensitive case conversion that will work in Scala on both JVM/JS?

I can think of several ways, all of them as ugly:

Method 1: My own implementation

Implement my own toUpperCase for specifically 26 ASCII characters of English alphabet.

Method 1.1: My own implementation using Scala chars

Basically the same, but at least reuse Scala toUpper to convert individual chars.

Method 2: Interface

Implement something like

trait CaseChangeOps {
  def toUpperCase(s: String): String
}

object Main {
  var caseChanger: CaseChanger
}

// whenever I want to use it, do it like that:
Main.caseChanger.toUpperCase("like this") // => "LIKE THIS"

in shared code, and then in JS have:

object CaseChangerJs {
  def toUpperCase(s: String): String = s.toUpperCase
}

object MainJs {
  Main.caseChanger = CaseChangerJs
}

... and in JVM:

object CaseChangerJvm {
  def toUpperCase(s: String): String = s.toUpperCase(Locale.ROOT)
}

object MainJvm {
  Main.caseChanger = CaseChangerJvm
}

Method 3: bring external scala-java-locales

There is a distinct 3rd party library scala-java-locales, which is listed as ScalaJS-compatible, and can be used to augument ScalaJS.

Looks like a massive overkill, though, as I literally only need locale-insensitive case conversions, not the whole thing for all possible locales.

Any better ideas?

GreyCat
  • 16,622
  • 18
  • 74
  • 112
  • 3
    Should the title be "Scala vs Scala.js"? Java isn't really relevant here. – Tim Mar 02 '20 at 10:24
  • @Tim: Actually, I believe the point the OP is making is that methods of the Scala Standard Library seem to behave differently on different platforms, which they should not. The reason for this is, of course, the fact that large parts of the Scala Standard Library are implemented in terms of Java (or are in fact not even implemented at all, but are just taken from Java). This works fine *as long as you are running on Java*. Which, however, is not necessarily the case: we have Scala.js and Scala-native, we used to have Scala.NET. We have Scala on Android. We'd like to have Scala on iOS. – Jörg W Mittag Mar 02 '20 at 18:33
  • So, I guess, the question title should rather be something like: "What exact behavior of `toUpperCase` is guaranteed by the Scala Language Specification"? To which the answer, sadly is "None whatsoever." – Jörg W Mittag Mar 02 '20 at 18:34

1 Answers1

9

The standard approach is close to your method 2, but much simpler. In shared code you just call

Platform.toUpperLocaleInsensitive(string)

which has different implementations on JVM and JS:

// JVM
object Platform {
  def toUpperLocaleInsensitive(s: String) = s.toUpperCase(Locale.ROOT)

  // other methods with different implementations
}

// JS
object Platform {
  def toUpperLocaleInsensitive(s: String) = s.toUpperCase()

  // other methods with different implementations
}

See the description of a similar case in Hands-on Scala.js.

This works because shared code doesn't need to compile by itself, only together with platform-specific code.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487