Implicit conversion using an intermediary val and map/flatMap - scala

I'm having this implicit def to convert an Option[Int] to an Option[String]
implicit def optIntOptString(n: Option[Int]): Option[String] = Some(n.get.toString)
while this one works:
val optIntVal: Option[Int] = Some(3)
val converted: Option[String] = optIntVal // the 'optIntVal' which is of type Option[Int] gets converted implicitly to Option[String] due to the implicit def 'optIntOptString'
this doesn't:
val failConv: Option[String] = Some(3)
found : Int(3)
required: String
val failConv: Option[String] = Some(3)
I'm trying to do a conversion without using an intermediary val
My intention is to use the implicit conversion within a map/flatmap function, something like:
val someList: List[Int] = List(1, 2, 3)
someList.map((num:Int) => {
val z:Option[String] = Some(num)
z
})
But even with an intermediary val that holds the source type i get an error, seems like the implicit conversion isn't working within the map method:
found : Int
required: String
someList.map((num:Int) => { val z:Option[String] = Some(num); z })

Related

scala function with implicit parameters

I have scala function as below,
scala> def getOrders: (String, String) => Seq[String] = (user: String, apiToken: String) => Seq.empty[String]
def getOrders: (String, String) => Seq[String]
scala> getOrders("prayagupd", "A1B2C3")
val res0: Seq[String] = List()
I want to pass in a third parameter as a implicit parameter but it does not seem possible for a function.
Here's what I want achieved using a method,
scala> def getOrders(user: String, apiToken: String)(implicit clientType: String) = Seq.empty[String]
def getOrders
(user: String, apiToken: String)(implicit clientType: String): Seq[String]
scala> implicit val clientType: String = "android"
implicit val clientType: String = "android"
scala> getOrders("prayagupd", "A1B2C3")
val res2: Seq[String] = List()
It does not seem possible because of the fact that apply function is predefined, which won't extra accept implicit parameter.
scala> new Function2[String, String, Seq[String]] {
def apply(user: String, apiToken: String): Seq[String] = Seq.empty
}
val res4: (String, String) => Seq[String] = <function2>
Overloadding does not do the trick either,
scala> new Function2[String, String, Seq[String]] {
def apply(user: String, apiToken: String): Seq[String] = Seq.empty
def apply(user: String, apiToken: String)(implicit clientType: String) = Seq("order1")
}
val res9: (String, String) => Seq[String] = <function2>
scala> implicit val clientType: String = "device"
implicit val clientType: String = "device"
scala> res9("prayagupd", "apiToken")
val res10: Seq[String] = List()
Is it that implicits are not recommended at all for functions or I'm missing something?
Experimental, your function might be expressed as follows without the implicit:
scala> def getOrders: (String, String) => (String) => Seq[String] = (user: String, apiToken: String) => (clientType: String) => Seq.empty[String]
def getOrders: (String, String) => String => Seq[String]
Poking around on that... it doesn't like implicit anywhere in there that might give you want you want.
An answer to a related question suggests the reason: getOrders "... is a method, not a function, and eta-expansion (which converts methods to functions) is not attempted until after implicit application." It seems that implicits are resolved at a method level, not a function level.

How to use ConcurrentHashMap computeIfAbsent() in Scala

I'm using a ConcurrentHashMap in Scala and I would like to use the computeIfAbsent() method but can't figure out the syntax for the second argument. Can someone show me what would be the proper syntax?
When running the following code
val data = new ConcurrentHashMap[String, LongAdder]
data.computeIfAbsent("bob", k: String => new LongAdder()).increment()
I'm getting the following error
Type mismatch, expected: Function[_ >: String, _ <: LongAdder], actual: (String) => Any
Thanking you in advance
Francis
The problem is that you're using java.util.concurrent.ConcurrentHashMap, which accepts java.util.function.Function as a parameter for computeIfAbsent() instead of scala.Function1 which you pass to it.
Since scala doesn't support lambda conversion for functional interfaces as Java does (at least not without the -Xexperimental flag), you can solve this by implementing a java.util.function.Function explicitly:
val data = new ConcurrentHashMap[String, LongAdder]
val adderSupplier = new java.util.function.Function[String, LongAdder]() {
override def apply(t: String): LongAdder = new LongAdder()
}
data.computeIfAbsent("bob", adderSupplier).increment()
Alternatively, if you need this more often, you may write a utility conversion function or even an implicit conversion:
object FunctionConverter {
implicit def scalaFunctionToJava[From, To](function: (From) => To): java.util.function.Function[From, To] = {
new java.util.function.Function[From, To] {
override def apply(input: From): To = function(input)
}
}
}
import FunctionConverter._
val data = new ConcurrentHashMap[String, LongAdder]()
data.computeIfAbsent("bob", (k: String) => new LongAdder()) // <- implicit conversion applied here
If you enable -Xexperimental flag you can use scala anonymous function notation for this:
scala> val data = new java.util.concurrent.ConcurrentHashMap[String, Int]
data: java.util.concurrent.ConcurrentHashMap[String,Int] = {}
scala> data.computeIfAbsent("bob", _.size)
res0: Int = 3
Note that you still can't pass regular scala Function
scala> val f: String => Int = _.size
f: String => Int = <function1>
scala> data.computeIfAbsent("bob", f)
<console>:13: error: type mismatch;
found : String => Int
required: java.util.function.Function[_ >: String, _ <: Int]
data.computeIfAbsent("bob", f)
^
But eta-expansion will work
scala> def a(s: String): Int = s.size
a: (s: String)Int
scala> data.computeIfAbsent("bob", a)
res3: Int = 3

no type parameters for method flatMap

I don't know why the following code can not compile, this is the error message:
Error:(29, 7) no type parameters for method flatMap: (f: String => Option[B])Option[B] exist so that it can be applied to arguments (String => Some[Class[?0]] forSome { type ?0 <: org.apache.hadoop.io.compress.CompressionCodec })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : String => Some[Class[?0]] forSome { type ?0 <: org.apache.hadoop.io.compress.CompressionCodec }
required: String => Option[?B]
a.flatMap(codecClassName => {
^
and code
def f(a: Option[String]): Unit = {
a.flatMap(codecClassName => {
val codecFactory = new CompressionCodecFactory(new Configuration())
val codecClass = codecFactory.getCodecClassByName(codecClassName)
if (codecClass == null) {
throw new RuntimeException("Unknown or not supported codec:" + codecClassName)
}
Some(codecClass)
})
}
This seems to be related to the fact that getClass and classOf are not returning the exact same thing. See Scala equivalent of Java java.lang.Class<T> Object for more details.
Looking around for a workaround I came across Scala Getting class type from string representation.
So how about:
val codecClass = Manifest.classType(codecFactory.getCodecClassByName(codecClassName))
This should work. flatMap involves both map and flatten, so it may need more type annotations in some cases. The overall code works after annotation of the function parameter, i.e. (codecClassName: String).
Note the other change -- that flatMap with an inner function returning an Option type is the same as a map if that function returns what's inside the Option (i.e. flattens the option) (see below).
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.io.compress.{CompressionCodec, CompressionCodecFactory}
...
def f(a: Option[String]): Option[Class[_ <: CompressionCodec]] = {
a.map((codecClassName: String) => {
val codecFactory = new CompressionCodecFactory(new Configuration())
val codecClass = codecFactory.getCodecClassByName(codecClassName)
if (codecClass == null) {
throw new RuntimeException("Unknown or not supported codec:" + codecClassName)
}
codecClass
})
}
To show the relationship between flatMap and map as described above:
scala> val opt: Option[Int] = Some(1)
opt: Option[Int] = Some(1)
scala> opt.map((i: Int) => i + 1)
res0: Option[Int] = Some(2)
scala> val opt2: Option[Int] = None
opt2: Option[Int] = None
scala> opt.flatMap((i: Int) => Some(i + 1))
res1: Option[Int] = Some(2)
scala> opt2.map((i: Int) => i + 1)
res3: Option[Int] = None
scala> opt2.flatMap((i: Int) => Some(i + 1))
res2: Option[Int] = None

How to lazy evaluate functions in a sequence in Scala

Let's say I have three functions in a class that can return a Option[File]. I am looking for a nice way to execute them in order and stop when the first function evaluates to a Some (similar to what pick() does but with lazy evaluation), i.e. I don't want to use nested if-else statements.
abstract class Foo() {
def first(): Option[File]
def second(): Option[File]
def third(): Option[File]
def pick(): Option[File] = {
Seq(first(), second(), third()).find(_.isDefined).map(_.get)
)
}
The parameter to Option.orElse is passed by name, so you can just use that to chain your methods:
def pick(): Option[File] = first orElse second orElse third
scala> def f1:Option[Int] = {println("1"); None}
f1: Option[Int]
scala> def f2:Option[Int] = {println("2"); Some(2)}
f2: Option[Int]
scala> def f3:Option[Int] = {println("3"); Some(3)}
f3: Option[Int]
scala> f1 orElse f2 orElse f3
1
2
res0: Option[Int] = Some(2)

String to Int in Scala

Suppose I need to convert a String to Int in Scala. If the string is not a number I would like to return None rather than throw an exception.
I found the following solution
def toMaybeInt(s:String) = {
import scala.util.control.Exception._
catching(classOf[NumberFormatException]) opt s.toInt
}
Does it make sense ? Would you change/improve it ?
I'd use scala.util.Try which returns Success or Failure for a computation that may throw an exception.
scala> val zero = "0"
zero: String = 0
scala> val foo = "foo"
foo: String = foo
scala> scala.util.Try(zero.toInt)
res5: scala.util.Try[Int] = Success(0)
scala> scala.util.Try(foo.toInt)
res6: scala.util.Try[Int] = Failure(java.lang.NumberFormatException: For input string: "foo")
So, toMaybeInt(s: String) becomes:
def toMaybeInt(s:String) = {
scala.util.Try(s.toInt)
}
For getting an option in any case, regardless of possible exceptions due to number malformation,
import scala.util.Try
def toOptInt(s:String) = Try(s.toInt) toOption
Then
scala> toOptInt("123")
res2: Option[Int] = Some(123)
scala> toOptInt("1a23")
res3: Option[Int] = None
Further, consider
implicit class convertToOptInt(val s: String) extends AnyVal {
def toOptInt() = Try(s.toInt) toOption
}
Hence
scala> "123".toOptInt
res5: Option[Int] = Some(123)
scala> "1a23".toOptInt
res6: Option[Int] = None