Below are the two partial function that are expected to perform sme tasks but defined in diffrent ways.
val pf1 : PartialFunction[String, String] = {
case s : String if (s != null) => s.toUpperCase()
}
//> pf1 : PartialFunction[String,String] = <function1>
val lift1 = pf1.lift
//> lift1 : String => Option[String] = <function1>
val d1 = lift1(null)
//> d1 : Option[String] = None
val d2 = lift1("hello world")
//> d2 : Option[String] = Some(hello world)
val pf2 = PartialFunction[String, String] {
case s : String if(s != null) => s.toUpperCase()
}
//> pf2 : PartialFunction[String,String] = <function1>
val lift2 = pf2.lift
//> lift2 : String => Option[String] = <function1>
val d3 = lift2(null)
//> scala.MatchError: null
val d4 = lift2("hii")
//> d4 : Option[String] = Some(hii)
Why passing null to lift2 gives MatchError, when the definition of both lift1 and lift2 is same?
If you look at PartialFunction.apply, you will see that it does a bit something different then what you've expected:
/** Converts ordinary function to partial one
* #since 2.10
*/
def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }
So it wraps a normal function into partial function that is defined on all domain. And that's why when you lift, you receive exception - because inside is still a normal not-lifted partial function, that is not defined
Why passing null to lift2 gives MatchError, when the definition of
both lift1 and lift2 is same?
They're not defined the same. When you define:
val pf1: PartialFunction[String, String] = {
case s : String if (s != null) => s.toUpperCase()
}
The compiler creates a PartialFunction, like this:
this.pf1 = ({
new <$anon: Function1>()
}: PartialFunction);
But when you declare pf2 like this:
val pf2 = PartialFunction[String, String] {
case s : String if(s != null) => s.toUpperCase()
}
You're actually telling the compiler "please take this Function1 and pass it to PartialFunction.apply. That is why the compiler does this:
this.pf2 = scala.PartialFunction.apply({
(new <$anon: Function1>(): Function1)
});
Which actually wraps a Function1 inside a partial function. So when you call pf2.lift(null), it will internally call the Function1, causing a MatchError as you're seeing.
Related
Suppose I have a function-like type e.g.
trait Parser[-Context, +Out]
and I want to be able to combine multiple parsers such that the combined Context will be the most-specific type among the combined parsers' contexts. For example:
Parser[Any, Int] + Parser[String, Long] = Parser[String, (Int, Long)]
Parser[String, Int] + Parser[Any, Long] = Parser[String, (Int, Long)]
Parser[Option[Int], Foo] + Parser[Some[Int], Bar] = Parser[Some[Int], (Foo, Bar)]
Parser[String, Foo] + Parser[Int, Bar] = <should be a compile error>
To put the example in more concrete terms, suppose I have a function combiner like
def zipFuncs[A, B1, B2](f1: A => B1, f2: A => B2): A => (B1, B2) = {
a => (f1(a), f2(a))
}
and some functions like
val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }
Now I can do
> zipFuncs(f1, f2)
res1: String => (Int, Int) = <function>
> zipFuncs(f1, f3)
res2: Option[Int] => (Int, Int) = <function>
> zipFuncs(f2, f3)
res3: Option[Int] with String => (Int, Int) = <function1>
But what I want is for zipFuncs(f2, f3) to not compile at all. Since String is not a subtype of Option[Int], and Option[Int] is not a subtype of String, there's no way to construct an input value for res3.
I did create a typeclass:
// this says type `T` is the most specific type between `T1` and `T2`
sealed trait MostSpecificType[T, T1, T2] extends (T => (T1, T2))
// implementation of `object MostSpecificType` omitted
def zipFuncs[A, A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)(
implicit mst: MostSpecificType[A, A1, A2]
): A => (B1, B2) = { a: A =>
val (a1, a2) = mst(a)
f1(a1) -> f2(a2)
}
This accomplishes the goal described above, but with a really annoying problem. IntelliJ will highlight valid combinations as errors, inferring that the "most specific type (A)" is actually Nothing when it is in fact a real value. Here's the actual issue in practice.
The highlighting issue is surely a bug in IntelliJ, and google searching seems to imply that various resets/cache wipes/etc should fix it (it didn't). Regardless of the blame, I'm hoping to find an alternate approach that both satisfies my original requirement, and doesn't confuse IntelliJ.
You can achieve that using generalized type constraints:
def zipFuncs[A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)
(implicit ev: A2 <:< A1): A2 => (B1, B2) = {
a => (f1(a), f2(a))
}
val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }
zipFuncs(f1, f2) // works
zipFuncs(f1, f3) // works
zipFuncs(f2, f3) // cannot prove that Option[Int] <:< String
However, this requires the second function to use a more specific type in the input parameter than the first one. This is OK unless you also want zipFuncs(f2, f1) to work too. If you do have that requirement, I don't see any other way than doing some implicit type gymnastics similar to the ones you already do.
EDIT: See Eduardo's answer for a neat trick on achieving this.
And yes, I also had a number of situations when IntelliJ sees something as an error when in fact it is not. I know it's tedious but I don't see a way to fix the situation other than reporting an issue and waiting.
If you want this to work only when one of the types is a subtype of the other, then you can do this:
def Zip[A,X,Y](f: A => X, g: A => Y): A => (X,Y) = a => (f(a), g(a))
implicit class ZipOps[A,X](val f: A => X) extends AnyVal {
def zip[A0, Y](g: A0 => Y)(implicit ev: A0 <:< A): A0 => (X,Y) =
Zip({a: A0 => f(a)},g)
def zip[A0 >: A, Y](g: A0 => Y): A => (X,Y) =
Zip(f,g)
}
val f1: Any => Int = { a: Any => 123 }
val f2: String => Int = { a: String => 123 }
val f3: Option[Int] => Int = { a: Option[Int] => 123 }
val x1 = f1 zip f2 // works
val x1swap = f2 zip f1 // works
val x2 = f1 zip f3 // works
val x3 = f2 zip f3 // cannot prove that Option[Int] <:< String
val x3swap = f3 zip f2 // cannot prove that String <:< Option[Int]
I don't know why the following code can not compile, this is the error message:
Error:(29, 7) no type parameters for method flatMap: (f: String => Option[B])Option[B] exist so that it can be applied to arguments (String => Some[Class[?0]] forSome { type ?0 <: org.apache.hadoop.io.compress.CompressionCodec })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : String => Some[Class[?0]] forSome { type ?0 <: org.apache.hadoop.io.compress.CompressionCodec }
required: String => Option[?B]
a.flatMap(codecClassName => {
^
and code
def f(a: Option[String]): Unit = {
a.flatMap(codecClassName => {
val codecFactory = new CompressionCodecFactory(new Configuration())
val codecClass = codecFactory.getCodecClassByName(codecClassName)
if (codecClass == null) {
throw new RuntimeException("Unknown or not supported codec:" + codecClassName)
}
Some(codecClass)
})
}
This seems to be related to the fact that getClass and classOf are not returning the exact same thing. See Scala equivalent of Java java.lang.Class<T> Object for more details.
Looking around for a workaround I came across Scala Getting class type from string representation.
So how about:
val codecClass = Manifest.classType(codecFactory.getCodecClassByName(codecClassName))
This should work. flatMap involves both map and flatten, so it may need more type annotations in some cases. The overall code works after annotation of the function parameter, i.e. (codecClassName: String).
Note the other change -- that flatMap with an inner function returning an Option type is the same as a map if that function returns what's inside the Option (i.e. flattens the option) (see below).
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.io.compress.{CompressionCodec, CompressionCodecFactory}
...
def f(a: Option[String]): Option[Class[_ <: CompressionCodec]] = {
a.map((codecClassName: String) => {
val codecFactory = new CompressionCodecFactory(new Configuration())
val codecClass = codecFactory.getCodecClassByName(codecClassName)
if (codecClass == null) {
throw new RuntimeException("Unknown or not supported codec:" + codecClassName)
}
codecClass
})
}
To show the relationship between flatMap and map as described above:
scala> val opt: Option[Int] = Some(1)
opt: Option[Int] = Some(1)
scala> opt.map((i: Int) => i + 1)
res0: Option[Int] = Some(2)
scala> val opt2: Option[Int] = None
opt2: Option[Int] = None
scala> opt.flatMap((i: Int) => Some(i + 1))
res1: Option[Int] = Some(2)
scala> opt2.map((i: Int) => i + 1)
res3: Option[Int] = None
scala> opt2.flatMap((i: Int) => Some(i + 1))
res2: Option[Int] = None
It's weird but my code prints u. Any ideas why does it do such stuff?
object PF extends App {
val f1: PartialFunction[Int, String] = {
case x: Int if x % 2 == 0 => "2"
}
val f2: PartialFunction[Int, String] = {
case x: Int if x % 3 == 0 => "3"
}
val f3: PartialFunction[Int, String] = {
case x: Int if x % 5 == 0 => "5"
}
val result = f1.orElse(f2.orElse(f3.orElse("<undef>")))
println(result.apply(1))
}
Your code interprets the string "" as PartialFunction:
val result: PartialFunction[Int, String] = "<undef>"
result.apply(1) // second character of "<undef>" --> u
This happens through an implicit conversion from String to WrappedString which is a subtype of Seq[Char]. Further, Seq[T] is a subtype of PartialFunction[Int, T] (given an index, get an element of the Seq if it exists).
The last line reaches this case, since 1 is not divisible by any of 2,3,5 (so it falls through f1, f2 and f3).
What you would want instead is applyOrElse:
val fun = f1 orElse f2 orElse f3
fun.applyOrElse(1, "<undef>") // --> "<undef>"
Alternatively, you can specify a fallback partial function:
val result = f1 orElse f2 orElse f3 orElse {
case _ => "<undef>"
}
I have the following function
scala> def f1 = (prefix: String) => prefix + ".field"
f1: String => java.lang.String
And I'd like to define another function from f1, that fixed the value of prefix to p1, like this
def f2: () => String = () => f1("p1")
or more shortly
def f2 = () => f1("p1")
I think that the same could be achieved using Function.curried or f.curried and partialy applied functions, but I still couldn't do it...
--
Having a look a this article I found a more verbose way of defining it. I guess the above syntax is just suger for this longer form...
scala> object f2 extends Function0[String] {
| override def apply = f1("p1")
| }
defined module f2
scala> f2
res37: f2.type = <function0>
scala> f2()
res38: java.lang.String = p1.field
You can only "curry" functions with more than one argument. At least with the methods that Scala provides by default. With two parameters it works like this:
val f2 = (prefix: String, foo:String) => prefix + ".field"
val f1 = f2.curried("p1")
If you want to do the same with Function1, you can "pimp" the class to add a new method:
implicit def addCurry[A,B](f:Function[A,B]) = new Function1WithCurried(f)
class Function1WithCurried[-A,+B](f:Function1[A,B]) {
def curried:Function1[A,Function0[B]] = (x:A) => { () => f(x) }
}
def f1 = (prefix: String) => prefix + ".field"
val f0 = f1.curried
val f2 = f1.curried("p1")
Extending #Kim Stebel's answer bit further. You can do this:
scala> val f2 = (prefix: String, foo:String) => prefix + ".field"
f2: (String, String) => java.lang.String = <function2>
scala> val f1 = f2("p1",_:String)
f1: String => java.lang.String = <function1>
And to make it clear, what we are doing here it not currying, it's 'partial application'.
If what you're looking for is currying, you could do it even more verbose like this:
scala> def f1(f: String => String)(s: String) = f(s)
f1: (f: String => String)(s: String)String
scala> def f2 = () => f1(p => p + ".field")("p1")
f2: () => String
scala> f2()
res4: String = p1.field
In Scala, the PartialFunction[A, B] class is derived from type Function[A, B] (see Scala Reference, 12.3.3). However, this seems counterintuitive to me, since a Function (which needs to be defined for all A) has more stringent requirements than a PartialFunction, which can be undefined at some places.
The problem I've come across is that when I have a partial function, I cannot use a Function to extend the partial function. Eg. I cannot do:
(pf orElse (_)=>"default")(x)
(Hope the syntax is at least remotely right)
Why is this subtyping done reversely? Are there any reasons that I've overlooked, like the fact that the Function types are built-in?
BTW, it would be also nice if Function1 :> Function0 so I needn't have the dummy argument in the example above :-)
Edit to clarify the subtyping problem
The difference between the two approaches can be emphasized by looking at two examples. Which of them is right?
One:
val zeroOne : PartialFunction[Float, Float] = { case 0 => 1 }
val sinc = zeroOne orElse ((x) => sin(x)/x) // should this be a breach of promise?
Two:
def foo(f : (Int)=>Int) {
print(f(1))
}
val bar = new PartialFunction[Int, Int] {
def apply(x : Int) = x/2
def isDefinedAt(x : Int) = x%2 == 0
}
foo(bar) // should this be a breach of promise?
Because in Scala (as in any Turing complete language) there is no guarantee that a Function is total.
val f = {x : Int => 1 / x}
That function is not defined at 0. A PartialFunction is just a Function that promises to tell you where it's not defined. Still, Scala makes it easy enough to do what you want
def func2Partial[A,R](f : A => R) : PartialFunction[A,R] = {case x => f(x)}
val pf : PartialFunction[Int, String] = {case 1 => "one"}
val g = pf orElse func2Partial{_ : Int => "default"}
scala> g(1)
res0: String = one
scala> g(2)
res1: String = default
If you prefer, you can make func2Partial implicit.
PartialFunction has methods which Function1 does not, therefore it is the subtype. Those methods are isDefinedAt and orElse.
Your real problem is that PartialFunctions are not inferred sometimes when you'd really like them to be. I'm hopeful that will be addressed at some future date. For instance this doesn't work:
scala> val pf: PartialFunction[String, String] = { case "a" => "foo" }
pf: PartialFunction[String,String] = <function>
scala> pf orElse { case x => "default" }
<console>:6: error: missing parameter type for expanded function
((x0$1) => x0$1 match { case (x # _) => "default" })
But this does:
scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>
Of course you could always do this:
scala> implicit def f2pf[T,R](f: Function1[T,R]): PartialFunction[T,R] =
new PartialFunction[T,R] {
def apply(x: T) = f(x)
def isDefinedAt(x: T) = true
}
f2pf: [T,R](f: (T) => R)PartialFunction[T,R]
And now it's more like you want:
scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>
scala> println(res7("a") + " " + res7("quux"))
foo default