Keeping intermediate results of function composition - scala

Suppose I've got the following three functions:
val f1: Int => Option[String] = ???
val f2: String => Option[Int] = ???
val f3: Int => Option[Int] = ???
I can compose them as follows:
val f: Int => Option[Int] = x =>
for {
x1 <- f1(x)
x2 <- f2(x1)
x3 <- f3(x2)
} yield x3
Suppose now that I need to keep the intermediate results of execution f1, f2, f3 and pass them to the caller:
class Result(x: Int) {
val r1 = f1(x)
val r2 = r1 flatMap f2
val r3 = r2 flatMap f3
def apply = r3
}
val f: Int => Result = x => new Result(x)
Does it make sense ? How would you improve/simplify this solution ?

Homogenous List
It's pretty simple for single type, suppose
val g1: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
val g2: Int => Option[Int] = x => Some(x * 3 + 1)
val g3: Int => Option[Int] = x => if (x >= 4) Some(x - 4) else None
You can define
def bind[T]: (Option[T], T => Option[T]) => Option[T] = _ flatMap _
def chain[T](x: T, fs: List[T => Option[T]]) = fs.scanLeft(Some(x): Option[T])(bind)
And now
chain(4, g1 :: g2 :: g3 :: Nil)
will be
List(Some(4), Some(2), Some(7), Some(3))
preserving all intermediate values.
Heterogenous List
But we can do if there are multiple types involved?
Fortunately there is shapeless library for special structures named Heterogenous List which could handle list-like multi-typed sequences of values.
So suppose we have
import scala.util.Try
val f1: Int => Option[String] = x => Some(x.toString)
val f2: String => Option[Int] = x => Try(x.toInt).toOption
val f3: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
Lets define heterogenous analogues to previous functions:
import shapeless._
import ops.hlist.LeftScanner._
import shapeless.ops.hlist._
object hBind extends Poly2 {
implicit def bind[T, G] = at[T => Option[G], Option[T]]((f, o) => o flatMap f)
}
def hChain[Z, L <: HList](z: Z, fs: L)
(implicit lScan: LeftScanner[L, Option[Z], hBind.type]) =
lScan(fs, Some(z))
And now
hChain(4, f1 :: f2 :: f3 :: HNil)
Evaluates to
Some(4) :: Some("4") :: Some(4) :: Some(2) :: HNil
Class converter
Now if you urged to save your result in some class like
case class Result(init: Option[Int],
x1: Option[String],
x2: Option[Int],
x3: Option[Int])
You could easily use it's Generic representation
just ensure yourself that
Generic[Result].from(hChain(4, f1 :: f2 :: f3 :: HNil)) ==
Result(Some(4),Some("4"),Some(4),Some(2))

Related

Composing a sequence of functions of variable types in scala

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))

Combine 2 partial functions

I have two partial functions returning unit (f1, f2). For instance, something like that:
val f1 = {
case s: arg => //do some
//etc... lots of cases
}
val f2 = {
case s: anotherArg => //do some
//lots of cases
}
Is there a concise way to compose this to partial functions the way as that if
f(x) = {f1(x); f2(x)} iff f1.isDefinedAt(x) && f2.isDefinedAt(x)
f(x) = f1(x); iff f1.isDefinedAt(x) && !f2.isDefinedAt(x)
f(x) = f2(x); iff !f1.isDefinedAt(x) && f2.isDefinedAt(x)
orElse
f1 orElse f2
Scala REPL
scala> val f: PartialFunction[Int, Int] = { case 1 => 1 }
f: PartialFunction[Int,Int] = <function1>
scala> val g: PartialFunction[Int, Int] = { case 2 => 2 }
g: PartialFunction[Int,Int] = <function1>
scala> val h = f orElse g
h: PartialFunction[Int,Int] = <function1>
scala> h(1)
res3: Int = 1
scala> h(2)
res4: Int = 2
scala> h.isDefinedAt(1)
res6: Boolean = true
scala> h.isDefinedAt(2)
res7: Boolean = true
Both both functions to execute on common cases
Using List of partial functions and foldLeft
Scala REPL
scala> val f: PartialFunction[Int, Int] = { case 1 => 1 case 3 => 3}
f: PartialFunction[Int,Int] = <function1>
scala> val g: PartialFunction[Int, Int] = { case 2 => 2 case 3 => 3}
g: PartialFunction[Int,Int] = <function1>
scala> val h = f orElse g
h: PartialFunction[Int,Int] = <function1>
scala> h(3)
res10: Int = 3
scala> h(3)
res11: Int = 3
scala> val h = List(f, g)
h: List[PartialFunction[Int,Int]] = List(<function1>, <function1>)
scala> def i(arg: Int) = h.foldLeft(0){(result, f) => if (f.isDefinedAt(arg)) result + f(arg) else result }
i: (arg: Int)Int
scala> i(3)
res12: Int = 6
Although pamu's answer is good, I don't like the fact that it is bound to specific Int type. Unfortunately you didn't specify result type well enough, so I see 3 alternatives:
You want to get list of all results of all defined functions and you don't care about which function produced which result. In this case something like this would work:
def callAll[A, B](funcs: List[PartialFunction[A, B]], a: A): List[B] = funcs.foldRight(List.empty[B])((f, acc) => if (f.isDefinedAt(a)) f.apply(a) :: acc else acc)
if order of elements is not important you may use
def callAll[A, B](funcs: List[PartialFunction[A, B]], a: A): List[B] = funcs.foldLeft(List.empty[B])((f, acc) => if (f.isDefinedAt(a)) f.apply(a) :: acc else acc)
which probably will be a bit faster
You want to get Option with Some in case corresponding function is defined at the point or None otherwise. In such case something like this would work:
def callAllOption[A, B](funcs: List[PartialFunction[A, B]], a: A): List[Option[B]] = funcs.map(f => f.lift.apply(a))
If you don't want to create List explicitly, you can use varargs such as:
def callAllOptionVarArg[A, B](a: A, funcs: PartialFunction[A, B]*): List[Option[B]] = funcs.map(f => f.lift.apply(a)).toList
or such curried version to specify value after functions:
def callAllOptionVarArg2[A, B](funcs: PartialFunction[A, B]*)(a: A): List[Option[B]] = funcs.map(f => f.lift.apply(a)).toList
You call functions purely for side effects and return value is not important, in which case you can safely use second (a bit faster) callAll definition
Examples:
val f: PartialFunction[Int, Int] = {
case 1 => 1
case 3 => 3
}
val g: PartialFunction[Int, Int] = {
case 2 => 2
case 3 => 4
}
val fl = List(f, g)
println(callAll(fl, 1))
println(callAll(fl, 3))
println(callAllOption(fl, 2))
println(callAllOptionVarArg(1, f, g))
println(callAllOptionVarArg2(f, g)(3))
List(1)
List(3, 4)
List(None, Some(2))
List(Some(1), None)
List(Some(3), Some(4))

Functions composition, which accumulates intermediate results

Suppose I have a few functions Int => Option[Int]:
val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None
Now I would like to compose them and make a new function, which accumulates the intermediate results, i.e. the results of f1, f2, and f3.
So I add a new class Accumulator:
class Accumulator(x: Int) {
val ox1 = f1(x)
val ox2 = ox1.flatMap(f2)
val ox3 = ox2.flatMap(f3)
def apply() = ox3
}
val f = {x => new Accumulator(x)}
Now I can see all intermediate results of the computation:
scala> f(0)
res18: X = $$$a5cddfc4633c5dd8aa603ddc4f9aad5$$$$w$X#10596df6
scala> res18.ox1
res19: Option[Int] = Some(1)
scala> res18.ox2
res20: Option[Int] = Some(3)
scala> res18()
res21: Option[Int] = Some(6)
I do not like this approach because it requires a new class for every computation. Could you suggest another approach to write a function f composed from f1, f2, and f3 that return also the intermediate results, i.e. the results of f1, f2, and f3 calls.
You could use .scanLeft on a List of Function, which (from the docs):
Produces a collection containing cumulative results of applying the
operator going left to right.
scala> val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
f1: Int => Option[Int] = <function1>
scala> val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
f2: Int => Option[Int] = <function1>
scala> val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None
f3: Int => Option[Int] = <function1>
scala> val fList = List(f1,f2,f3)
fList: List[Int => Option[Int]] = List(<function1>, <function1>, <function1>)
scala> val composed = fList.scanLeft((x:Int) => Option(x)) {
case (composedFun, f) => (x:Int) => (composedFun(x)) flatMap f
}.tail
composedFunctions: List[Int => Option[Int]] = List(<function1>, <function1>, <function1>)
scala> composed.map(_(2))
res24: List[Option[Int]] = List(Some(3), Some(5), Some(8))
scala> composed.map(_(8))
res25: List[Option[Int]] = List(Some(9), Some(11), None)
Note that I had to introduce an initial value (z, here (x:Int) => Option(x)).
You might want to write a function that takes a list of functions and uses funList.head as the initial value (and calls .scanLeft on funList.tail instead of funList).
Why not use a foldLeft with a list of functions?
def accumulate(x: Int, funcs: List[Int => Option[Int]]): List[Option[Int]] = funcs.foldLeft(List[Option[Int]]()) {
case (Nil, func) => List(func(x))
case (res :: tail, func) => res.flatMap(func) :: res :: tail
}.reverse
val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None
accumulate(0, List(f1, f2, f3))
This gives List[Option[Int]] = List(Some(1), Some(3), Some(6)).
EDIT:
As Marth pointed out, there's a dedicated function for this - scanLeft, however, I'd like to propose a different approach to using it. Make the initial value your input parameter instead of a function:
def accumulate(x: Int, funcs: List[Int => Option[Int]]): List[Option[Int]] =
funcs.scanLeft(Option(x)) {
case (acc, op) => acc.flatMap(op)
}.tail
val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None
accumulate(0, List(f1, f2, f3))

Scala match function against variable

When I'm matching value of case classes, such as:
sealed abstract class Op
case class UOp[T, K](f: T => K) extends Op
case class BOp[T, Z, K](f: (T, Z) => K) extends Op
like this:
def f(op: Op): Int =
op match
{
case BOp(g) => g(1,2)
case UOp(g) => g(0)
}
the compiler infers it as
val g: (Nothing, Nothing) => Any
val g: Nothing => Any
Why am I getting Nothing as the type? Is it because of JVM type erasure? Are there elegant ways to match functions against variables?
I came up with this "hackish" solution, maybe there are other ways or cleaner ways to do this still without relying on reflection.
Define a few partial functions which will handle various args:
scala> val f: PartialFunction[Any, String] = { case (x: Int, y: String) => y * x }
f: PartialFunction[Any,String] = <function1>
scala> val g: PartialFunction[Any, String] = { case x: Int => x.toString }
g: PartialFunction[Any,String] = <function1>
scala> def h: PartialFunction[Any, BigDecimal] = { case (a: Int, b: Double, c: Long) => BigDecimal(a) + b + c }
h: PartialFunction[Any,BigDecimal]
scala> val l: List[PartialFunction[Any, Any]] = f :: g :: h :: Nil
l: List[PartialFunction[Any,Any]] = List(<function1>, <function1>, <function1>)
Check which functions can handle different inputs:
scala> l.map(_.isDefinedAt(1))
res0: List[Boolean] = List(false, true, false)
scala> l.map(_.isDefinedAt((1, "one")))
res1: List[Boolean] = List(true, false, false)
Given input find and apply a function:
scala> def applyFunction(input: Any): Option[Any] = {
| l find (_.isDefinedAt(input)) map (_ (input))
| }
applyFunction: (input: Any)Option[Any]
scala> applyFunction(1)
res1: Option[Any] = Some(1)
scala> applyFunction((2, "one"))
res2: Option[Any] = Some(oneone)
scala> applyFunction("one")
res3: Option[Any] = None
scala> applyFunction(1, 1.1, 9L)
res10: Option[Any] = Some(11.1)
This looks quite type unsafe and there must be better ways to do this.
I think magnet pattern should handle this well in more typesafe manner.

Composing Futures and Options

I have four types A,B, C and D, an initial value x of the type Future[Option[A]] and three functions: f1: A => Option[B] , f2: B => Future[Option[C]] and f3: C => D.
How can I write a for comprehension starting with x that results in a value of the type Future[Option[D]] that would be the "composition" of the three functions?
You can use monad transformers (from Scalaz) for this:
import scalaz.OptionT
import scalaz.std.option._
import scalaz.syntax.monad._
val result: Future[Option[D]] = (for {
a <- OptionT(x)
b <- OptionT(f1(a).point[Future])
c <- OptionT(f2(b))
} yield f3(c)).run
You'll need a monad instance for Future; there's one in scalaz-contrib.
This isn't necessarily the best solution, but it's what I've come up with.
I started by trying to find a common type to work with
type N[X, Y] = Option[X] => Future[Option[Y]]
...then converting f1, f2, and f3 to that common type.
val f1: (A => Option[B]) = ???
val f1N: N[A, B] = {
case None => Future.successful(None)
case Some(a) => Future.successful(f1(a))
}
val f2: (B => Future[Option[C]]) = ???
val f2N: N[B, C] = {
case None => Future.successful(None)
case Some(b) => f2(b)
}
val f3: C => D = ???
val f3N: N[C, D] = {
case None => Future.successful(None)
case Some(c) => Future.successful(Some(f3(c)))
}
Now that I've created f1N, f2N, and f3N, I can use them in a nice-looking for-comprehension.
val y: Future[Option[D]] = for {
aOpt <- x
bOpt <- f1N(aOpt)
cOpt <- f2N(bOpt)
dOpt <- f3N(cOpt)
} yield dOpt