Is it possible to pattern match on function heads in scala?
For example, can I write something along the lines of:
def myFunction(a:: b:: xs): Int = ???
def myFunction(a:: xs): Int = ???
def myFunction(List.empty): Int = ???
You can use partial functions for this case. Example:
val myFunctionCase1: PartialFunction[List[Int], Int] = {
case a :: b :: xs => ???
}
val myFunctionCase2: PartialFunction[List[Int], Int] = {
case a :: xs => ???
}
val myFunctionCase3: PartialFunction[List[Int], Int] = {
case Nil => ???
}
// compose functions
val myFunction: List[Int] => Int =
myFunctionCase1 orElse myFunctionCase2 orElse myFunctionCase3
Usage examples:
myFunctionCase1(List(1,2,3)) // invoke
myFunctionCase1(List(1)) // throw MatchError
myFunctionCase2(List(1)) // invoke
...
myFunction(List(1,2,3))
myFunction(List(1))
myFunction(Nil)
...
Related
Scastie version
With this infra-structure:
trait Pat[A]
object Pat {
def apply[A](elems: A*): Pat[A] = ???
}
implicit class PatOps[A](p: Pat[A]) {
def ++ (that: Pat[A]): Pat[A] = ???
def bubble: Pat[Pat[A]] = ???
def grouped(size: Pat[Int]): Pat[Pat[A]] = ???
}
implicit class PatPatOps[A](p: Pat[Pat[A]]) {
def map[B](f: Pat[A] => Pat[B]): Pat[Pat[B]] = ???
def flatMap[B](f: Pat[A] => Pat[B]): Pat[B] = ???
def flatten: Pat[A] = ???
}
It is possible to write the following for-comprehension:
trait Test1 {
val lPat = Pat(1, 2, 3)
val xs = for {
len <- lPat.bubble
cantus <- Pat(4, 40, 3).grouped(len)
} yield {
cantus ++ Pat(-1)
}
xs.flatten
}
But this one, using an intermediate variable, fails:
trait Test2 {
val lPat = Pat(1, 2, 3)
val xs = for {
len <- lPat.bubble // XXX
brown = Pat(4, 40, 3)
cantus <- brown.grouped(len)
} yield {
cantus ++ Pat(-1)
}
xs.flatten
}
The error for the line marked XXX is:
type mismatch;
found : (Playground.this.Pat[Int], Playground.this.Pat[Int])
required: Playground.this.Pat[?]
Scala is 2.12.4
This happens when you define map with overly restrictive signature map[B](f: Pat[A] => Pat[B]). Recall that usually, it is supposed to accept functions with arbitrary result type B, that is, it's supposed to be rather something like:
map[B](f: A => B): <stuff>
Now, your for-comprehension with intermediate helper variable brown
val xs = for {
len <- lPat.bubble
brown = Pat(4, 40, 3)
cantus <- brown.grouped(len)
} yield {
cantus ++ Pat(-1)
}
is rewritten using a map into
val xs = lPat.bubble.map(((len) => {
val brown = Pat(4, 40, 3);
scala.Tuple2(len, brown)
})).flatMap(((x$1) => x$1: #scala.unchecked match {
case scala.Tuple2((len # _), (brown # _)) =>
brown.
grouped(len).
map(((cantus) => cantus.$plus$plus(Pat(-1))))
}))
as described in the documentation or in my overly detailed answer here.
Note how the return type of the implicitly generated map is now something like (Pat[A], Pat[Int]) (the type of the tuple (len, brown)), and doesn't match the pattern Pat[B] from your declaration.
I don't see any workarounds. Just do whatever you can to avoid defining map as map[B](f: Pat[A] => Pat[B]), otherwise it will behave way too strangely. Avoid breaking functoriality of map. If your Pat[X] cannot map f: X => Y to a Pat[Y] for arbitrary X and Y, then don't call it map.
Edit: there is always a work-around...
One thing you could do is to introduce some kind of implicitly supplied CanPatFrom:
trait CanPatFrom[X, A] extends (X => Pat[A])
and then
...
def map[X, B](f: Pat[A] => X)(implicit cpf: CanPatFrom[X, B]) = {
val pb: Pat[B] = cpf(f(...))
/* do your stuff here with `Pat[B]` instead of
* generic `X`
*/
...
}
Assuming that your Pat carries some kind of cartesian-monoidal structure, you could define
CanPatFrom[Pat[A], Pat[A]],
CanPatFrom[(Pat[A], Pat[B]), Pat[(A, B)]],
CanPatFrom[(Pat[A], Pat[B], Pat[C]), Pat[(A, B, C)]],
...
and thereby obtain a map that can at least cope with the case that the return type is a tuple.
Riffing off of Andrey Tyukin's answer, another thing you can do with implicits is assert that some type parameter satisfies some constraint. For example, we can assert that a parameter A is a subclass of Pat[A1] for some other parameter A1. Here's the code:
object Pats extends App {
trait Pat[A]
object Pat {
def apply[A](elems: A*): Pat[A] = ???
}
implicit class PatOps[A](p: Pat[A]) {
def ++(that: Pat[A]): Pat[A] = ???
def bubble: Pat[Pat[A]] = ???
def grouped(size: Pat[Int]): Pat[Pat[A]] = ???
def map[B, A1, B1](f: A => B)
(implicit
ev1: A <:< Pat[A1],
ev2: B <:< Pat[B1]): Pat[B] = ???
def flatMap[B, A1, B1](f: A => Pat[B])
(implicit
ev1: A <:< Pat[A1],
ev2: B <:< Pat[B1]): Pat[B] = ???
def flatten[A1](implicit ev: A <:< Pat[A1]): Pat[A1] = ???
}
val lPat = Pat(1, 2, 3)
val xs = for {
len <- lPat.bubble
cantus <- Pat(4, 40, 3).grouped(len)
} yield cantus ++ Pat(-1)
xs.flatten
}
I wouldn't recommend taking this approach, though, because it violates the usual meaning of map and flatMap. What I might suggest instead is creating a private class PatInternal and completely obfuscate it from the end user.
Suppose I have a function to check a string:
case class MyError(msg: String)
val oops = MyError("oops")
def validate(s: String):Either[MyError, Unit] =
if (s == "a") Right(()) else Left(oops)
Now I would like to reuse it and write a new function to check the head of a list of strings and return either an error or the list tail.
// should be validateHeadOfList but I don't want to change the name now
def validateList(xs: List[String]): Either[MyError, List[String]] =
xs match {
case head::tail =>
validate(head).fold(err => Left(err), _ => Right(tail))
case _ => Left(oops)
}
This function works but doesn't look elegant. How would you improve it ?
Your implementation looks rather idiomatic to me, but if you're looking for a possibly more concise alternative - I think this does exactly the same, and is (arguably) more concise:
def validateList(xs: List[String]): Either[MyError, List[String]] =
xs.headOption.map(validate).map(_.right.map(_ => xs.tail)).getOrElse(Left(oops))
Assuming you actually want to return either a single error or the original list, then the following will work. I can't comment on whether this is idiomatic Scala though.
case class MyError(msg: String)
val oops = MyError("oops")
def validate(s: String): Either[MyError, Unit] =
if (s == "a") ().asRight else oops.asLeft
final def validateList(list: List[String]): Either[MyError, List[String]] = {
#scala.annotation.tailrec
def loop(xs: List[String]): Either[MyError, List[String]] = {
xs match {
case head :: tail =>
validate(head) match {
case Left(error) => error.asLeft
case _ => loop(tail)
}
case _ => list.asRight
}
}
loop(list)
}
For example:
scala> validateList(List("a", "a", "a"))
res0: Either[MyError,List[String]] = Right(List(a, a, a))
scala> validateList(List("a", "b", "a"))
res1: Either[MyError,List[String]] = Left(MyError(oops))
Suppose I am refactoring a function like this:
def check(ox: Option[Int]): Unit = ox match {
case None => throw new Exception("X is missing")
case Some(x) if x < 0 => throw new Exception("X is negative")
case _ => ()
}
I'd like to get rid of the exceptions but I need to keep the check signature and behavior as is, so I'm factoring out a pure implementation -- doCheck:
import scala.util.{Try, Success, Failure}
def doCheck(ox: Option[Int]): Try[Unit] = ???
def check(ox: Option[Int]): Unit = doCheck(ox).get
Now I am implementing doCheck as follows:
def doCheck(ox: Option[Int]): Try[Unit] = for {
x <- ox toTry MissingX()
_ <- (x > 0) toTry NegativeX(x)
} yield ()
Using the following implicits:
implicit class OptionTry[T](o: Option[T]) {
def toTry(e: Exception): Try[T] = o match {
case Some(t) => Success(t)
case None => Failure(e)
}
}
implicit class BoolTry(bool: Boolean) {
def toTry(e: Exception): Try[Unit] = if (bool) Success(Unit) else Failure(e)
}
The implicits certainly add more code. I hope to replace OptionTry with an implicit from scalaz/cats someday and maybe find an analog for BoolTry.
You could refactor with a loan pattern and Try.
def withChecked[T](i: Option[Int])(f: Int => T): Try[T] = i match {
case None => Failure(new java.util.NoSuchElementException())
case Some(p) if p >= 0 => Success(p).map(f)
case _ => Failure(
new IllegalArgumentException(s"negative integer: $i"))
}
Then it can be used as following.
val res1: Try[String] = withChecked(None)(_.toString)
// res1 == Failure(NoSuchElement)
val res2: Try[Int] = withChecked(Some(-1))(identity)
// res2 == Failure(IllegalArgumentException)
def foo(id: Int): MyType = ???
val res3: Try[MyType] = withChecked(Some(2)) { id => foo(id) }
// res3 == Success(MyType)
How can I change list of Eithers into two list of value Right and Left. When I use partition it returns two lists of Either's not values. What is the simplest way to do it?
foldLeft allows you to easily write your own method:
def separateEithers[T, U](list: List[Either[T, U]]) = {
val (ls, rs) = list.foldLeft(List[T](), List[U]()) {
case ((ls, rs), Left(x)) => (x :: ls, rs)
case ((ls, rs), Right(x)) => (ls, x :: rs)
}
(ls.reverse, rs.reverse)
}
You'll have to map the two resulting lists after partitioning.
val origin: List[Either[A, B]] = ???
val (lefts, rights) = origin.partition(_.isInstanceOf[Left[_]])
val leftValues = lefts.map(_.asInstanceOf[Left[A]].a)
val rightValues = rights.map(_.asInstanceOf[Right[B]].b)
If you are not happy with the casts and isInstanceOf's, you can also do it in two passes:
val leftValues = origin collect {
case Left(a) => a
}
val rightValues = origin collect {
case Right(b) => b
}
And if you are not happy with the two passes, you'll have to do it "by hand":
def myPartition[A, B](origin: List[Either[A, B]]): (List[A], List[B]) = {
val leftBuilder = List.newBuilder[A]
val rightBuilder = List.newBuilder[B]
origin foreach {
case Left(a) => leftBuilder += a
case Right(b) => rightBuilder += b
}
(leftBuilder.result(), rightBuilder.result())
}
Finally, if you don't like mutable state, you can do so:
def myPartition[A, B](origin: List[Either[A, B]]): (List[A], List[B]) = {
#tailrec
def loop(xs: List[Either[A, B]], accLeft: List[A],
accRight: List[B]): (List[A], List[B]) = {
xs match {
case Nil => (accLeft.reverse, accRight.reverse)
case Left(a) :: xr => loop(xr, a :: accLeft, accRight)
case Right(b) :: xr => loop(xr, accLeft, b :: accRight)
}
}
loop(origin, Nil, Nil)
}
If making two passes through the list is okay for you, you can use collect:
type E = Either[String, Int]
val xs: List[E] = List(Left("foo"), Right(1), Left("bar"), Right(2))
val rights = xs.collect { case Right(x) => x}
// rights: List[Int] = List(1, 2)
val lefts = xs.collect { case Left(x) => x}
// lefts: List[String] = List(foo, bar)
Using for comprehensions, like this,
for ( Left(v) <- xs ) yield v
and
for ( Right(v) <- xs ) yield v
If come across an interesting case with thunks versus functions in the presence of type Nothing:
object Test {
def apply(thunk: => Any ): String => Any = _ => thunk
def apply(fun: String => Any): String => Any = fun
}
val res0 = Test { println("hello") }
res0("foo") // --> hello
val res1 = Test { x: String => println(s"hello $x") }
res1("foo") // --> hello foo
val res2 = Test { x: String => throw new RuntimeException("ex") }
util.Try(res2("foo")) // --> Failure
val res3 = Test { throw new RuntimeException("ex") } // boom!
Now the tricky bit is the last case. Is it possible to modify the apply method to make Scala choose the thunk version of it, instead of the Function1 version (which is more specific and thus preferred, and Nothing <: Function1[_,_], therefore...)
I tried to come up with low-high priority implicits and magnet pattern, but haven't found a solution yet.
Here's a quick draft of a type-safe approach based on type classes:
object Test {
trait Wrap[A, B] { def apply(a: => A): String => B }
trait LowWrap {
implicit def thunkWrap[A] = new Wrap[A, A] { def apply(a: => A) = _ => a }
}
trait MidWrap extends LowWrap {
implicit def funcWrap[A] = new Wrap[String => A, A] {
def apply(f: => String => A) = f
}
}
object Wrap extends MidWrap {
implicit object nothingWrap extends Wrap[Nothing, Nothing] {
def apply(f: => Nothing) = _ => f
}
}
def apply[A, B](a: => A)(implicit w: Wrap[A, B]) = w(a)
}
And then:
scala> Test { println("hello") }
res0: String => Unit = <function1>
scala> res0("foo")
hello
scala> Test { x: String => println(s"hello $x") }
res2: String => Unit = <function1>
scala> res2("foo")
hello foo
scala> Test { x: String => throw new RuntimeException("ex") }
res4: String => Nothing = <function1>
scala> util.Try(res4("foo"))
res5: scala.util.Try[Nothing] = Failure(java.lang.RuntimeException: ex)
scala> Test { throw new RuntimeException("ex") }
res6: String => Nothing = <function1>
scala> util.Try(res6("foo"))
res7: scala.util.Try[Nothing] = Failure(java.lang.RuntimeException: ex)
You might be able to simplify a bit, add variance, etc.
Use:
val res3 = Test { (throw new RuntimeException("ex")): Any }
This works as expected. (No exception on creation, exception when calling res3).
A solution which uses reflection. (Still curious: can one write a static solution?)
import reflect.runtime.universe._
object Test {
def apply[A: TypeTag](thunk: => A): String => Any =
if (typeOf[A] =:= typeOf[Nothing]) // hah, caught you
_ => thunk
else if (typeOf[A] <:< typeOf[String => Any])
thunk.asInstanceOf[String => Any]
else
_ => thunk
}
val res0 = Test { println("hello") }
res0("foo") // --> hello
val res1 = Test { x: String => println(s"hello $x") }
res1("foo") // --> hello foo
val res2 = Test { x: String => throw new RuntimeException("ex") }
util.Try(res2("foo")) // --> Failure
val res3 = Test { throw new RuntimeException("ex") }
util.Try(res3("foo")) // --> Failure
(a macro based version of this would be static, I guess)