I don't understand why this code compiles :
def lift[A,B](f: A => B): Option[A] => Option[B] = _ map f
If I do :
val f: Int => Double = _.toDouble
Then lift(f) works fine, but f map f gives an error : error: value map is not a member of Int => Double
I don't understand why this code compiles :
def lift[A,B](f: A => B): Option[A] => Option[B] = _ map f
It's short for
def lift[A,B](f: A => B): Option[A] => Option[B] = (o: Option[A]) => o map f
Method lift returns a function that:
- takes an Option[A]
- modifies that option's contents using some function f: A => B
- returns the result, Option[B]
So, when you "lift" some function f: A => B, you basically upgrade it from A => B to Option[A] => Option[B]. When this new upgraded function is given o: Option[A], it will map that option's content into Option[B] (by using f: A => B which was provided when original function was being upgraded).
If I do:
val f: Int => Double = _.toDouble
Then lift(f) works fine, but f map f gives an error error: value map is > not a member of Int => Double
Yes, because you cannot map over a function. You can only map over something by using a function (by the way, things you can map over are called functors).
What you want to do is:
Define some function f (you defined val f: Int => Double = _.toDouble)
Use lift to "upgrade" you function f from Int => Double to Option[Int] => Option[Double]
Use your new upgraded function to map over an Option[Int], thereby transforming it into an Option[Double].
So:
val f: Int => Double = _.toDouble
val upgraded = lift(f)
println(upgraded(Some(8))) // prints Some(8.0)
The lift function simply lifts your function f: A=> B to the function on some other domain, in this case it is Option[_]. So, your implementation of lift gives you a function f': Option[A] => Option[B], and because Option has the map function, you can call it on the first input parameter (this is the 'underscore').
You don't have to call f map f. What you do is lifting your f function:
val f: Int => Double = _.toDouble
val lifted = lift(f)
lifted(Some(42)) //it should be Some(42.0)
Related
What I'm trying to do is apply a sequence of transformations to a dataset where each function takes the output of the previous step and transforms it for the next step. E.g.
val f1: Function1[Int, Double] = _ / 2d
val f2: Function1[Double, BigDecimal] = x=>BigDecimal(x - 2.1)
val f3: Function1[BigDecimal, String] = _.toString
val chained = (f1 andThen f2 andThen f3)(_)
println(chained(10))
What I want is a function f that takes an input a Seq(f1, f2, ...) and returns the chaining of them, where f1, f2, ...fn do not all have the same input and the same output types T. But they are composable, so for example:
f1: Function1[A,B]
f2: Function1[B,C]
f3: Function1[C,D]
then the chaining function will return a function
f: [A,D].
Thanks,
Z
Two solution proposals here:
A solution that requires a special kind of list that can keep track of all the types in the chain of functions.
An asInstanceOf-heavy solution which works on ordinary lists.
Keeping track of all the types of intermediate results
An ordinary list would lose track of the types of all the intermediate results. Here is a list of functions that keeps track of all those types:
sealed trait Func1List[-In, +Res] {
def ::[I, O <: In](h: I => O): Func1List[I, Res] = ConsFunc1(h, this)
}
object Func1List {
def last[In, Res](f: In => Res): Func1List[In, Res] = LastFunc1(f)
def nil[A]: Func1List[A, A] = LastFunc1(identity)
}
case class LastFunc1[-In, +Res](f: In => Res)
extends Func1List[In, Res]
case class ConsFunc1[-In, Out, +Res](head: In => Out, tail: Func1List[Out, Res])
extends Func1List[In, Res]
Now, for a Func1List, we can define a function that concatenates all the elements:
def andThenAll[A, Z](fs: Func1List[A, Z]): A => Z = fs match {
case LastFunc1(f) => f
case c: ConsFunc1[A, t, Z] => c.head andThen andThenAll[t, Z](c.tail)
}
A little test:
val f1: Function1[Int, Double] = _ / 2d
val f2: Function1[Double, BigDecimal] = x => BigDecimal(x - 2.1)
val f3: Function1[BigDecimal, String] = _.toString
val fs = f1 :: f2 :: Func1List.last(f3)
val f = andThenAll(fs)
println(f(42)) // prints 18.9
Just asInstanceOf all the things
A somewhat less refined, but much shorter solution:
def andThenAll[X, Y](fs: List[_ => _]): X => Y = fs match {
case Nil => (identity[X] _).asInstanceOf[X => Y]
case List(f) => f.asInstanceOf[X => Y]
case hd :: tl => hd match {
case f: Function1[X #unchecked, o] => f andThen andThenAll[o, Y](tl)
}
}
This here also results in 18.9:
println(andThenAll[Int, String](List(f1, f2, f3))(42))
As a part of learning Scala I try to implement Haskell's flip function (a function with signature (A => B => C) => (B => A => C)) in Scala - and implement it as a function (using val) and not as a method (using def).
I can implement it as a method, for instance this way:
def flip[A, B, C](f: (A, B) => C):((B, A) => C) = (b: B, a: A) => f(a, b)
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
However, when I try to implement it as a function, it does not work:
val flip = (f: ((Any, Any) => Any)) => ((a: Any, b: Any) => f(b, a))
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
When I try to compile this code, it fails with this message:
Error:(8, 18) type mismatch;
found : (Int, Int) => Int
required: (Any, Any) => Any
val f = flip(minus)
I understand why it fails: I try to pass (Int, Int) => Int where (Any, Any) => Any is expected. However, I don't know how to fix this problem. Is it possible at all?
Scala doesn't support polymorphic functions, unlike methods which are. This is due to the first class value nature of functions, which are simply instances of the FunctioN traits. These functions are classes, and they need the types to be bound at declaration site.
If we took the flip method and tried to eta expand it to a function, we'd see:
val flipFn = flip _
We'd get back in return a value of type:
((Nothing, Nothing) => Nothing) => (Nothing, Nothing) => Nothing
Due to the fact that none of the types were bound, hence the compiler resorts to the buttom type Nothing.
However, not all hope is lost. There is a library called shapeless which does allow us to define polymorphic functions via PolyN.
We can implement flip like this:
import shapeless.Poly1
object flip extends Poly1 {
implicit def genericCase[A, B, C] = at[(A, B) => C](f => (b: B, a: A) => f(a, b))
}
flip is no different from the FunctionN trait, it defines an apply method which will be called.
We use it like this:
def main(args: Array[String]): Unit = {
val minus = (a: Int, b: Int) => a - b
val f = flip(minus)
println(f(3, 5))
}
Yielding:
2
This would also work for String:
def main(args: Array[String]): Unit = {
val stringConcat = (a: String, b: String) => a + b
val f = flip(stringConcat)
println(f("hello", "world"))
}
Yielding:
worldhello
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.
I am not sure how the following function works:
def lift[A, B](f: A => B): Option[A] => Option[B] = _ map f
I know it transforms a function which takes a value of type A and returns a value of type B into a function which takes a value of type Option[A] and returns an Option[B]. But I am stuck on what the underscore is doing and how the map works. Can someone expand the function definition?
def lift[A, B](f: A => B): Option[A] => Option[B] = _ map f
it exapnds to
def lift[A, B](f: A => B): Option[A] => Option[B] = x => x map f
(this is how underscore works in this context)
The return type is annotated to be Option[A] => Option[B]. Thanks to this compiler will know that x is of type Option[A]. We can say it explicitly, expanding it further
def lift[A, B](f: A => B): Option[A] => Option[B] = (x: Option[A]) => x map f
So, the returned function takes an Option[A] and maps it using f. Option.map takes parameter that is a function A => B so everything is correct.
Note that since I annotated the type that function takes as a parameter you can now omit the type of lift method like this
def lift[A, B](f: A => B) = (x: Option[A]) => x map f
All the snippets I wrote in this post are equivalent.
_ map f in this case returns a function that takes an Option[A] and returns an Option[B].
The _ in this case is a shortcut to build and return a function by specifying its argument position.
So here, the position of the Option[A] argument.
Whenever this returned function will be further called, it would execute:
thePassedOptionA map f
I am trying to create the following lift function but I am hitting a brick wall:
scala> def liftOff[A,B](f : A => B) : A => Option[B] = _.map(f)
<console>:10: error: value map is not a member of type parameter A
def liftOff[A,B](f : A => B) : A => Option[B] = _.map(f)
I thought I could denote that the type parameter A is mappable but the mappable trait is now deprecated since 2.10. If the input type of the returned function is an Option it's not a problem but that is not what I need:
scala> def liftOff[A,B](f : A => B) : Option[A] => Option[B] = _ map f
liftOff: [A, B](f: A => B)Option[A] => Option[B]
Is there a way to do this?
If what you are trying to define is a method that takes a function from A to B, and returns a function from A to Option[B], then you just need to combine f with a function to enclose the result in an Option, such as:
def liftOff[A,B](f : A => B) : A => Option[B] = f andThen Option.apply
if this isn't what you meant, then you might need to edit your question to make your intent clearer.
Another option is to lift* it to the Option type first:
scala> def liftOff[A,B](f : A => B) : A => Option[B] = Some(_) map f
liftOff: [A, B](f: A => B)A => Option[B]
scala> liftOff((x:Int) => 2.0 * x)(3)
res6: Option[Double] = Some(6.0)
*Note: Here Some is the unit/return method lifting A to a monadic (Option) type.