0

I would like to have some clarity on custom string interpolation in Scala. I am looking at the example from this page -> https://www.scala-lang.org/api/2.12.6/scala/StringContext.html

If I understand correctly, the compiler interprets any string preceded by any literal (without space) differently. E.g: s"abc"

This seems to be translated to an instantiation of the case class - StringContext by compiler. This case class takes >=1 strings as arguments.

When we have literals preceded by other than s, f or raw, that becomes a custom interpolator and we need to have a function for implicit conversion.

In the example given in the above link, the implicit class JsonHelper - takes a StringContext value. In the call: val x: JSONObject = json"{ a: $a }", I am not getting how { a: $a } is made into a StringContext and how it is used in the function definition.

Can someone please help me.

Thanks!

smac89
  • 39,374
  • 15
  • 132
  • 179
user3103957
  • 636
  • 4
  • 16
  • [Here](https://github.com/pathikrit/dijon/blob/181b6ac29f179ad685f564b996d8738d1a5bd375/src/main/scala/com/github/pathikrit/dijon/package.scala#L144-L146) is a working example from the Dijon parser - the string is build and parsed in run-time to get the AST tree. – Andriy Plokhotnyuk Dec 02 '19 at 08:31

2 Answers2

3

The compiler finds the parts to be interpolated in "{ a: $a }" (that would be $a) and splits the string except those parts into "{ a: " and " }", which are passed to StringContext:

StringContext("{ a: ", " }")

Then the json method is invoked with the parts found in the first step:

StringContext("{ a: ", " }").json(a)

This logic doesn't care whether the interpolator is custom or not; the f, s and raw are just already defined on StringContext.

So then your def json needs to use sc.parts (Seq("{ a: ", " }") in this case) and args (Seq(a)) to build the result you want.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thanks @Alexey Romanov!; much helpful. I actually figured this out and posted as response in the previous answer. – user3103957 Dec 02 '19 at 09:43
2

It is not the string that is converted to StringContext. Notice the definition of the implicit class and what it is doing:

implicit class JsonHelper(private val sc: StringContext) extends AnyVal {
  def json(args: Any*): JSONObject = ...
}

You see that all this class is doing is just adding a new behaviour to StringContext. Since StringContext is the mechanism for creating special string interpolation such as seen with s (The simple string interpolator), this class is just adding its own interpolator called json.

In the usage, val x: JSONObject = json"{ a: $a }", scala searches for the json interpolator and finds the definition in JsonHelper because JsonHelper has added this behaviour to StringContext via the implicit definition.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • Thanks @smac89! As you say, it is adding new behavior, then it needs to be inheriting StringContext. But JsonHelper is just taking a value of the type StringContext; but there is no inheritance. I am not quite sure how "{ a: $a }" is converted into StringContext. – user3103957 Dec 02 '19 at 03:22
  • 1
    @user3103957 I think you may be unclear as to how implicit conversions work in scala. There is no need for inheritance. The `implicit` keyword is all that is needed to add this new behaviour. Reading this stackoverflow [question](https://stackoverflow.com/questions/2861501/can-someone-explain-me-implicit-conversions-in-scala) might help to unveil some answers – smac89 Dec 02 '19 at 03:27
  • thanks for the link! Though I have a fair idea of implicit conversion, string interpolation is little different than the generic concept of implicit conversion. Compiler does non-standard task on top of standard activity when it comes to string interpolation. I looked at the "Advanced Usage" section in this link -> https://docs.scala-lang.org/overviews/core/string-interpolation.html. This explains the non-standard task done by the compiler. Actually my confusion was on these non-standard item and now it clears the confusion. – user3103957 Dec 02 '19 at 07:39
  • 1
    Generally while calling a function in implicit conversion (the function does not exist in the invoking object), we supply the arguments to the function. The compiler takes care of converting the object (on which the function is invoked). But in string interpolation, the compiler not only does the object conversion; but also suppplies the arguments to the function. This is the additional task. The link explains this well. Thank you very much for your help. I appreciate your valuable time. Thanks! – user3103957 Dec 02 '19 at 07:39