Can I implement a method from a trait using a case statement? - scala

I am trying to implement a method using a case statement, but the following code does not compile.
I am aware I can get this working by using a pattern match, but am curious as to why the case statement is incompatible as a direct implementation....
trait Calculation[Input, Result] {
def calculate(in: Input): Result
}
class CalculationImpl : Calculation[String, int] {
// missing parameter type for expanded function
// The argument types of an anonymous function must be fully known. (SLS 8.5)
def calculate = {
case "one" => 1
case "two" => 2
case s: String => 0
}
}
As a compromise, I could change the semantics of the trait so that calculate becomes a parameterless method which returns a Function1, rather than a method which takes an Input parameter and returns a Result. However, this is not ideal...
trait Calculation[Input, Result] {
def calculate: Input => Result // Works, but semantics have changed.
}
class CalculationImpl : Calculation[String, int] {
def calculate = {
case "one" => 1
case "two" => 2
case s: String => 0
}
}
(note: the above is pseudo-code - I have not tried compiling this exact code)

You just need to fix your syntax and it will work:
def calculate(s: String) = s match {
case "one" => 1
case "two" => 2
case s: String => 0
}

You can get closer to the original semantics and still cut the boilerplate by defining calculate as a function value:
trait Calculation[Input, Result] {
type F = Input => Result
val calculate: F
}
class CalculationImpl extends Calculation[String, Int] {
val calculate: F = {
case "one" => 1
case "two" => 2
case s: String => 0
}
}

Related

Can I avoid using asInstanceOf in a pattern match when matching a stateful subclass object?

As I was modelling expressions like Var("x") or Number(7) and writing an eval function with pattern matching, I ran into a case where I could not avoid using the ´asInstanceOf` method.
2 restrictions
I do not want to avoid pattern matching by declaring an eval method in Expr and define it in its subclasses (cf. Expression problem, I prefer pattern match).
I also do not want something like Var("x", 7).
sealed trait Expr
object Expr {
def eval(e: Expr): Int = e match {
case Number(n) => n
case Var(_) => e.asInstanceOf[Var].getValue()
}
}
case class Number(n: Int) extends Expr
case class Var(s: String) extends Expr {
var value = 0
def getValue(): Int = value
def updateValue(x: Int): Unit = {
this.value = x
}
}
val x = Var("x")
x.updateValue(1)
Expr.eval(x) // 1
When I define the second case like this: case Var(x) => Var(x).getValue(), i get Expr.eval(x) // 0. This is, because Var(x) on the right side will construct a fresh Var with value 0.
I'm ok with using asInstanceOf but in the sense of improvement, I wonder if there is a cleaner solution than using asInstanceOf, which I haven't found yet.
You can use # to bind a variable to a pattern. Use it like this:
def eval(e: Expr): Int = e match {
case Number(n) => n
case v#Var(_) => v.getValue()
}
You can also check the type of a variable in a pattern match
def eval(e: Expr): Int = e match {
case Number(n) => n
case v: Var => v.getValue()
}

Scala Generic method errors on parameter operation

I get "type mismatch; found : Int(1) required: String" error when I try to return the incremented value of the input parameter from a Scala generic method below.
I did try using the Case method for this but it did not work as well. Basically I want to decide the operation based on input Type to the method and return the calculated/modified value.
object GenericOperations {
// def increment[typ](val x:typ):typ = x match {
// case _:Int => x + 1
// case _:String => x + "x"
// }
def increment2(x:Any):Any = {
if(x.isInstanceOf[Int]) {
x+1
}
else if (x.isInstanceOf[String]) {
x + "x"
}
else {
println("No Match type")
}
}
}
I would rather use method overloading:
def increment2(x: Int) = x + 1
def increment2(x: String) = x + "x"
If you are sure you need exactly one function you may use match.
def increment2(x: Any): Any = x match {
case v: Int => v + 1
case v: String => v + "x"
case _ =>
throw new Exception("No Match type")
}
But returning Any isn't good thing as you cannot use its result without type cast
GenericOperations.increment2(3) + 3 // type mismatch
Still you may use the same match way:
def increment2[T](x: T): T = (x match {
case v: Int => v + 1
case v: String => v + "x"
case _ => throw new Exception("No Match type")
}) match {
case v: T => v
case _ => throw new Exception("Invalid increment expression result type")
}
As it has been mentioned in the comments there is also typeclass way:
//type class
trait Incrementable[T] {
def inc(x: T): T
}
//type class instance for String
implicit val incString = new Incrementable[String] {
def inc(x: String) = x + "x"
}
//type class instance for Int, single abstract method (SAM) form
implicit val incInt: Incrementable[Int] = (x: Int) => x + 1
def increment2[T: Incrementable](x: T): T = implicitly[Incrementable[T]].inc(x)
You have declared x to be of type Any. Which means you are only allowed to use the methods of Any.
You are calling x.+(1). There is no + method in Any. Therefore, you can't use +.
You should be getting an error about + not existing, but you don't, so what's happening here?
There is an implicit conversion for string concatenation, which can convert an arbitrary object into a String and then concatenate another String to it. In this case, it converts x to an any2stringadd and then tries to add 1 to it, but the any2stringadd.+ method only takes a String as its argument, and thus you get the strange error message that it is expecting a String.
You, however, are passing 1 as an argument, which is an Int not a String.
Note that any2stringadd is deprecated in Scala 2.13, so in the future you would just get an error about a non-existent method.
Note that you have tagged this question with generics and also talk about generics multiple times in the subject and the question body, yet there are no generics in your code. In fact, with generics, this problem would not exist.
See also
Scala Beginner trying to use Parameterised Type
Type parameter in scala
Maybe something like this , even though I still don't like it because the usage of getOrElse , but here you go anyway:
object GenericOperations {
// def increment[typ](val x:typ):typ = x match {
// case _:Int => x + 1
// case _:String => x + "x"
// }
def increment2(x:Any):Any = {
if(x.isInstanceOf[Int]) {
toInt(x).getOrElse(0)+1
}
else if (x.isInstanceOf[String]) {
x + "x"
}
else {
println("No Match type")
}
}
def toInt(x: Any): Option[Int] = x match {
case i: Int => Some(i)
case _ => None
}
}

Get value of an optional class field

Let's suppose I have a class Toto with two optional fields :
case class Toto(a : Option[Int], b: Option[Int])
And a class Titi with one optional Toto :
case class Titi(c : Option[Toto])
We create an instance of a class Titi :
val test = Titi(Some(Toto(Some(1),Some(2))))
Now I want to access the second field of Toto in Titi variable by supposing that Titi or b can be equal to None but this statement is impossible :
test.c.getOrElse("Something").b.getOrElse(0)
How do I proceed to do so ?
You should use flatMap:
test.c.flatMap(_.b).getOrElse(0)
In a case in any place in the hierarchy there is None 0 will be returned.
If you have even a deeper object hierarchy with properties returning Option you can chain flatMap:
test.c
.flatMap(_.b)
.flatMap(_.d)
//...etc
Scala also has also the special syntax for unwrapping deeply nested monadic types, called for comprehension:
val result = for {
c <- test.c
a <- c.a
} yield a
result.getOrElse(0)
Under the hood, it is compiled to similar code as chained flatMap.
This basically works like this:
If c is None to gets directly to getOrElse and returns 0
If it's Some then it checks b, if it's None it goes to getOrElse if not then value wrapped in Some is returned.
In case you would want to return something different distinguishing which Option is None, then I would just use match:
test.c match {
case Some(c) => c.getOrElse(0)
// if you return String in one branch and integer in other then inferred type would be Any!
case None => "Something"
}
Your val test is wrong, it should be this
val test = Titi(Some(Toto(Some(1),Some(2))))
Other thing, in the getOrElse, you have to put a type that makes sense
test.c.getOrElse(Toto(None,None))
you can achieve it by pattern matching
val test: Titi = Titi(Some(Toto(Some(1), None)))
val res = test.c match {
case Some(Toto(_, Some(x))) => x
case _ => 0
}
result:
0
val x = test match {
case Titi(x) => {
x match {
case Some(x) => {
x.b match {
case Some(z) => z
case None => 1
}
}
case None => 1
}
}
case _ => 1
} //> x : Int = 2
u can use fold
test.c.fold(0)(_.b.fold(0)(i => i))

Return a class from function in Scala

I need to instantiate a different class depending on a the value of a particular argument in my application. Something like:
class x { val z = 5}
class y { val z = 10}
def myClass(input:String): Class[_] = {
input match{
case "x" => x
case "y" => y
}
}
val a = new myClass("x")
Any ideas how to do this or if this is possible? It tried the above code and I get a type mismatch with the output so maybe it's as simple as specifying the output type correctly, which I'm pretty sure I'm not.
Thanks.
You should clarify what you're trying to accomplish because runtime reflection would allow you to do this, but 99.9% of the time it's the wrong way to solve your problems.
You can do something like:
trait A
case class B(value: String) extends A
case class C(value: Int) extends A
def doSomething(input: String): A = input match {
case "x" => B("Wahoo")
case "y" => C(123)
}
It's not clear what it is you are actually trying to do: on one hand, you are saying that you need to instantiate the class, on the other, your function is declared to return the Class itself, not an instance.
So, if you are trying to create an instance, something like this would work:
def myClass(input: String): AnyRef = input match {
case "x" => new x
case "y" => new y
}
Note: there are two problems with this function. First, it will throw an exception if input happens to be neither x nor y ... This is not very nice. It is a better idea to make it return an Option, and add a default to None, when input isn't matched:
def myClass(input: String): Option[_] = Option(input) collect {
case "x" => new x
case "y" => new y
}
Another problem is that you cannot do very much with the value returned by this function the way it is written, because you don't know what type it has, so, you can't access any of it's members (other than the common stuff like toString, equals etc.).
It may be a better idea to define a common trait like the other answer suggests, and narrow down the return type of the function to that trait:
trait Foo { def z: Int }
class x extends Foo { val z = 5 }
class y extends Foo { val z = 10 }
def myClass(input: String): Option[Foo] = Option(input) collect {
case "x" => new x
case "y" => new y
}
Finally, if what you wanted was to actually return the Class object, and not an instance, then you almost have it, except, you need to add a classOf keyword (it's still better to make the result optional, in case input does not match):
def myClass(input: String): Option[Class[_]] = Option(input) collect {
case "x" => classOf[x]
case "y" => classOf[y]
}
Or, better, with the type boundary:
def myClass(input: String): Option[Class[_ <: Foo]] = Option(input) collect {
case "x" => classOf[x]
case "y" => classOf[y]
}

how to check return value type in scala

I am pretty a fresh man to learn scala.
I want to ask how to check the function return value type?
For example :
def decode(list :List[(Int, String)]):List[String] = {
//val result = List[String]()
//list.map(l => outputCharWithTime(l._1,l._2,Nil))
//result
excuteDecode(list,List[String]())
def excuteDecode(list:List[(Int,String)],result:List[String]):List[String] = list match {
case Nil => Nil
case x::Nil=>outputCharWithTime(x._1,x._2,result)
case x::y =>excuteDecode(y,outputCharWithTime(x._1,x._2,result))
}
def outputCharWithTime(times:Int,str:String , result :List[String]):List[String]={
times match{
case 0 => result
case x => outputCharWithTime(times-1,str,str::result)
}
}
}
In this code , all the function return type is set to List[String], also created one empty List[String] parameter for excuteDecode() function .
However I get a compilation error:
Error:(128, 5) type mismatch;
found : Unit
required: List[String]
}
Anyone can tell me why there exist problem and how to check the actual return type by ourself ?
The order of statements matters here.
def decode(list :List[(Int, String)]):List[String] = {
def excuteDecode(list:List[(Int,String)],result:List[String]):List[String] = list match {
case Nil => Nil
case x::Nil=>outputCharWithTime(x._1,x._2,result)
case x::y =>excuteDecode(y,outputCharWithTime(x._1,x._2,result))
}
def outputCharWithTime(times:Int,str:String , result :List[String]):List[String]={
times match{
case 0 => result
case x => outputCharWithTime(times-1,str,str::result)
}
}
excuteDecode(list,List[String]()) // Moved here
}
In Scala, the last expression in a block defines, what the whole block returns; statements such as def are defined to produce a Unit (()).