Scala pattern matching variable binding - scala

Why can't I bind the variable in #-style when the extractor return Option[<Type>]? I.e. this one does not work:
object IsUpperCase {
def unapply(s: String): Option[String] = {
if (s.toUpperCase() == s) {
Some(s)
} else {
None
}
}
}
val s = "DuDu#qwadasd.ru"
s match {
case u # IsUpperCase() => println("gotcha!") // what? "wrong number of arguments for object IsUpperCase"?
case _ =>
}
But this one works!
val s = "DuDu#qwadasd.ru"
s match {
case IsUpperCase(u) => println("gotcha!")
case _ =>
}
From the other hand, if IsUpperCase looks like this:
object IsUpperCase {
def unapply(s: String): Boolean = {
return s.toUpperCase() == s
}
}
Then the first example works, and the second does not! Why is it this way?

what? "wrong number of arguments for object IsUpperCase"?
case u # IsUpperCase() => println("gotcha!")
Well, yes. The return type of unapply is Option[String], which means the pattern match of IsUpperCase must accept a parameter, like this:
case u # IsUpperCase(_) => println("gotcha!") // I don't care about the parameter
The unapply definition that fits the first pattern is this:
object IsUpperCase {
def unapply(s: String): Boolean = s.toUpperCase() == s
}
That can be used to pattern match against IsUpperCase().

Because for the first example you need to write something like case u # IsUpperCase(v) => or case u # IsUpperCase(_) =>, which means "match IsUpperCase(v) and if it succeeds bind the original string to u".

Related

Scala: Return an instance of the passed-in type

I am trying to implement a simple validator method that would take
a type as its first parameter and a String as the second and would always return an instance of the passed-in type. Without no further ado - this is the method:
def validateType[T](taip: T, input: String):T = taip match {
case Boolean => {
val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase()
if(simplifiedInput.matches("[yn]")){
simplifiedInput match {
case "y" => true
case "n" => false
}
} else{
validateType(Boolean, StdIn.readLine("$simplifiedInput is not a valid "
+"answer, please try again"))
}
}
}
However I get a compile error (red underscore in my IDE) saying:
"type mismatch; Found Boolean(true) required : T
I realize that T here is a type, but how can I specify that I want to return an instance of that type? I tried to play with the ClassTag also but with no success. Thank you.
Since you want a behavior for a given type, and not a given object or value, a typeclass would be relevant.
trait Validable[T] {
def validate(input: String): T
}
object Validable {
implicit val boolean = new Validable[Boolean] {
def validate(input: String): Boolean = {
val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase()
if(simplifiedInput.matches("[yn]")){
simplifiedInput match {
case "y" => true
case "n" => false
}
} else {
validate(StdIn.readLine("$simplifiedInput is not a valid "
+"answer, please try again"))
}
}
//if you want to write a validator for another type, simply add a new instance.
implicit val otherTypeValidable = new Validable[OtherType] {
def validate(input: String): OtherType = ...
}
}
def validateType[T](input: String)(implicit validable: Validable[T]): T =
validable.validate(input)
validateType[Boolean]("y") // returns true
Of course, you can implement an instance for your typeclass for each relevant type. This will throw a compilation error if you use it with a type for which you do not have an implicit instance of Validable[T], so it is safer than using reflection.
You can use TypeTag for this, like:
import scala.reflect.runtime.universe._
def validateType[T](input: String)(implicit typeTag: TypeTag[T]): T = {
typeTag.tpe match {
case t if t =:= typeOf[Boolean] => {
val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase()
if (simplifiedInput.matches("[yn]")) {
simplifiedInput match {
case "y" => true.asInstanceOf[T]
case "n" => false.asInstanceOf[T]
}
} else {
validateType(StdIn.readLine("$simplifiedInput is not a valid "
+ "answer, please try again"))
}
}
}
}
validateType[Boolean]("y") //set Boolean type for `T`
>true
In there =:= used to test whether Type is equal. more comparator see the reference. and for the return type to T, you need cast it by asInstanceOf[T].

Pattern Matching to check if string is null or empty

Is it possible to check if string is null or empty using match?
I'm trying to do something like:
def sendToYahoo(message:Email) ={
val clientConfiguration = new ClientService().getClientConfiguration()
val messageId : Seq[Char] = message.identifier
messageId match {
case messageId.isEmpty => validate()
case !messageId.isEmpty => //blabla
}
}
But i have a compile error.
Thank in advance.
You can write a simple function like:
def isEmpty(x: String) = Option(x).forall(_.isEmpty)
or
def isEmpty(x: String) = x == null || x.isEmpty
You might also want to trim the string, if you consider " " to be empty as well.
def isEmpty(x: String) = x == null || x.trim.isEmpty
and then use it
val messageId = message.identifier
messageId match {
case id if isEmpty(id) => validate()
case id => // blabla
}
or without a match
if (isEmpty(messageId)) {
validate()
} else {
// blabla
}
or even
object EmptyString {
def unapply(s: String): Option[String] =
if (s == null || s.trim.isEmpty) Some(s) else None
}
message.identifier match {
case EmptyString(s) => validate()
case _ => // blabla
}
def isNullOrEmpty[T](s: Seq[T]) = s match {
case null => true
case Seq() => true
case _ => false
}
If you're looking to check whether a String is null or empty, here's a one-liner that does that. You wrap it with an Option, use filter to make sure it's not an empty String then call getOrElse to provide a default value in case the original value is either null or an empty String.
Option(s).filter(!_.isEmpty).getOrElse(columnName)

Can I use a class variable in a Scala match statement?

Say I have something like this:
obj match {
case objTypeOne : TypeOne => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
Now I want to generalise, to pass in one of the types to match:
obj match {
case objTypeOne : clazz => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
But this isn't allowed, I think for syntactic rather than semantic reasons (although I guess also that even though the clazz is a Class[C] the type is erased and so the type of the Option will be lost).
I ended up with:
if(clazzOne.isAssignableFrom(obj.getClass)) Some(clazz.cast(obj))
if(obj.isInstanceOf[TypeTwo]) Some(obj.asInstanceOf[TypeTwo])
None
I just wondered if there was a nicer way.
You could define an extractor to match your object:
class IsClass[T: Manifest] {
def unapply(any: Any): Option[T] = {
if (implicitly[Manifest[T]].erasure.isInstance(any)) {
Some(any.asInstanceOf[T])
} else {
None
}
}
}
So let's test it:
class Base { def baseMethod = () }
class Derived extends Base
val IsBase = new IsClass[Base]
def test(a:Any) = a match {
case IsBase(b) =>
println("base")
b.baseMethod
case _ => println("?")
}
test(new Base)
test(1)
You will have to define a val for your extractor, you can't inline IsBase, for example. Otherwise it would be interpreted as an extractor.
You could use pattern guards to achieve that. Try something like this:
obj match {
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case objTypeOne if clazz.isAssignableFrom(objTypeOne.getClass) => Some(clazz.cast(objTypeOne))
case _ => None
}
You can use a local type alias for that:
def matcher[T](obj: Any)(implicit man: Manifest[T]) = {
val instance = man.erasure.newInstance.asInstanceOf[AnyRef]
type T = instance.type // type alias
obj match {
case objTypeOne : T => "a"
case objTypeTwo : TypeTwo => "b"
case _ => "c"
}
}
scala> matcher[TypeOne](TypeOne())
res108: java.lang.String = a
scala> matcher[TypeTwo](TypeOne())
res109: java.lang.String = c
UPDATE: Aaron Novstrup has pointed out that singleton type will only work if man.erasure.newInstance==obj (see ยง3.2.1 of the spec)

Optional function parameter with generic return type

How would you implement class that parses some input via regex and transforms founded string to some other type? My approach is:
class ARegex[T](regex:Regex, reform:Option[String => T]){
def findFirst(input:String):Option[T] = {
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s) // this won't compile because of type mismatch
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
class BRegex[T](regex:Regex, reform:Option[String => T]) {
def findFirst(input:String) = { //returns Option[Any] - erasure
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s)
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
We can solve this problem by eliminating the Option part of the reform's type, and using a different mechanism to indicate that we don't want to change the match in any way. This mechanism is to use identity as a default parameter or pass identity when you don't want the type to change.
class ARegex[T](regex:Regex, reform:String => T = identity[String](_)){
def findFirst(input:String):Option[T] = {
regex.findFirstIn(input) match{
case None => None
case Some(s) => Some(reform(s))
}
}
}
new ARegex("something".r).findFirst("something else") //returns Option[String]
new ARegex("3".r, {x=>x.toInt}).findFirst("number 3") //returns Option[Int]
Well, the problem is the type mismatch, because you are returning either a String or a T, which, of course, are unified at Any. You can't say you are going to return Option[T] and then return Option[String].
Other than that, a simplified version of that code is this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Any] =
regex findFirstIn input map { s => reform map (_(s)) getOrElse s }
}
You could return an Option[Either[String, T]], though. The code would look like this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Either[String, T]] =
regex findFirstIn input map { s => reform map (_(s)) toRight s }
}
Why is reform Option[String => T] instead of just String => T? If you don't pass in a mechanism for creating an instance of your desired type, there's no mechanism for the runtime system to actually create the appropriate object. If you really need to pass in an Option[String => T] then your second case should simply return None.
Also, flatMap is your friend, and will give you the correct behavior (i.e. if reform is None, the method returns None.
class RegexExtractor[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[T] = reform.flatMap(f => regex.findFirstIn(input).map(f))
}

Is it a rule that unapply will always return an Option?

I tried to create an unapply method to use in pattern matching, and I tried to make it return something different than Option, however, Eclipse shows that as an error. Is it a rule that unapply must return an Option[T] ?
EDIT: here's the code I'm trying to use. I switched the code from the previous section so that unapply returns a Boolean
import java.util.regex._
object NumberMatcher {
def apply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
def unapply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
}
object x {
def main(args : Array[String]) : Unit = {
val strings = List("geo12","neo493","leo")
for(val str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println ("no")
}
}
}
}
Eclipse says wrong number of arguments for object NumberMatcher. Why is that?
If you want to return something with unapply, return it inside Some. Returning Boolean just tests if the match can be made or not.
Here is how a pattern matching is translated:
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
Assuming NumberMatcher returns Option[...], it will do:
val r1 = NumberMatcher.unapply(str)
if (r1 != None) {
val group = r1.get
println(group)
} else {
println("no")
}
If NumberMatcher returns Boolean, then you can have it receive something. In that case, this is what happens:
str match {
case NumberMatcher() => println("yes")
case _ => println("no")
}
becomes
val r1 = NumberMatcher.unapply(str)
if (r1) {
println("yes")
} else {
println("no")
}
Note that this is a very superficial explanation. Case matches can test for constants, have additional guard conditions, alternatives, use unapply recursively, use unapplySeq, etc. Here I'm only showing very basic usage to address a specific question. I strongly advise searching for a fuller explanation of pattern matching.
Take a look at this example again.
I quote
The return type of an unapply should be chosen as follows:
* If it is just a test, return a Boolean. For instance case even()
* If it returns a single sub-value of type T, return a Option[T]
* If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
When you defined unapply to return a Boolean, you were indicating that the pattern doesn't have any wildcards to match (or bind). So the case statement for this unextractor should be case NumberMatcher => println(str), and giving it a variable to fill is wrong.
Alternatively, to make case NumberMatcher(group) => println(group) you need to define unapply() to return Option[String]
package com.tutorial.extracters
object ExtracterwithBooleanReturnType extends App {
import java.util.regex._
object NumberMatcher {
def apply(x: String*) = {
x
}
def unapply(x: String): Option[Boolean] = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return Some(matcher.find)
}
}
val strings = NumberMatcher("geo12", "neo493", "leo")
for (str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
}
}
we can achieve this with above code as well