Why can't I reuse "unapply" without repeating the method signature - scala

The following Scala code compiles fine:
val f = (input: String) => Some("result")
object Extract {
def unapply(input: String): Option[String] = f(input)
}
val Extract(result) = "a string"
But if I replace the extractor by:
object Extract {
def unapply = f
}
Then compilation fails with:
error: an unapply result must have a member `def isEmpty: Boolean
val Extract(result) = "a string"
^
Why? Where does def isEmpty: Boolean come from?

In Scala 2.10 (and before) unapply had to always return an Option or a Boolean. Since 2.11, it can return any type, so far as it has def isEmpty: Boolean and def get: <some type> methods (like Option does). See https://hseeberger.wordpress.com/2013/10/04/name-based-extractors-in-scala-2-11/ for an explanation why it's useful.
But your unapply returns a String => Some[String], which doesn't have either, and that's what the error says.

To answer your first question - isEmpty comes from internals of Option type.
def unapply = f means - create a parameterless method that returns a function. This is not a method by itself and thus you have an error.
You can further read about difference between function and method in Scala there: Difference between method and function in Scala

Related

I broke Scala type safety somehow

Can some explain to me how I was able to create a case class with the wrong type? Bellow the worksheet output:
case class OptObj(os: Option[String], s: String)
//defined class OptObj
val osCrazy: Option[Any] = Option(Option(Option(1)))
//osCrazy: Option[Any] = Some(Some(Some(1)))
def anyOpt: Option[Any] = osCrazy
//anyOpt: Option[Any]
def getOpt[T]: Option[T] = anyOpt.map(_.asInstanceOf[T])
//getOpt: [T]=> Option[T]
val o = OptObj(getOpt[String], "It has the wrong type inside, but was built!")
//o: OptObj = OptObj(Some(Some(Some(1))),It has the wrong type inside, but was built)
o == OptObj(None,"")
//res0: Boolean = false
o == o
//res1: Boolean = true
o.os
//res2: Option[String] = Some(Some(Some(1)))
o.os.get
//java.lang.ClassCastException: scala.Some cannot be cast to java.lang.String
The object is created with the wrong type inside, shouldn't object creation fail? It just fails when I try to make some operation on the os attribute.
The documentation for Any.asInstanceOf explains what you are seeing. The short of it is that the code compiles due to type erasure.
Note that the success of a cast at runtime is modulo Scala's erasure semantics. Therefore the expression 1.asInstanceOf[String] will throw a ClassCastException at runtime, while the expression List(1).asInstanceOf[List[String]] will not. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the requested type.

Scala cast to a variable type

I have the following code to cast value to the type of default:
def fct[T](value: Any, default: T): T = {
val result = value.asInstanceOf[T]
println(result, result.getClass.getName, result.isInstanceOf[T])
result
}
val res = fct("foo", 42)
Which result is:
(foo,java.lang.String,true)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(Test.sc2.tmp)
at #worksheet#.res$lzycompute(Test.sc2.tmp:7)
at #worksheet#.res(Test.sc2.tmp:7)
at #worksheet#.#worksheet#(Test.sc2.tmp:7)
Question is: why the println is displayed? The cast should fail. I tried to try / catch the 3 lines but no exception are raised during the function call.
Because of type erasure, it's impossible to actually implement value.{as/is}InstanceOf[T] where T is a type parameter. Scala's designers decided it should still compile, but value.asInstanceOf[T] is actually a no-op (just like the equivalent (T) value in Java), while value.isInstanceOf[T] always returns true (in Java, value instanceOf T is a compiler error). (Since it never does what you want, I'd strongly like to see a warning instead, at least for isInstanceOf.)
But Scala allows to do what you want using ClassTag:
import scala.reflect.ClassTag
def fct[T](value: Any, default: T)(implicit tag: ClassTag[T]): T = {
val result = (tag match {
case ClassTag.Int => value.asInstanceOf[Int]
... same for other primitives
case _ => tag.runtimeClass.cast(value)
}).asInstanceOf[T]
println(result, result.getClass.getName, tag.runtimeClass.isInstance(result))
result
}
(You still need asInstanceOf[T] because tag.runtimeClass usually returns the same class T represents but not always, so its static return type has to be Class[_] and cast returns Any).
However, pattern-matching : T handles presence of a ClassTag automatically and already handles boxing, so
def fct[T](value: Any, default: T)(implicit tag: ClassTag[T]): T = value match {
case result: T => result
case _ => default // or whatever you want to do if value is not a T
}
is the better way to do this.

What method conversion has been applied to this example?

In the spec of scala 6.26.2, there are 4 kind of conversions for methods:
MethodConversions
The following four implicit conversions can be applied to methods which are not applied to some argument list.
Evaluation. A parameterless method m of type => T is always converted to type T by evaluating the expression to which m is bound.
Implicit Application. If the method takes only implicit parameters, implicit argu- ments are passed following the rules of §7.2.
Eta Expansion. Otherwise, if the method is not a constructor, and the expected type pt is a function type (Ts′) ⇒ T′, eta-expansion (§6.26.5) is performed on the expression e.
Empty Application. Otherwise, if e has method type ()T , it is implicitly applied to the empty argument list, yielding e().
Some scala code:
def hello(): String = "abc"
def world1(s: String) = s
def world2(s: => String) = s
def world3(s: () => String) = s
world1(hello)
world2(hello)
world3(hello)
My question is, for world1, world2 and world3, which conversion rule is applied to each of them?
def world1(s: String) = s
world1(hello)
Empty application: hello is evaluated as hello() and then passed as the argument of world1
def world2(s: => String) = s
world2(hello)
Eta-expansion + Evaluation: hello is expanded into a function and evaluated lazily.
def world3(s: () => String) = s
world3(hello)
Eta-expansion: hello is expanded to a Function0[String]
Eta-expansion is necessary because methods and functions are two different things, even though the scala compiler is very good at hiding this difference.
The difference comes from the fact that methods are a JVM concept, whereas first-class functions are a scala-specific concept.
You can try this out in a REPL. First, let's define a method hello
scala> def hello(s: String) = s"hello $s"
hello: (s: String)String
scala> hello.hashCode
<console>:9: error: missing arguments for method hello;
follow this method with `_' if you want to treat it as a partially applied function
hello.hashCode
^
Woops, hello is not a value (in the JVM sense)! Not having an hashcode is a proof of it.
Let's get a Function1 out of the hello method
scala> hello _
res10: String => String = <function1>
scala> (hello _).hashCode
res11: Int = 1710358635
A function is a value, so it has an hashcode.

Is it possible to write a method in Scala returning objects with different type parameter?

Is it possible to write a method in Scala which returns an object of a type-parameterized class with different type paramter ? Something like this:
class A[T]
def f(switch: Boolean): A = if(switch) new A[Int] else new A[String]
Please note: The Code above is fictional to show the type of problem; The code above does not make semantically sense.
The code above will not compile because return type A is not parameterized.
You can, and you can even do it with type-safety with the aid of implicit arguments that encapsulate the pairings:
class TypeMapping[+A,B] {
def newListB = List.empty[B]
}
trait Logical
object True extends Logical
object False extends Logical
implicit val mapFalseToInt = new TypeMapping[False.type,Int]
implicit val mapTrueToString = new TypeMapping[True.type,String]
def f[A <: Logical,B](switch: A)(implicit tmap: TypeMapping[A,B]) = tmap.newListB
scala> f(True)
res2: List[String] = List()
scala> f(False)
res3: List[Int] = List()
You do have to explicitly map from boolean values to the custom True and False values.
(I have chosen List as the target class just as an example; you could pick anything or even make it generic with a little more work.)
(Edit: as oxbow_lakes points out, if you need all possible return values to be represented on the same code path, then this alone won't do it, because the superclass of List[Int] and List[String] is List[Any], which isn't much help. In that case, you should use an Either. My solution is for a single function that will be used only in the True or False contexts, and can maintain the type information there.)
One way of expressing this would be by using Either;
def f(switch: Boolean) = if (switch) Left(new A[Int]) else Right(newA[String])
This of course returns an Either[A[Int], A[String]]. You certainly cannot (at the moment) declare a method which returns some parameterized type P, with some subset of type parameters (i.e. only Int or String).
The language ceylon has union types and I understand the intention is to add these to scala in the near future, in which case, you could define a method:
def f(switch: Boolean): A[Int|String] = ...
Well, you could do something like that.
scala> class A {
| type T
| }
defined class A
scala> def f(b: Boolean): A = if(b) new A { type T = Int } else new A { type T = String }
f: (b: Boolean)A
But this is pointless. Types are a compile time information, and that information is getting lost here.
How about an absolutely minimal change to the "fictional code"? If we just add [_] after the "fictional" return type, the code will compile:
class A[T]
def f(switch: Boolean):A[_] = if(switch) new A[Int] else new A[String]
It is worth noting that A[_] is not the same as A[Any]. A[T] does not need to be defined covariant for the code to compile.
Unfortunately, information about the type gets lost.

Scala implicit conversion problem

I am struggling with a Scala implicit conversion problem. The following code snippet illustrates my problem :
import org.junit.{ Test, Before, After };
class ImplicitsTest {
implicit def toStringWrapper(str: String) = new StringWrapper(str);
#Test
def test(){
val res1: Predicate = "str" startsWith "other";
}
}
class StringWrapper(str: String){
def startsWith(other: String): Predicate = null;
}
trait Predicate
How can I force the String literal "str" to be converted through the implicit conversion toStringWrapper to get startsWith return Predicate instead of Boolean?
The code example doesn't compile. I am aware that String has already a startsWith method, I just want to use a different one, and I thought that using implicit conversions might be a way to do it.
Scala thankfully doesn't let you sneak replacement methods in without you noticing--if you call a method on a class, and the class has that method, that's the method call you get. To do otherwise would likely cause all sorts of confusion.
That leaves you with two other options:
(1) Rename the method
(2) Add a specific method to do the conversion.
The second approach works like so: you define a class that has both the method that you want and a uniquely-named method that returns itself, and optionally an implicit conversion from that class back to string if you want to be able to use the custom item like the original string (as if it had extended String):
object ImplicitExample {
class CustomString(s: String) {
def original = s
def custom = this
def startsWith(other: String): Int = if (s.startsWith(other)) 1 else 0
}
implicit def string_to_custom(s: String) = new CustomString(s)
implicit def custom_to_string(c: CustomString) = c.original
def test = {
println("This".custom.startsWith("Thi"))
println("This".custom.length())
}
}
scala> ImplicitExample.test
1
4
scala>
An implicit conversion is triggered in Scala only if the receiver does not contain the method being invoked or if an expression has a type different than the expected type.
Since the String object above contains the method startsWith, no implicit conversion is triggered. The compiler does, however, check if the type of the right hand expression "str".startsWith("other"); (that is Boolean) can be converted to a Predicate. Since there is no such implicit conversion in scope, it reports an error.
Note, also, that implicit conversions must be unambiguous. For instance, you might try to override Scala string behaviour for some other methods using implicit conversions. Java String objects are not Scala sequences (Seq), meaning that they do not have methods such as sorted, which returns the sorted version of the sequence. If you invoke it on a string:
scala> "string" sorted
res1: String = ginrst
This works because an implicit conversion defined in the Predef object gets triggered. Note that providing your own implicit conversion results in an error:
scala> implicit def wrap(s: String) = new { def sorted = "Hi!" }
wrap: (s: String)java.lang.Object{def sorted: java.lang.String}
scala> "string" sorted
<console>:7: error: type mismatch;
found : java.lang.String
required: ?{val sorted: ?}
Note that implicit conversions are not applicable because they are ambiguous:
...