scala string interpolation with a variable length - scala

I use scala f string interpolator as follows:
def format(id: Int) = f"A$id%04d"
format(21) // A0021
However, I would like to be able to define a length once and for all (before fixed to 4), and get a function that it is going to format the string with that length.
So, instead of having
def format(length: Int, id: Int) = ???
f(5, 21) // A00021
I would like to have this:
def format(length: Int)(id: Int) = ???
val f = format(5)
f(21) // A00021
How can I implement this using scala f interpolator or other?
Update
I was not looking for such a solution involving the compiler at runtime, but I appreciate som-snytt's answer. Here there is a working solution based on his answer:
import scala.tools.reflect._,scala.reflect.runtime._,universe._
def defFormat(length: Int): Int => String = {
val code = raw"""(i: Int) => f"A$$i%0${length}d""""
tb.eval(tb.parse(code)).asInstanceOf[Int => String]
}
val format = defFormat(length = 5)
format(21)

scala> def format(n: Int)(i: Int) =
| f"A%%0${n}d" format i
format: (n: Int)(i: Int)String
scala> format(5) _
res0: Int => String = <function1>
scala> .apply(21)
res1: String = A00021
Edit:
scala> import scala.tools.reflect._,scala.reflect.runtime._,universe._
import scala.tools.reflect._
import scala.reflect.runtime._
import universe._
scala> val tb = currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl#2d10e0b1
scala> def f(n: Int)(i: Int): String = {
| val code = raw"""f"A$${$i}%0${n}d""""
| tb.eval(tb.parse(code)).asInstanceOf[String]
| }
f: (n: Int)(i: Int)String
scala> val g = f(5) _
g: Int => String = <function1>
scala> g(21)
res9: String = A00021
That doesn't actually help much. You really want to
scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
which throws if the format is bad.
scala> val code = raw"""(i: Int) => f"A$${i}%k0${10}d""""
code: String = (i: Int) => f"A${i}%k010d"
scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:170)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:148)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:121)
at scala.reflect.internal.Trees$class.wrappingIntoTerm(Trees.scala:1716)
at scala.reflect.internal.SymbolTable.wrappingIntoTerm(SymbolTable.scala:16)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.withWrapping$1(ToolBoxFactory.scala:120)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.transformDuringTyper(ToolBoxFactory.scala:121)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.typecheck(ToolBoxFactory.scala:169)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:375)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:367)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.liftedTree2$1(ToolBoxFactory.scala:355)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:355)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:367)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:27)
... 32 elided
scala> val code = raw"""(i: Int) => f"A$${i}%0${10}d""""
code: String = (i: Int) => f"A${i}%010d"
scala> tb.typecheck(tb.parse(code))
res19: tb.u.Tree =
((i: Int) => ({
val arg$macro$9: Int = i;
new scala.collection.immutable.StringOps("A%010d").format(arg$macro$9)
}: String))

You can't do it using f because its whole point is to make sure it can check the format string for type errors, so the format string has to be static. f could support this scenario explicitly, but it doesn't.
You could make format a macro, but this seems like an overkill. Not to mention that it would have to be defined in a separate module, which looks very inconvenient for this scenario.

Related

How to get the type of a function in Scala source [duplicate]

In the REPL there's a command to print the type:
scala> val s = "House"
scala> import scala.reflect.runtime.universe._
scala> val li = typeOf[List[Int]]
scala> :type s
String
scala> :type li
reflect.runtime.universe.Type
How can I get this ":type expr" functionality in my Scala program to print types?
Let me clarify the ":type expr" functionality I would like to have, something like this:
println(s.colonType) // String
println(li.colonType) // reflect.runtime.universe.Type
How can I get such a "colonType" method in my Scala program outside the REPL (where I don't have the :type command available)?
I looked into the sources of the REPL (or better of scalac which contains the REPL) and found this (Source):
private def replInfo(sym: Symbol) = {
sym.info match {
case NullaryMethodType(restpe) if sym.isAccessor => restpe
case info => info
}
}
Method info of scala.reflect.internal.Symbols.Symbol returns a Type (Source). Later toString is called on this type, therefore you should do the same if you want the same type information:
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> u.typeOf[List[String]].toString
res26: String = scala.List[String]
scala> u.typeOf[Int => String].toString
res27: String = Int => String
The following implicit conversion should do the trick for you:
import reflect.runtime.universe._
implicit class ColonTypeExtender [T : TypeTag] (x : T) {
def colonType = typeOf[T].toString
}
Is that what you'd like to achieve?
val s = "House"
println(s.getClass.getName) // java.lang.String
println(s.getClass.getSimpleName) // String
Tested with Scala REPL 2.11 :
Add _ after function name to treat it as partially applied function.
Example :
scala> def addWithSyntaxSugar(x: Int) = (y:Int) => x + y
addWithSyntaxSugar: (x: Int)Int => Int
scala> addWithSyntaxSugar _
res0: Int => (Int => Int) = <function1>
scala>

Scala Double error when compiling in SPARK-REPL

I am learning Scala at the moment
<pre>
scala> val sample = similarities.filter(m => {
| val movies = m._1
| (movieNames(movies._1).contains("Star Wars (1977)"))
| })
</pre>
sample: org.apache.spark.rdd.RDD[((Int, Int), Double)] = FilteredRDD[25] at filter at :36
The sample compiled just fine
But when I tried to call sample again in the next command
<pre>
scala> val result = sample.map(v => {
| val m1 = v._1._1
| val m2 = v._1._2
| val correl = v._2._1
| //val rcorr = v._2._2
| // val cos = v._2._3
| //val j = v._2._4
| (movieNames(m1), movieNames(m2), correl)
| })
<console>:41: error: value _1 is not a member of Double
val correl = v._2._1
</pre>
Can someone please help me.Thanks in advance
Given the amount of indexing on the composed tuple, consider wrapping ((Int, Int), Double) onto a case class and defining an implicit on it as follows,
case class Movie(m1: Int, m2: Int, correl: Double)
implicit def RichMovie(v: ((Int,Int),Double) ) = Movie(v._1._1, v._1._2, v._2)
Thus, given an instance of a composed tuple
scala> val m = ( (1,2), 3.5)
m: ((Int, Int), Double) = ((1,2),3.5)
we can access its members as follows,
scala> m.m1
res0: Int = 1
scala> m.m2
res1: Int = 2
scala> m.correl
res2: Double = 3.5
val correl = v._2._1
should be just
val correl = v._2
because it is part of the second element in the tuple ((Int, Int), Double)

Convert Any to Double using asInstanceOf?

Is there a supported way to achieve a conversion of any numeric type to a double. E.g.
val i = 12345
val f = 1234.5F
val d = 1234.5D
val arr = Array[Any](i,f,d)
val anotherD = arr(0).asInstanceOf[Numeric].toDouble
Naturally the above code is not correct as given - since Numeric requires Type arguments.
scala> val i = 12345
i: Int = 12345
scala> val f = 1234.5F
f: Float = 1234.5
scala> val d = 1234.5D
d: Double = 1234.5
scala> val arr = Array[Any](i,f,d)
arr: Array[Any] = Array(12345, 1234.5, 1234.5)
scala> val anotherD = arr(0).asInstanceOf[Numeric].toDouble
<console>:11: error: type Numeric takes type parameters
val anotherD = arr(0).asInstanceOf[Numeric].toDouble
Now I realize the above may be achieved via match/case , along the following lines:
(a, e) match {
case (a : Double, e : Double) =>
Math.abs(a - e) <= CompareTol
case (a : Float, e : Float) =>
Math.abs(a - e) <= CompareTol
.. etc
But I was wondering if there were a means to more compactly express the operation. This code is within TEST classes and efficiency is not an important criterion. Specifically: reflection calls are OK. Thanks.
I assume you are on the JVM. The Number class does like what you want to achieve with the doubleValue method:
val arr = Array[Number](i,f,d)
val ds = arr.map(_.doubleValue())
This is horrible, and probably not efficient, but it works (on your example) :p
scala> import scala.language.reflectiveCalls
import scala.language.reflectiveCalls
scala> arr.map(_.asInstanceOf[{ def toDouble: Double }].toDouble)
res2: Array[Double] = Array(12345.0, 1234.5, 1234.5)

Scala: convert string to Int or None

I am trying to get a number out of an xml field
...
<Quantity>12</Quantity>
...
via
Some((recipe \ "Main" \ "Quantity").text.toInt)
Sometimes there may not be a value in the xml, though. The text will be "" and this throws an java.lang.NumberFormatException.
What is a clean way to get either an Int or a None?
scala> import scala.util.Try
import scala.util.Try
scala> def tryToInt( s: String ) = Try(s.toInt).toOption
tryToInt: (s: String)Option[Int]
scala> tryToInt("123")
res0: Option[Int] = Some(123)
scala> tryToInt("")
res1: Option[Int] = None
Scala 2.13 introduced String::toIntOption:
"5".toIntOption // Option[Int] = Some(5)
"abc".toIntOption // Option[Int] = None
"abc".toIntOption.getOrElse(-1) // Int = -1
More of a side note on usage following accepted answer. After import scala.util.Try, consider
implicit class RichOptionConvert(val s: String) extends AnyVal {
def toOptInt() = Try (s.toInt) toOption
}
or similarly but in a bit more elaborated form that addresses only the relevant exception in converting onto integral values, after import java.lang.NumberFormatException,
implicit class RichOptionConvert(val s: String) extends AnyVal {
def toOptInt() =
try {
Some(s.toInt)
} catch {
case e: NumberFormatException => None
}
}
Thus,
"123".toOptInt
res: Option[Int] = Some(123)
Array(4,5,6).mkString.toOptInt
res: Option[Int] = Some(456)
"nan".toInt
res: Option[Int] = None
Here's another way of doing this that doesn't require writing your own function and which can also be used to lift to Either.
scala> import util.control.Exception._
import util.control.Exception._
scala> allCatch.opt { "42".toInt }
res0: Option[Int] = Some(42)
scala> allCatch.opt { "answer".toInt }
res1: Option[Int] = None
scala> allCatch.either { "42".toInt }
res3: scala.util.Either[Throwable,Int] = Right(42)
(A nice blog post on the subject.)

Using lazy evaluation functions in varargs

What is wrong is the following method?
def someMethod(funcs: => Option[String]*) = {
...
}
That actually "works" under 2.7.7 if you add parens:
scala> def someMethod(funcs: => (Option[String]*)) = funcs
someMethod: (=> Option[String]*)Option[String]*
except it doesn't actually work at runtime:
scala> someMethod(Some("Fish"),None)
scala.MatchError: Some(Fish)
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136)
at .someMethod(<console>:4)
at .<init>(<console>:6)
at .<clinit>(<console>) ...
In 2.8 it refuses to let you specify X* as the output of any function or by-name parameter, even though you can specify it as an input (this is r21230, post-Beta 1):
scala> var f: (Option[Int]*) => Int = _
f: (Option[Int]*) => Int = null
scala> var f: (Option[Int]*) => (Option[Int]*) = _
<console>:1: error: no * parameter type allowed here
var f: (Option[Int]*) => (Option[Int]*) = _
But if you try to convert from a method, it works:
scala> def m(oi: Option[Int]*) = oi
m: (oi: Option[Int]*)Option[Int]*
scala> var f = (m _)
f: (Option[Int]*) => Option[Int]* = <function1>
scala> f(Some(1),None)
res0: Option[Int]* = WrappedArray(Some(1), None)
So it's not entirely consistent.
In any case, you can possibly achieve what you want by passing in an Array and then sending that array to something that takes repeated arguments:
scala> def aMethod(os: Option[String]*) { os.foreach(println) }
aMethod: (os: Option[String]*)Unit
scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) }
someMethod: (funcs: => Array[Option[String]])Unit
scala> someMethod(Array(Some("Hello"),Some("there"),None))
Some(Hello)
Some(there)
None
If you really want to (easily) pass a bunch of lazily evaluated arguments, then you need a little bit of infrastructure that as far as I know doesn't nicely exist in the library (this is code for 2.8; view it as inspiration for a similar strategy in 2.7):
class Lazy[+T](t: () => T, lt: Lazy[T]) {
val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params)
def ~[S >: T](s: => S) = new Lazy[S](s _,this)
}
object Lz extends Lazy[Nothing](null,null) {
implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray
}
Now you can easily create a bunch of parameters that are lazily evaluated:
scala> import Lz._ // To get implicit def
import Lz._
scala> def lazyAdder(ff: Array[()=>Int]) = {
| println("I'm adding now!");
| (0 /: ff){(n,f) => n+f()}
| }
lazyAdder: (ff: Array[() => Int])Int
scala> def yelp = { println("You evaluated me!"); 5 }
yelp: Int
scala> val a = 3
a: Int = 3
scala> var b = 7
b: Int = 7
scala> lazyAdder( Lz ~ yelp ~ (a+b) )
I'm adding now!
You evaluated me!
res0: Int = 15
scala> val plist = Lz ~ yelp ~ (a+b)
plist: Lazy[Int] = Lazy#1ee1775
scala> b = 1
b: Int = 1
scala> lazyAdder(plist)
I'm adding now!
You evaluated me!
res1: Int = 9
Evidently repeated arguments are not available for by-name parameters.