Scala + Shapeless abstract over curried function - scala

I'm trying to figure out how to abstract over a curried function.
I've can abstract over an uncurried function via:
def liftAU[F, P <: Product, L <: HList, R, A[_]](f: F)
(implicit
fp: FnToProduct.Aux[F, L => R],
gen: Generic.Aux[P, L],
ap: Applicative[A]
): A[P] => A[R] = p => p.map(gen.to).map(f.toProduct)
This will take a function like (Int, Int) => Int and turn it into something like Option[(Int, Int)] => Option[Int]. And it works for any arity of function.
I want to create the curried version which will take a function like Int => Int => Int and convert it to Option[Int] => Option[Int] => Option[Int].
It should also work for any arity of curried function.
Since FnToProduct only works on the first parameter list, it's not helpful here, I've also tried to write some recursive definitions at the typelevel, but I'm having issues defining the types.
Not really sure if its possible, but would love to know if others have tried anything like this.

Dmytro's answer doesn't actually work for me unless I change the instance names in one of the objects, and even then it doesn't work for a function like Int => Int => Int => Int, and I find working with Poly values really annoying, so instead of debugging the previous answer, I'm just going to write my own.
You can actually write this operation pretty nicely using a 100% Shapeless-free type class:
import cats.Applicative
trait LiftCurried[F[_], I, O] {
type Out
def apply(f: F[I => O]): F[I] => Out
}
object LiftCurried extends LowPriorityLiftCurried {
implicit def liftCurried1[F[_]: Applicative, I, I2, O2](implicit
lc: LiftCurried[F, I2, O2]
): Aux[F, I, I2 => O2, F[I2] => lc.Out] = new LiftCurried[F, I, I2 => O2] {
type Out = F[I2] => lc.Out
def apply(f: F[I => I2 => O2]): F[I] => F[I2] => lc.Out =
(Applicative[F].ap(f) _).andThen(lc(_))
}
}
trait LowPriorityLiftCurried {
type Aux[F[_], I, O, Out0] = LiftCurried[F, I, O] { type Out = Out0 }
implicit def liftCurried0[F[_]: Applicative, I, O]: Aux[F, I, O, F[O]] =
new LiftCurried[F, I, O] {
type Out = F[O]
def apply(f: F[I => O]): F[I] => F[O] = Applicative[F].ap(f) _
}
}
It's probably possible to make that a little cleaner but I find it reasonable readable as it is.
You might want to have something concrete like this:
def liftCurriedIntoOption[I, O](f: I => O)(implicit
lc: LiftCurried[Option, I, O]
): Option[I] => lc.Out = lc(Some(f))
And then we can demonstrate that it works with some functions like this:
val f: Int => Int => Int = x => y => x + y
val g: Int => Int => Int => Int = x => y => z => x + y * z
val h: Int => Int => Int => String => String = x => y => z => _ * (x + y * z)
And then:
scala> import cats.instances.option._
import cats.instances.option._
scala> val ff = liftCurriedIntoOption(f)
ff: Option[Int] => (Option[Int] => Option[Int]) = scala.Function1$$Lambda$1744/350671260#73d06630
scala> val gg = liftCurriedIntoOption(g)
gg: Option[Int] => (Option[Int] => (Option[Int] => Option[Int])) = scala.Function1$$Lambda$1744/350671260#2bb9b82c
scala> val hh = liftCurriedIntoOption(h)
hh: Option[Int] => (Option[Int] => (Option[Int] => (Option[String] => Option[String]))) = scala.Function1$$Lambda$1744/350671260#45eec9c6
We can also apply it a couple more times just for the hell of it:
scala> val hhhh = liftCurriedIntoOption(liftCurriedIntoOption(hh))
hhh: Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[String]]] => Option[Option[Option[String]]]))) = scala.Function1$$Lambda$1744/350671260#592593bd
So the types look okay, and for the values…
scala> ff(Some(1))(Some(2))
res0: Option[Int] = Some(3)
scala> ff(Some(1))(None)
res1: Option[Int] = None
scala> hh(Some(1))(None)(None)(None)
res2: Option[String] = None
scala> hh(Some(1))(Some(2))(Some(3))(Some("a"))
res3: Option[String] = Some(aaaaaaa)
…which I think is what you were aiming for.

You can define recursive Poly
object constNone extends Poly1 {
implicit def zeroCase[In]: Case.Aux[In, Option[Int]] = at(_ => None)
implicit def succCase[In, In1, Out](implicit
cse: Case.Aux[In, Out]): Case.Aux[In1, In => Out] = at(_ => cse(_))
}
object transform extends Poly1 {
implicit def zeroCase: Case.Aux[Int, Option[Int]] = at(Some(_))
implicit def succCase[In, Out](implicit
cse: Case.Aux[In, Out],
noneCase: constNone.Case.Aux[In, Out]
): Case.Aux[Int => In, Option[Int] => Out] =
at(f => {
case Some(n) => cse(f(n))
case None => noneCase(f(0))
})
}
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(Some(2)) //Some(3)
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(None) //None
(transform((x: Int) => (y: Int) => x + y) _)(None)(Some(2)) //None

Related

How to flat nest tuple parameter in function?

I have a function g which need parameter (Int, (Int, Int)) => Int, and a flat function f0 (Int, Int, Int) => Int
I want to construct a function ft which can flat parameters of g to f0.
Here is the example:
val f0: ((Int, Int, Int)) => Int = (x: (Int, Int, Int)) => {
x._1 + x._2 + x._3
}
def g(f: ((Int, (Int, Int))) => Int): Int = f(1,(2,3))
def ft(f: ((Int, Int, Int)) => Int): ((Int, (Int, Int))) => Int = (p: (Int, (Int, Int))) => {
f(p._1, p._2._1, p._2._2)
}
// invoke it
g(ft(f0))
But I have several functions of nested tuples, and I don't want to transform each manually. For example, ((Int, Int), (Int, Int)) => Int to (Int, Int, Int, Int) => Int
Here said it could use shapeless
Then the new function would like
import shapeless._
import ops.tuple.FlatMapper
trait LowPriorityFlatten extends Poly1 {
implicit def default[T] = at[T](Tuple1(_))
}
object flatten extends LowPriorityFlatten {
implicit def caseTuple[P <: Product](implicit lfm: Lazy[FlatMapper[P, flatten.type]]) =
at[P](lfm.value(_))
}
def ft(f: ((Int, Int, Int)) => Int): ((Int, (Int, Int))) => Int = (p: (Int, (Int, Int))) => {
val a: (Int, Int, Int) = flatten(p).asInstanceOf[(Int, Int, Int)]
f(a)
}
Code above has two problem:
how to define function ft[A, B, C](f: A => C): B where A is a flatten type of B ?
flatten(p) will product type FlatMapper.this.Out and miss the type, so I use asInstanceOf to cast type here.
So, How to write a function to flatten any kind of nested tuple in a parameter?
The following code works in Scala 3:
scala> type Flat[T <: Tuple] <: Tuple = T match
| case EmptyTuple => EmptyTuple
| case h *: t => h match
| case Tuple => Tuple.Concat[Flat[h], Flat[t]]
| case _ => h *: Flat[t]
|
scala> def flat[T <: Tuple](v: T): Flat[T] = (v match
| case e: EmptyTuple => e
| case h *: ts => h match
| case t: Tuple => flat(t) ++ flat(ts)
| case _ => h *: flat(ts)).asInstanceOf[Flat[T]]
def flat[T <: Tuple](v: T): Flat[T]
scala> def ft[A <: Tuple, C](f: Flat[A] => C): A => C = a => f(flat(a))
def ft[A <: Tuple, C](f: Flat[A] => C): A => C
scala> val f0: ((Int, Int, Int)) => Int = x => x._1 + x._2 + x._3
scala> def g0(f: ((Int, (Int, Int))) => Int): Int = f(1,(2,3))
scala> g0(ft(f0))
val res0: Int = 6
Edit: Add scala2's version:
import shapeless._
import ops.tuple.FlatMapper
import syntax.std.tuple._
trait LowPriorityFlat extends Poly1 {
implicit def default[T] = at[T](Tuple1(_))
}
object Flat extends LowPriorityFlat {
implicit def caseTuple[P <: Product](implicit fm: FlatMapper[P, Flat.type]) =
at[P](_.flatMap(Flat))
}
type F[A, B] = FlatMapper.Aux[A, Flat.type, B]
def flatTup[T <: Product](t: T)(implicit lfm: FlatMapper[T, Flat.type]): lfm.Out =
FlatMapper[T, Flat.type].apply(t)
def flatFun[A <: Product, B <: Product, C](f: B => C)
(implicit lfm: F[A, B]): A => C =
a => f(flatTup(a))
val f0: ((Int, Double, Int, Double)) => Double = { case(i1, d1, i2, d2) => (i1 + i2) / (d1 + d2) }
def g0(f: (((Int, Double), (Int, Double))) => Double): Double = f((1, 2.0), (3, 4.0))
val r0 = g0(flatFun(f0))

How to define a lift function in Scala

Can someone help me, how to define this function:
def lift[A, B, T](op: (T,T) => T)(f: A => T, g: B => T): (A,B) => T = /* ... */
Perhaps
def lift[A, B, T](op: (T,T) => T)(f: A => T, g: B => T): (A,B) => T =
(a: A, b: B) => op(f(a), g(b))
which gives
def op(a: Int, b: Int): Int = a + b
def f(x: String): Int = x.toInt
def g(x: List[String]): Int = x.length
lift(op)(f,g)("41", List("2"))
// Int = 42

How to compose tupled unary functions by combining their input tuples

I've been playing around with shapeless for a bit now.
But, yesterday I got stuck when trying to compose tupled functions.
What I was looking into specifically is composing two unary functions f1: T => R and f2: R => U => S into f: TU => S where T is a TupleN and TU := (t1, ... , tn, u)
import shapeless.ops.tuple._
implicit class Composable[T <: Product, R](val f1: T => R) extends AnyVal{
def compose2[U, S](f2: R => U => S)(implicit p: Prepend[T, Tuple1[U]]): (p.Out => S) = {
// how to provide the two required implicits for Last[p.Out] and Init[p.Out]?
tu => f1.andThen(f2)(tu.init)(tu.last)
}
}
val f1: ((Int, Int)) => Int = x => x._1 * x._2
val f2: ((Int, Int, Int)) => Int = f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
What I've been struggling with is providing the implicit proof for the tuple operations last and init, so the above code won't compile!
From a logical perspective it feels trivial as result of Prepend, but I couldn't figure out a way. So any idea is welcome :)
Using shapeless's facilities to abstract over arity I got somehow closer:
import shapeless.ops.function.{FnFromProduct, FnToProduct}
import shapeless.{::, HList}
implicit class Composable[F](val f: F) extends AnyVal{
// the new param U is appended upfront
def compose2[I <: HList, R, U, S](f2: R => U => S)
(implicit ftp: FnToProduct.Aux[F, I => R], ffp: FnFromProduct[U :: I => S]): ffp.Out = {
ffp(list => f2.compose(ftp(f))(list.tail)(list.head))
}
}
val f1: (Int, Int) => Int = (x1,x2) => x1 * x2
val f2: (Int, Int, Int) => Int = f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
This works, but then again I was really looking for compose2 to work on unary tupled Function1s. Also, this results in f: (U, t1, ..., tn) => S rather than f: TU => S with TU := (t1, ... , tn, u).
As Miles says, this would be more convenient with an undo for Prepend, but since the length of the second part is fixed, an approach similar to the one in my other answer isn't too bad at all:
import shapeless.ops.tuple._
implicit class Composable[T <: Product, R](val f1: T => R) extends AnyVal {
def compose2[U, S, TU](f2: R => U => S)(implicit
p: Prepend.Aux[T, Tuple1[U], TU],
i: Init.Aux[TU, T],
l: Last.Aux[TU, U]
): (p.Out => S) =
tu => f1.andThen(f2)(i(tu))(l(tu))
}
And then:
scala> val f1: ((Int, Int)) => Int = x => x._1 * x._2
f1: ((Int, Int)) => Int = <function1>
scala> val f2: ((Int, Int, Int)) => Int =
| f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
f2: ((Int, Int, Int)) => Int = <function1>
scala> f2((2, 3, 4))
res1: Int = 10
The trick is adding the output of Prepend to the type parameter list for compose2—which will generally be inferred—and then using Prepend.Aux to make sure that it's inferred appropriately. You'll often find in Shapeless that you need to refer to the output type of a type class in other type class instances in the same implicit parameter list in this way, and the Aux type members make doing so a little more convenient.

Implicit lifting in scala

I want to implicitly convert functions from A => B to List[A] => List[B].
I wrote the following implicit definition:
implicit def lift[A, B](f: A => B): List[A] => List[B] = ...
Unfortunately, when I write the following code, implicit aren't applied:
val plusOne: (List[Int]) => List[Int] = (x: Int) => (x + 1)
If I annotate the function with explicit time, it works fine.
Why? How can I fix it?
UPDATE. It seems that the problem is specific to anonymous functions. Compare:
#Test
def localLiftingGenerics {
implicit def anyPairToList[X, Y](x: (X, Y)): List[X] => List[Y] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ("abc", 239)
}
#Test
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => Y): List[X] => List[Y] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ((x: String) => x.length)
}
The first one is compiled well. The second one is marked as error
According to The Scala Language Specification / Expressions / Anonymous Functions (6.23):
If the expected type of the anonymous function is of the form
scala.Functionn[S1, …, Sn, R], the expected type of e is R ...
So, the result type of the function will be inferred as List[Int] unless you separate the function definition from the function value assignment (to get rid of the expected type):
val function = (x: Int) => (x + 1)
val plusOne: (List[Int]) => List[Int] = function
or specify the function type explicitly:
val plusOne: (List[Int]) => List[Int] = ((x: Int) => (x + 1)): Int => Int
(Scala 2.9.1-1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_05)
A first observation: If you duplicate fun2ListFun and rename it to, e.g.,``fun2ListFun, you'll get
found : String => <error>
required: List[String] => List[Int]
Note that implicit conversions are not applicable because they are ambiguous:
both method fun2ListFun2 of type [X, Y](f: X => Y)List[X] => List[Y]
and method fun2ListFun of type [X, Y](f: X => Y)List[X] => List[Y]
are possible conversion functions from String => <error> to List[String] => List[Int]
val v: List[String] => List[Int] = ((x: String) => x.length)
It looks as if the compiler considers both implicits as applicable.
A second observation:
Splitting
val v: List[String] => List[Int] = ((x: String) => x.length) /* Error*/
into
val f = ((x: String) => x.length)
val v: List[String] => List[Int] = f /* Works */
makes the compiler happy.
The implicit conversion for the input value compiles. So we just have a problem for the output of the anonymous function
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => Y): List[X] => Y = throw new UnsupportedOperationException
val v: List[String] => Int = ((x: String) => x.length)
}
A possible fix using a second implicit conversion:
def localLiftingFuns {
implicit def fun2ListFun[X, Y](f: X => List[Y]): List[X] => List[Y] = throw new UnsupportedOperationException
implicit def type2ListType[X](x:X): List[X] = throw new UnsupportedOperationException
val v: List[String] => List[Int] = ((x: String) => x.length)
}
This version compiles.
Seems the compiler has hard time figuring what is going on with the type of the function. If you will give him a little help, it would work:
scala> implicit def lift[A,B](f: A => B) = (_:List[A]).map(f)
lift: [A, B](f: (A) => B)(List[A]) => List[B]
scala> val f: List[Int] => List[Int] = ((_:Int) + 1):(Int => Int)
f: (List[Int]) => List[Int] = <function1>

Lifting methods to function values in Scala

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>