Scala pattern matching on None and Some() in the same case - scala

What is the most elegant case to have None and Some() in the same case? Something like:
val data: Option[Int] = getSomeData()
data match {
case None || Some(data) && data > 50 =>
case _ =>
}

You can use Option.forall as condition.
def foo(data: Option[Int]): Unit =
if (data.forall(_ > 50)) println("OK")
else println("KO")
foo(None)
// => OK
foo(Some(1))
// => KO
foo(Some(51))
// OK

Normally such pattern matching can be written as follows
data match {
case None => doSomething()
case Some(data) if data > 50 => doSomething()
case _ => doOther()
}
If such combination (None || Some(data) && data > 50) happens often you can introduce custom extractor
object GreaterThan50OrEmpty {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
You can even call it as you want
object `None || Some(data) && data > 50` {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case `None || Some(data) && data > 50`() => println("matches pattern")
case _ => println("default")
}
Slightly more general approach
class GreaterThanOrEmpty(dataBound: Int) {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > dataBound => true
case _ => false
}
}
val GreaterThan50OrEmpty = new GreaterThanOrEmpty(50)
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
You can even generate such unapply automatically (although I guess it's not worth it)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro paradise")
class extractor[A] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ExtractorMacro.impl
}
object ExtractorMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val typA = c.prefix.tree match {
case q"new extractor[$a]" => a
}
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val cases = tname.decoded.split('|').map(s => s"case $s => true").mkString("\n")
val casesTree = c.parse(
s"""arg match {
| $cases
| case _ => false
|}""".stripMargin)
q"""$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
def unapply(arg: $typA): Boolean = $casesTree
}"""
case _ => c.abort(c.enclosingPosition, "not object")
}
}
}
Usage:
#extractor[Option[Int]]
object `Some(x) if x < 25 | None | Some(data) if data > 50`
//Warning:scalac: object ... extends scala.AnyRef {
// ...
// def unapply(arg: Option[Int]): Boolean = arg match {
// case Some((x # _)) if x.$less(25) => true
// case None => true
// case Some((data # _)) if data.$greater(50) => true
// case _ => false
// }
//}
def test(arg: Any) = arg match {
case `Some(x) if x < 25 | None | Some(data) if data > 50`() =>
println("matches pattern")
case _ => println("default")
}
test(None) // matches pattern
test(Some(51)) // matches pattern
test(Some(24)) // matches pattern
test(Some(30)) // default

Related

Using existing methods in macro

Suppose i have some class with some methods
class Clz ... {
def someMethod: Map[String, Long] = ...
def id: Long = 0L
}
i'm need to reuse someMethod and overwrite id
i don't know why but it's throw Stackoverflow and also i'm need to do something with params/methods of Clz without returning the result
what i've tried:
object Macros {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls # q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$stats }") :: Nil =>
val someMethodM = stats
.filter(case q"$_ def $methodName: $_ = $_" => methodName.toString == "someMethod")
.map {
case q"$_ def $_: $_ = $res" => res
}
q"""
$cls
object ClzCompanion {
val someMethodsRef: Map[String, Int] = Map.apply(..$someMethodM)
..${
paramss
.flatten
.foreach {
case q"$_ val $nname: $tpt = $valuee" =>
// simply do something with valuee and nothing else
// "fire and forget"
}
}
..${
paramss
.flatten
.map {
case q"$_ val $nname: $tpt = $valuee" =>
// here logic, if this nname in someMethodsRef then
// someMethodsRef.find({ case (key, _) => key == nname) match {
// case Some(_) => "def $nname: ($tpt, Int) = ... // new method with body
// case None => do nothing...
}
}
}
"""
}
}
}
how i can overwrite the id method at Clz?
and why it throws StackOverflow??

How can I improve this breadth-first search in Scala to make it more idomatic FP?

Been looking for a way to do this without a queue, and make it tail-recursive. I'm thinking LazyLists might also help. Would a queue be faster? I'm basically sending mutated state down through each function call with the next level of children.
case class Tree [A] (
value : A,
Right: Option[Tree[A]],
Left: Option[Tree[A]]
)
object Tree {
def liftChildren[A](t: Tree[A]) = {
List(t.Left, t.Right).flatten
}
def findChild[A](value: A, t: Tree[A]) : Option[Tree[A]] = {
var lvl = 0
def searchChildren(t: List[Tree[A]]): (Option[Tree[A]], List[Tree[A]]) = {
// could be removed, just for fun
lvl += 1
t.foreach(tt => println(s"Scanning Level ${lvl.toString} Value ${tt.value.toString}"))
//
val curfind = t.find(tt => {
tt.value == value
})
curfind match {
case Some(tr) => (Some(tr), t)
case None => {
val children: List[Tree[A]] = t.flatMap(tt => Tree.liftChildren(tt))
children.isEmpty match {
case true => (None, List.empty)
case false => searchChildren(children)
}
}
}
}
searchChildren(List(t))._1
}
}
object main extends App {
println("hello world")
val tree = Tree[Int](
1,
Some(
Tree[Int](2, None, Some(
Tree[Int](5,None, Some(Tree[Int](6, None,None))))
)
) ,
Some(
Tree[Int](
3,
Some(
Tree[Int](4, None, Some(Tree[Int](7, None,None)))
), None
)
)
)
val res = Tree.findChild(6, tree)
println("FoundIt" + res)
}
It's working as I expect. I'm just wondering whther this could be any better or more idiomatic FP. Would the cats library help at all?
Here is a tail-recursive implementation, using pattern matching.
final case class Tree[+A](value: A, left: Option[Tree[A]], right: Option[Tree[A]])
def find[A](value: A)(tree: Tree[A]): Option[Tree[A]] = {
import scala.collection.immutable.Queue
#annotation.tailrec
def bfs(queue: Queue[Tree[A]]): Option[Tree[A]] =
queue.dequeueOption match {
case None => None
case Some((tree, remaining)) => tree match {
case Tree(`value`, _, _) => Some(tree)
case Tree(_, Some(left), Some(right)) => bfs(queue = remaining.enqueue(left).enqueue(right))
case Tree(_, Some(left), None) => bfs(queue = remaining.enqueue(left))
case Tree(_, None, Some(right)) => bfs(queue = remaining.enqueue(right))
case Tree(_, None, None) => bfs(queue = remaining)
}
}
bfs(queue = Queue(tree))
}
def find[A](value: A)(tree: Tree[A]): Option[Tree[A]] = {
#annotation.tailrec
def dfs(stack: List[Tree[A]]): Option[Tree[A]] =
stack match {
case Nil => None
case tree :: remaining => tree match {
case Tree(`value`, _, _) => Some(tree)
case Tree(_, Some(left), Some(right)) => dfs(stack = left :: right :: remaining)
case Tree(_, Some(left), None) => dfs(stack = left :: remaining)
case Tree(_, None, Some(right)) => dfs(stack = right :: remaining)
case Tree(_, None, None) => dfs(stack = remaining)
}
}
dfs(stack = List(tree))
}
Here are some implementations using LazyList.
final case class Tree[+A](value: A, children: List[Tree[A]])
// DFS by right.
def find[A](value: A)(tree: Tree[A]): Option[Tree[A]] =
LazyList.unfold(List(tree)) {
case Nil => None
case tree :: remaining => Some((tree, tree.children reverse_::: remaining))
}.find(tree => tree.value == value)
// DFS by left.
def find[A](value: A)(tree: Tree[A]): Option[Tree[A]] =
LazyList.unfold(List(tree)) {
case Nil => None
case tree :: remaining => Some((tree, tree.children ::: remaining))
}.find(tree => tree.value == value)
// BFS
def find[A](value: A)(tree: Tree[A]): Option[Tree[A]] =
LazyList.unfold(Queue(tree)) { queue =>
queue.dequeueOption.map {
case (tree, remaining) => (tree, remaining.enqueueAll(tree.children))
}
}.find(tree => tree.value == value)

Case Classes w/ Option Parameters & Case Matching

I have a case class that has multiple parameters of which some are Options. Here is a simplified example:
case class Foobar(a: String, b: Option[String], c: Option[CustomClass])
I want to be able to match cases of Foobar where b and/or c is not None. For example, one case could be:
testResult match {
case Foobar("str1", Some(_), None) => "good"
case Foobar("str2", None, Some(_)) => "ok"
case _ => "bad"
}
Furthermore, I want to reference the case patterns via variables and this is where I'm stuck. I want to do something like the following:
val goodPat = Foobar("str1", Some(_), None) // compile fail
val okPat = Foobar("str2", None, Some(_)) // compile fail
testResult match {
case `goodPat` => "good"
case `okPat` => "ok"
case _ => "bad"
}
Is something like this possible? Is there another way to specify "not None"? Is there another way to approach this problem?
EDIT: I'm adding more details and context to the question. I have a large List of 2-tuples representing unit tests for a particular function. The 2-tuples represent the input and expected output. Eg.
// imagine this list is much bigger and Foobar contains more Option parameters
val tests = List(
("test1", Foobar("idkfa", None, None)),
// I know these fail to compile but I need to do something like this
("test2", Foobar("idclip", Some("baz"), Some(_)),
("test3", Foobar("iddqd", Some(_), None)
)
tests.foreach(test => {
val (input, expected) = test
myFunction(input) match {
case `expected` => println("ok")
case _ => println("bad")
}
})
I think you seeking for something like this:
case class DoomOpt(s: String)
case class Foobar(a: String, b: Option[String], c: Option[DoomOpt])
def myFunction(s: String): Foobar = { // your code here }
val tests = Map[String, PartialFunction[Foobar, Unit]](
"idkfa" → { case Foobar("idkfa", None, None) ⇒ },
"test2" → { case Foobar("idclip", Some("baz"), Some(_)) ⇒ },
"test3" → { case Foobar("iddqd", Some(_), None) ⇒ },
"idspispopd" → { case Foobar("idspispopd", Some(_), None) ⇒ }
)
tests.foreach { case (input, checker) =>
if (checker.isDefinedAt(myFunction(input)))
println("ok")
else
println("bad")
}
Pattern matching uses extractors which provide the unapply function to deconstruct the object. So... you can just supply your custom extractor in this case. Create a list of these extractor test cases and apply them one by one.
case class Foobar(s: String, o: Option[Int])
trait TestExtractor {
def unapply(fbar: Foobar): Boolean
}
object somePatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", Some(_)) => true
case _ => false
}
}
object nonePatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", None) => true
case _ => false
}
}
object bazPatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", Some("baz")) => true
case _ => false
}
}
val testList: List[(String, TestExtractor)] = List(("test1", nonePatExtractor), ("test2", bazPatExtractor), ("test3", somePatExtractor))
val fooToTest = Foobar("yes", Some(5))
testList.foreach({
case (testName, extractor) => {
fooToTest match {
case pat # extractor() => println("testName :: " + testName + ", Result :: ok")
case _ => println("testName :: " + testName + ", Result :: bad")
}
}
})
And if you are looking for a more extendible approach then you can consider something like following,
case class Foobar(s: String, o1: Option[Int], o2: Option[String])
case class TestCondition(df: Foobar => Boolean) {
def test(foobar: Foobar): Boolean = df(foobar)
}
val o1IsNone = TestCondition(f => f.o1.isEmpty)
val o1IsSome = TestCondition(f => f.o1.isDefined)
val o2IsNone = TestCondition(f => f.o2.isEmpty)
val o2IsSome = TestCondition(f => f.o2.isDefined)
case class TestCase(tcs: List[TestCondition]) {
def test(foobar: Foobar) = tcs.foldLeft(true)({ case (acc, tc) => acc && tc.test(foobar) })
}
val testList = List[(String, TestCase)](
("test1", TestCase(List(o1IsSome, o2IsSome))),
("test2", TestCase(List(o1IsSome, o2IsNone))),
("test3", TestCase(List(o1IsNone, o2IsSome))),
("test4", TestCase(List(o1IsNone, o2IsNone)))
)
val foobarToTest = Foobar("yes", Some(5), None)
testList.foreach({
case (testName, testCase) => {
foobarToTest match {
case foobar: Foobar if testCase.test(foobar) => println("testName :: " + testName + ", Result :: ok")
case _ => println("testName :: " + testName + ", Result :: bad")
}
}
})

Type conversion from Unit to Future[Boolean]

I have the following function and I would like to return Future[Boolean] but the IDE prompts that I return Unit. I am new in Scala. Can someone point me out what I am doing wrong?
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.onSuccess {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.map {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}
Replace onSuccess with flatMap. Assuming your remove(x: String) method also returns a Future, that will also need to be flatMapped:
def remove(loginInfo: LoginInfo): Future[Boolean] = {
val result = findObject(loginInfo)
result.flatMap {
case Some(persistentPasswordInfo) =>
val removeResult = remove(persistentPasswordInfo._id.toString)
removeResult.flatMap {
case Left(ex) => Future.successful(false)
case Right(b) => Future.successful(b)
}
case None => Future.successful(false)
}
}

How to check if list contains all Some or None or both with pattern matching?

There is a list with type List[Option[String]], it may contain Some or None
val list:List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
list match {
case /*List with all Some*/ => println("all items are Some")
case /*List with all None*/ => println("all items are None")
case /*List with Some and None*/ => println("Contain both Some and None")
}
But I don't know how to write it. Is it possible to use pattern matching?
You can write custom extractors:
object AllSome {
def unapply[T](l: List[Option[T]]) = l.forall(_.isDefined)
}
object AllNone {
def unapply[T](l: List[Option[T]]) = l.forall(_ == None)
}
object Mixed {
def unapply[T](l: List[Option[T]]) = !AllNone.unapply(l) && !AllSome.unapply(l)
}
And use them like:
list match {
case AllSome() => ???
case AllNone() => ???
case Mixed() => ???
}
One approach involves flattening the list and comparing the resulting length with the original length, like this,
list.flatten.length match {
case 0 => println("All items are None")
case len if len < l.length => println("Contain both Some and None")
case _ => println("All items are Some")
}
Update
To get the contents of each Some, simply list.flatten , namely for instance,
list.flatten
res: List(aaa, bbb)
and
List().flatten
res: List[Nothing] = List()
If you really want a solution with well-defined semantics and based on pattern matching you could do something as follows:
abstract class CollectionStatus
case object Empty extends CollectionStatus
case object AllSome extends CollectionStatus
case object AllNone extends CollectionStatus
case object Mixed extends CollectionStatus
object CollectionStatus {
def default: CollectionStatus = Empty
}
def folder(status: CollectionStatus, o: Option[_]): CollectionStatus = {
(status, o) match {
case (Empty, Some(_)) => AllSome
case (Empty, None) => AllNone
case (AllSome, Some(_)) => AllSome
case (AllNone, None) => AllNone
case _ => Mixed
}
}
Here's how I would use it:
List[Option[String]]().foldLeft(CollectionStatus.default)(folder _) //Empty
List(Option("foo"), Option("bar")).foldLeft(CollectionStatus.default)(folder _) //AllSome
List(Option("foo"), None).foldLeft(CollectionStatus.default)(folder _) //Mixed
List(None, None).foldLeft(CollectionStatus.default)(folder _) //AllNone
This could be further improved by replacing foldLeft with a tail recursive function that would accumulate the status of the list, and finish its computations without traversing the whole list if it recognized the list was "Mixed" already:
import scala.annotation.tailrec
def listStatus(list: List[Option[_]]): CollectionStatus = {
#tailrec
def inner(acc: CollectionStatus, ls: List[Option[_]]): CollectionStatus = {
acc match {
case Mixed => Mixed
case s => {
ls match {
case Nil => s
case h :: t => {
inner(folder(s, h), t)
}
}
}
}
}
inner(CollectionStatus.default, list)
}
val l: List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
l.groupBy({
case Some(_) => "s"
case None => "n"
}).toList match {
case List(_,_) => println("both")
case List(a) => if( a._1 == "n") println("none") else println("some")
}