Is it possible to do match-case over functions?
I want to define a behavior for different types of functions. Say I have the following possibilities:
f: T => Int
f: T => String
f: T => Lis[Int]
f: T => Boolean
f: T => Double
...
and for each of these options I have a function; for example for Int output:
def doThisForInt(f: T => Int) = { ... }
and this for Boolean output:
`
def doThisForBoolean(f: T => Boolean) = { ... }
So now suppose a function definition is given: val f = (input: T) => true. We should choose the corresponding case to f: T => Boolean.
Note that all these functions differ in the output type. Alternatively, given f can I get the output type of this function?
TypeTags are what you are looking for:
import scala.reflect.runtime.universe._
def doThisForInt(f: T => Int) = ???
def printType[R: TypeTag](f: T => R) = typeOf[R] match {
case t if t =:= typeOf[Int] =>
val toInt: (T) => Int = f.asInstanceOf[T => Int]
doThisForInt(toInt)
case t if t =:= typeOf[Double] =>
// ...
case t if t =:= typeOf[List[Int]] =>
// ...
}
printType((x: T) => 1) // int
printType((x: T) => 2.0) // double
printType((x: T) => List(2)) // list
As you can see, it is possible, but not very elegant and against good practices.
Chains of instanceOf checks can often be replaced with virtual methods (see the example) and the result type of function can possibly be a type parameter. It's hard to give more advice without knowing more context for your use case.
Related
I asked this question earlier: Combine a PartialFunction with a regular function
and then realized, that I haven't actually asked it right.
So, here goes another attempt.
If I do this:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse { case x => x.toString }
it does not compile: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: PartialFunction[?,?]
But this works fine:
val x: Seq[String] = List(1,2,3).collect { case x => x.toString }
The question is what is the difference? The type of the argument is the same in both cases: PartialFunction[Int, String]. The value passed in is literally identical. Why one does one case work, but not the other?
You need to specify the type for bar because the compiler is unable to infer it. This compiles:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar : (Int => String) = foo orElse { case x => x.toString }
In the case of List(1,2,3).collect{case x => x.toString} the compiler is able to infer the input type of the partial function based off of how theList was typed.
final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That])
Based on the type parameters the compiler can infer that you are passing a correctly typed partial function. That's why List(1,2,3).collect{case x:String => x.toString} does not compile nor does List(1,2,3).collect{case x:Int => x.toString; case x: String => x.toString}.
Since List is covariant the compiler is able to infer that the partial function {case x => x.toString} is a partial function on Int. You'll notice that List(1,2,3).collect{case x => x.length} does not compile because the compiler is inferring that you're operating on either an Int or a subclass of Int.
Also keep in mind that the {case x => x.toString} is just syntactic sugar. If we do something like the below then your example works as expected
val f = new PartialFunction[Int, String](){
override def isDefinedAt(x: Int): Boolean = true
override def apply(v1: Int): String = v1.toString
}
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse f //This compiles fine.
List(1,2,3).collect{f} // This works as well.
So the only logical answer from my perspective is that the syntactic sugar that is able to generate a PartialFunction instance for {case x => x.toString} does not have enough information at compile time to be able to adequately type it as a PartialFunction[Int, String] in your orElse case.
You can use the library Extractor.scala.
import com.thoughtworks.Extractor._
// Define a PartialFunction
val pf: PartialFunction[Int, String] = {
case 1 => "matched by PartialFunction"
}
// Define an optional function
val f: Int => Option[String] = { i =>
if (i == 2) {
Some("matched by optional function")
} else {
None
}
}
// Convert an optional function to a PartialFunction
val pf2: PartialFunction[Int, String] = f.unlift
util.Random.nextInt(4) match {
case pf.extract(m) => // Convert a PartialFunction to a pattern
println(m)
case f.extract(m) => // Convert an optional function to a pattern
println(m)
case pf2.extract(m) => // Convert a PartialFunction to a pattern
throw new AssertionError("This case should never occur because it has the same condition as `f.extract`.")
case _ =>
println("Not matched")
}
I wrote a quite strange method in Scala.
def mystery(p: Int => String => Boolean): Int = ???
And now I cannot figure out what implementation and parameter value it should have.
So what is the simplest implementation this method can have? And what is the value we can pass to it?
My try:
def mystery(p: Int => String => Boolean): Int = {
val m = (x1: Int) => p(x1)
val n = (x2: String) => m(_)(x2)
val k = (x3: Boolean) => p(_)(_)(x3)
if (k) 1 else 0
}
How about this? p is a function, so to use it you need to pass it parameters. The parameters are curried, and the function is equivalent to Int => (String => Boolean). You give it an Int first, and it returns a function String => Boolean. So you give that a String, and it returns a Boolean.
def mystery(p: Int => String => Boolean): Int = {
val a = p(1)
val b = a("2")
if(b) 1 else 0
}
val f: Int => String => Boolean = (i: Int) => (s: String) => i.toString == s
mystery(f)
In your version, m, n, and k are all defined as functions. So, for example, when you say if (k) it doesn't make sense because if takes a Boolean, but you're giving it a function.
Is it possible to do something like the following?
def takeCurriedFnAsArg(f: (Int)(implicit MyClass) => Result)
Yes, it is possible.
When you have the second curried parameter marked as implicit, the function seems to be not of type
Int => (MyClass => Result) => ResultOfFunction
which it would be if the curried higher order function parameter was a regular parameter; instead, it looks like this:
Int => ResultOfFunction
Here's a quick example:
scala> def curriedFn(i : Int)(implicit func : String => Int) : Boolean = (i + func("test!")) % 2 == 0
curriedFn: (i: Int)(implicit func: String => Int)Boolean
scala> implicit val fn : String => Int = s => s.length
fn: String => Int = <function1>
scala> curriedFn _
res4: Int => Boolean = <function1>
As you can see, the implicit parameter got 'eliminated'. Why and how? That's a question for someone more knowledgeable than me. If I had to guess, I'd say the compiler directly substitutes the parameter with the implicit value, but that might very well be false.
Anyway, digressions aside, here's an example very relevant to your situation:
scala> def foo(func : Int => Boolean) = if(func(3)) "True!" else "False!"
foo: (func: Int => Boolean)String
scala> foo(curriedFn)
res2: String = True!
Now if the second function parameter wasn't implicit:
scala> def curriedNonImplicit(i : Int)(fn : String => Int) : Boolean = (i + fn("test!")) % 2 == 0
curriedNonImplicit: (i: Int)(fn: String => Int)Boolean
scala> curriedNonImplicit _
res5: Int => ((String => Int) => Boolean) = <function1>
As you can see, the type of the function is a bit different. That means that the solution will look different too:
scala> def baz(func : Int => (String => Int) => Boolean) = if(func(3)(s => s.length)) "True!" else "False!"
baz: (func: Int => ((String => Int) => Boolean))String
scala> baz(curriedNonImplicit)
res6: String = True!
You have to specify the function directly inside the method, as it wasn't implicitly provided before.
Does the Scala library provide any support for lifting a method of a given type to a function value?
For example, suppose I want to lift String.length. I can write
val f: String => Int = _.length
or
val f = { s: String => s.length }
However, this syntax is not always ideal (particularly in the midst of a larger expression). I think I'm looking for something that will enable expressions like
Lift[String](_.length)
Lift[Option[Int]].lift(_.filter)
and I have in mind something like this:
class Lift[T] {
def apply[R](f: T => R): T => R = f
def lift[A, R](f: (T) => (A) => R): (T, A) => R =
f(_)(_)
def lift[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
f(_)(_,_)
// ... etc. ...
}
object Lift {
def apply[T] = new Lift[T]
}
Question 1: Does the standard library (or any library) provide something like this?
Question 2: If not, is it possible to write it in such a way that Option.filter can be lifted as above (rather than as Lift[Option[Int]].lift[Int => Boolean, Option[Int]](_.filter))? Without supplying the type parameters on the lift method I get the following error:
error: missing parameter type for expanded function ((x$1) => x$1.filter)
Lift[Option[Int]].lift(_.filter)
^
Update:
Apparently, the problem I'm running in to has something to do with the overloaded lift method. If I rename the overloads, I can lift Option.filter without all the extra type parameters.
What is the problem with
(_: String).length
(_: Option[Int]).filter _
?
I finally came up with a solution that I'm happy with. This version supports simple syntax and a single entry point to the API, while also providing control over the form of the lifted function (i.e. uncurried, partly curried, or fully curried).
Examples:
I'll use the following class definition in the examples below:
class Foo {
def m1: Int = 1
def m2(i: Int): Int = i
def m3(i: Int, j: Int): Int = i + j
}
The simplest form of lifting is to return the method as a partially applied function, equivalent to invoking ((_: Foo).method _):
scala> lift[Foo](_.m1) // NOTE: trailing _ not required
res0: (Foo) => Int = <function1>
scala> lift[Foo](_.m2 _) // NOTE: trailing _ required
res1: (Foo) => (Int) => Int = <function1>
scala> lift[Foo](_.m3 _)
res2: (Foo) => (Int, Int) => Int = <function1> // NOTE: the result is partly curried
By importing some implicits, one can request curried or uncurried forms:
scala> {
| import CurriedLiftables._
| lift[Foo](_.m3 _)
| }
res3: (Foo) => (Int) => (Int) => Int = <function1>
scala> {
| import UncurriedLiftables._
| lift[Foo](_.m3 _)
| }
res4: (Foo, Int, Int) => Int = <function3>
Implementation:
class Lift[T] {
def apply[R,F](f: T => R)(implicit e: (T => R) Liftable F): F = e.lift(f)
}
object lift {
def apply[T] = new Lift[T]
}
class Liftable[From, To](val lift: From => To)
class DefaultLiftables {
implicit def lift[F]: F Liftable F = new Liftable(identity)
}
object Liftable extends DefaultLiftables
class UncurriedLiftable1 extends DefaultLiftables {
implicit def lift1[T, A, R]: (T => A => R) Liftable ((T, A) => R) =
new Liftable( f => f(_)(_) )
}
class UncurriedLiftable2 extends UncurriedLiftable1 {
implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable ((T, A1, A2) => R) =
new Liftable ( f => f(_)(_,_) )
}
// UncurriedLiftable3, UncurriedLiftable4, ...
object UncurriedLiftables extends UncurriedLiftable2
class CurriedLiftable2 extends DefaultLiftables {
implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable (T => A1 => A2 => R) =
new Liftable( f => (x: T) => (a1: A1) => (a2: A2) => f(x)(a1, a2) )
}
// CurriedLiftable3, CurriedLiftable4, ...
object CurriedLiftables extends CurriedLiftable2
My previous solution required a separate lift method for each arity:
import Lift._
val f1 = lift0[String](_.length)
val f2 = lift1[Option[Int]](_.filter)
val f3 = lift2[Either[String, Int]](_.fold)
Implementation:
class Lift0[T] {
def apply[R](f: T => R): T => R = f
}
class Lift1[T] {
def apply[A, R](f: (T) => (A) => R): (T, A) => R =
f(_)(_)
}
class Lift2[T] {
def apply[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
f(_)(_,_)
}
// ... etc. ...
object Lift {
def lift0[T] = new Lift0[T]
def lift1[T] = new Lift1[T]
def lift2[T] = new Lift2[T]
// ... etc. ...
}
Passing in filter as partially applied method seems to do the job:
scala> class Lift[T] {
| def apply[R](f: T => R): T => R = f
| }
defined class Lift
scala> object Lift {
| def apply[T] = new Lift[T]
| }
defined module Lift
scala> val ls = Lift[String](_.length)
ls: (String) => Int = <function1>
scala> val los = Lift[Option[Int]](_.filter _)
los: (Option[Int]) => ((Int) => Boolean) => Option[Int] = <function1>
I need define some case classes like the following one:
case class Gt(key: String, value: Any) extends Expression {
def evalute[V, E](f: String => Any) = {
def compare(v: Any): Boolean = {
v match {
case x: Number => x.doubleValue > value.asInstanceOf[Number].doubleValue
case x: Array[_] => x.forall(a => compare(a))
case x => x.toString > value.toString
}
}
compare(f(key))
}
}
i don't like repeat that for > < >= and <=
i also tried this:
trait Expression {
def evalute[V, E](f: String => Any) = true
def compare(v: Any, value: Any, cp: (Ordered[_], Ordered[_]) => Boolean): Boolean = {
v match {
case x: Number => cp(x.doubleValue, value.asInstanceOf[Number].doubleValue)
case x: Array[_] => x.forall(a => compare(a, value, cp))
case x => cp(x.toString, value.toString)
}
}
}
case class Gt(key: String, value: Any) extends Expression {
def evalute[V, E](f: String => Any) = {
compare(f(key), value, ((a, b) => a > b))
}
}
but that not working :(
error: could not find implicit value for parameter ord: scala.math.Ordering[scala.math.Ordered[_ >: _$1 with _$2]]
compare(f(key), value, ((a, b) => a > b))
Is there a way that pass a operator as a function in scala?
(a, b) => a > b works fine. Your problem is with the types.
What are V and E in evalute[V, E] supposed to be?
You pass it (a, b) => a > b as the parameter cp: (Ordered[_], Ordered[_]) => Boolean. So you have a: Ordered[_] and b: Ordered[_]. Which is the same as a: Ordered[X] forSome {type X} and b: Ordered[Y] forSome {type Y}. With these types, a > b doesn't make sense.
In Scala, those are not operators, but methods. You can lift any method to a function by putting an underscore after it. e.g.
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val f: (Int => Boolean) = 1 <= _
f: (Int) => Boolean = <function1>
scala> (0 to 2).map(f)
res0: scala.collection.immutable.IndexedSeq[Boolean] = Vector(false, true, true)
I'm not familiar with Scala, it does seem to have support for anonymous functions/lambdas: http://www.scala-lang.org/node/133