Let's consider a specific example. I have lots of functions that take a variable number of arguments, and return a Seq[T]. Say:
def nonNeg(start: Int, count: Int): Seq[Int] =
Iterator.from(start).take(count).toSeq
For each one of those function, I need to create a "Java version" of that function, returns a java.util.List[T]. I can create the "Java version" of the above function with:
def javaNonNeg(start: Int, count: Int): java.util.List[Int] =
nonNeg(start, count).asJava
This is somewhat verbose, as the list of parameters is duplicated twice. Instead, I'd like to create a higher level function that takes as a parameter a function of the form of nonNeg (any number and type of arguments, returns a Seq[T]) and returns a function which takes the same arguments, but returns a java.util.List[T]. Assuming that function was called makeJava, I'd then be able to write:
def javaNonNeg = makeJava(nonNeg)
Can makeJava be written using Shapeless ability to abstracting over arity? If it can, how, and it not, why and how else can this be done?
It is possible to use Shapeless to avoid the boilerplate—you just need to turn the original method into a FunctionN using plain old eta expansion, then convert to a function taking a single HList argument, and then back to a FunctionN with the new result type:
import java.util.{ List => JList }
import shapeless._, ops.function._
import scala.collection.JavaConverters._
def makeJava[F, A, L, S, R](f: F)(implicit
ftp: FnToProduct.Aux[F, L => S],
ev: S <:< Seq[R],
ffp: FnFromProduct[L => JList[R]]
) = ffp(l => ev(ftp(f)(l)).asJava)
And then:
scala> def nonNeg(start: Int, count: Int): Seq[Int] =
| Iterator.from(start).take(count).toSeq
nonNeg: (start: Int, count: Int)Seq[Int]
scala> val javaNonNeg = makeJava(nonNeg _)
javaNonNeg: (Int, Int) => java.util.List[Int] = <function2>
scala> javaNonNeg(1, 4)
res0: java.util.List[Int] = [1, 2, 3, 4]
javaNonNeg is a Function2, so from Java you can use javaNonNeg.apply(1, 4).
For 2 and more (in code below to 4) parameters you can use implicit parameters feature, for resolve result type by input parameter type
sealed trait FuncRes[F] {
type Param
type Result
def func : F => Param => Result
}
class Func[T, R](fn : T => R) {
trait FR[F, P] extends FuncRes[F] { type Param = P; type Result = R }
implicit def func2[T1,T2] = new FR[(T1,T2) => T, (T1,T2)] {
def func = f => p => fn(f.tupled(p))
}
implicit def func3[T1,T2,T3] = new FR[(T1,T2,T3) => T, (T1,T2,T3)] {
def func = f => p => fn(f.tupled(p))
}
implicit def func4[T1,T2,T3,T4] = new FR[(T1,T2,T3,T4) => T, (T1,T2,T3,T4)] {
def func = f => p => fn(f.tupled(p))
}
def makeFunc[F](f : F)(implicit ev : FuncRes[F]): ev.Param => ev.Result =
ev.func(f)
}
and after your def javaNonNeg = makeJava(nonNeg) function will look like:
object asJavaFunc extends Func((_ : Seq[Int]).asJava)
import asJavaFunc._
def javaNonNeq = makeFunc(nonNeg _)
And of course it has some disadvantages, but generally it satisfy your needs.
Related
I need to write a function that will receive a function that will transform a List[T] into a T. For example, summing the elements of the list.
My first try was to use shapeless polymorphic functions as:
object sumPoly extends (List ~> Option) = {
def apply[T: Numeric](list: List[T]): Option = Some(list.sum)
}
but I get an error because shapeless expect the function to be def apply[T](...)
The function that receives the function above would look like:
def test(list: List[Any], f: (List ~> Option)) = {
val intTypeCase = TypeCase[List[Int]]
val doubleTypeCase = TypeCase[List[Double]]
val longTypeCase = TypeCase[List[Long]]
list match {
intTypeCase(l) => f(l)
doubleTypeCase(l) => f(l)
longTypeCase(l) => f(l)
// more type matchings here
}
...
}
Is there a way to achieve what I want to do?
EDIT
After some searches I found that it is possible to do the following:
object sumPoly extends Poly1 {
implicit def caseListInt = at[List[Int]](x => x.sum)
implicit def caseListDouble = at[List[Double]](x => x.sum)
// more type matchings here
}
and calling sumPoly(List(1, 1, 1)) correctly returns 3. But if I define the test function as:
def test(list: List[Any], f: Poly1) = {
val intTypeCase = TypeCase[List[Int]]
val doubleTypeCase = TypeCase[List[Double]]
val longTypeCase = TypeCase[List[Long]]
list match {
intTypeCase(l) => f(l)
doubleTypeCase(l) => f(l)
longTypeCase(l) => f(l)
// more type matchings here
}
...
}
and pass the sumPoly function, I get the errors like this for each type I defined in the test function: could not find implicit value for parameter cse: shapeless.poly.Case[f.type,shapeless.::[List[Int],shapeless.HNil]]
Any ideas?
By looking at shapeless' code I found I can do the following:
object sumPoly extends Poly1 {
implicit def caseList[T: Numeric] = at[List[T]] { x => x.sum }
}
and then the function which will receive the Poly1 must have the following definition:
def test(list: List[Any], f: Poly1)
(implicit li : f.Case[List[Int]],
ld : f.Case[List[Double]],
ll : f.Case[List[Long]])= {
val intTypeCase = TypeCase[List[Int]]
val doubleTypeCase = TypeCase[List[Double]]
val longTypeCase = TypeCase[List[Long]]
list match {
intTypeCase(l) => f(l)
doubleTypeCase(l) => f(l)
longTypeCase(l) => f(l)
// more type matchings here
}
...
}
this way, I can create different Poly1 functions like sum, prod, min, max that convert List[T] into a T where T is numeric.
The design might seem contrived, but I am interop-ing with Java. More specifically, with Hadoop libraries written in Java.
consider a simple function that operates on collections distinctBy, which, like distinct remove "duplicates" (which are not necessary actual duplicates):
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.{Set=>MSet}
def distinctBy[T,R,Coll]
(xs: Coll)
(f: T => R)
(implicit ev: Coll <:< TraversableLike[T,Coll],
cbf: CanBuildFrom[Coll,T,Coll]): Coll = {
val builder = cbf(xs)
builder.sizeHint(xs.size)
val seen = MSet.empty[R]
xs.foreach { elem =>
if(!seen(f(elem))){
builder += elem
}
}
builder.result()
}
now consider a class to use it on:
case class X(i: Int, j: Int)
using this function naively fails:
scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
<console>:14: error: missing parameter type for expanded function ((x$1) => x$1.i)
distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
^
<console>:14: error: Cannot construct a collection of type scala.collection.immutable.Vector[X] with elements of type Any based on a collection of type scala.collection.immutable.Vector[X].
distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
^
but if I help the type inferencer, this works:
scala> distinctBy(Vector(X(1,2),X(3,2),X(1,1),X(2,2)))((x:X) => x.i)
res1: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))
scala> distinctBy[X,Int,Vector[X]](Vector(X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
res2: scala.collection.immutable.Vector[X] = Vector(X(1,2), X(3,2), X(1,1), X(2,2))
to my best understanding, since the function is given in a second argument list, the type inferencer should have picked up that it's a function from X to something. and since X has a member i of type Int, all should have been OK with the first try. so, what am I missing here?
This simplified version works fine for me:
object A {
def f1[T, R](l: List[T])(f: T=>R) = None
case class X(i: Int, j: Int)
f1(List(X(1,1),X(2,1)))(_.i)
}
As you can see collection in first parameter list has T type that allows scala inference type in second arguments list.
So you need build somehow dependencies between Col and T in your example. Not sure if third implicits parameters list helps here.
UPD.
Looks weird but seems it works:
object A {
def f1[T, R, Col[Z]](l: Col[T])(f: T => R) = None
case class X(i: Int, j: Int)
f1(List(X(1,1),X(2,1)))(_.i)
}
UPD2.
Rewritten sample from question.
import scala.collection.TraversableLike
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.{Set=>MSet}
def distinctBy[T,R,Coll[Z]]
(xs: Coll[T])
(f: T => R)
(implicit ev: Coll[T] <:< TraversableLike[T,Coll[T]],
cbf: CanBuildFrom[Coll[T],T,Coll[T]]): Coll[T] = {
val builder = cbf(xs)
builder.sizeHint(xs.size)
val seen = MSet.empty[R]
xs.foreach { elem =>
if(!seen(f(elem))){
builder += elem
seen.add(f(elem))
}
}
builder.result()
}
case class X(i: Int, j: Int)
distinctBy(Vector(X(1,2),X(1,2),X(3,2),X(1,1),X(2,2)))(_.i)
distinctBy(Map("1" -> X(1,2), "2" -> X(1,2), "3" -> X(3,2)))(_._2.i)
I'm pretty sure what I'd like to do is probably not possible and not a good idea anyway. Nonetheless, here it is.
I would like to find a generic way of transforming any method on any class into a function that takes as arguments an instance of the class and the parameters of the method and calls the method on the instance with the specified parameters (which is basically how method calls work at a low-level: the object instance is a hidden parameter pushed by the compiler on the stack frame)
Example:
Given
class A { def foo(param1: Type1): TypeN }
I would like to have something like:
def functor[X](methodName: String): (obj: X, methodParams: Types*) => TypeN
Such that:
// Type (obj: A, param1: Type1) => TypeN
val fooFn = functor[A]("foo")
val res: TypeN = fooFn(new A, new Type1)
Which would allow something like:
def flip[A, B, C](fn: A => B => C): B => A => C = (b: B) => (a: A) => fn(a)(b)
// Type (param1: Type1) => (obj: A) => TypeN
val fooFl = flip(fooFn.curried)
List(new A, new A).map(fooFl(new Type1) _)
One example where something like that could be useful follows:
Imagine you have:
val divide = (i: Int) => Try(2/i).toOption
List(1, 0).map(i => divide(i).getOrElse(0))
but you'd like an arguably cleaner:
import OptionUtils.getOrElse
List(1, 0).map(getOrElse(0) _ compose divide)
Now, I've defined that getOrElse manually as:
object OptionUtils {
def getOrElse[A](default: A)(option: Option[A]) = option.getOrElse(default)
}
But would like to be able to do automatically define such a methods for all (or any given selection) of methods in a specified class. Ideally I'd have something like:
val optionUtils: Utils[Option] = Utils(Option, List("getOrElse", "or"))
optionUtils.getOrElse(None, 1) // = 1
optionUtils.getOrElseFl(1)(None) // = 1
Any suggestions/comments?
You can use Java relection for this (here is scala code)
Or Scala reflection:
import scala.reflect._, runtime.universe._
def functor[X: TypeTag: ClassTag, R](methodName: String)(obj: X) = {
val m = runtimeMirror(getClass.getClassLoader)
val method = typeTag[X].tpe.declaration(newTermName(methodName)).asMethod
val call = m.reflect(obj).reflectMethod(method)
(call.apply _) andThen (_.asInstanceOf[R])
}
scala> functor[Option[Int], Int]("get") _
res19: Option[Int] => (Seq[Any] => Int) = <function1>
scala> res19(Some(5))(Nil)
res20: Int = 5
Suppose I have a list of functions as so:
val funcList = List(func1: A => T, func2: B => T, func2: C => T)
(where func1, et al. are defined elsewhere)
I want to write a method that will take a value and match it to the right function based on exact type (match a: A with func1: A => T) or throw an exception if there is no matching function.
Is there a simple way to do this?
This is similar to what a PartialFunction does, but I am not able to change the list of functions in funcList to PartialFunctions. I am thinking I have to do some kind of implicit conversion of the functions to a special class that knows the types it can handle and is able to pattern match against it (basically promoting those functions to a specialized PartialFunction). However, I can't figure out how to identify the "domain" of each function.
Thank you.
You cannot identify the domain of each function, because they are erased at runtime. Look up erasure if you want more information, but the short of it is that the information you want does not exist.
There are ways around type erasure, and you'll find plenty discussions on Stack Overflow itself. Some of them come down to storing the type information somewhere as a value, so that you can match on that.
Another possible solution is to simply forsake the use of parameterized types (generics in Java parlance) for your own customized types. That is, doing something like:
abstract class F1 extends (A => T)
object F1 {
def apply(f: A => T): F1 = new F1 {
def apply(n: A): T = f(n)
}
}
And so on. Since F1 doesn't have type parameters, you can match on it, and you can create functions of this type easily. Say both A and T are Int, then you could do this, for example:
F1(_ * 2)
The usual answer to work around type erasure is to use the help of manifests. In your case, you can do the following:
abstract class TypedFunc[-A:Manifest,+R:Manifest] extends (A => R) {
val retType: Manifest[_] = manifest[R]
val argType: Manifest[_] = manifest[A]
}
object TypedFunc {
implicit def apply[A:Manifest, R:Manifest]( f: A => R ): TypedFunc[A, R] = {
f match {
case tf: TypedFunc[A, R] => tf
case _ => new TypedFunc[A, R] { final def apply( arg: A ): R = f( arg ) }
}
}
}
def applyFunc[A, R, T >: A : Manifest]( funcs: Traversable[TypedFunc[A,R]] )( arg: T ): R = {
funcs.find{ f => f.argType <:< manifest[T] } match {
case Some( f ) => f( arg.asInstanceOf[A] )
case _ => sys.error("Could not find function with argument matching type " + manifest[T])
}
}
val func1 = { s: String => s.length }
val func2 = { l: Long => l.toInt }
val func3 = { s: Symbol => s.name.length }
val funcList = List(func1: TypedFunc[String,Int], func2: TypedFunc[Long, Int], func3: TypedFunc[Symbol, Int])
Testing in the REPL:
scala> applyFunc( funcList )( 'hello )
res22: Int = 5
scala> applyFunc( funcList )( "azerty" )
res23: Int = 6
scala> applyFunc( funcList )( 123L )
res24: Int = 123
scala> applyFunc( funcList )( 123 )
java.lang.RuntimeException: Could not find function with argument matching type Int
at scala.sys.package$.error(package.scala:27)
at .applyFunc(<console>:27)
at .<init>(<console>:14)
...
I think you're misunderstanding how a List is typed. List takes a single type parameter, which is the type of all the elements of the list. When you write
val funcList = List(func1: A => T, func2: B => T, func2: C => T)
the compiler will infer a type like funcList : List[A with B with C => T].
This means that each function in funcList takes a parameter that is a member of all of A, B, and C.
Apart from this, you can't (directly) match on function types due to type erasure.
What you could instead do is match on a itself, and call the appropriate function for the type:
a match {
case x : A => func1(x)
case x : B => func2(x)
case x : C => func3(x)
case _ => throw new Exception
}
(Of course, A, B, and C must remain distinct after type-erasure.)
If you need it to be dynamic, you're basically using reflection. Unfortunately Scala's reflection facilities are in flux, with version 2.10 released a few weeks ago, so there's less documentation for the current way of doing it; see How do the new Scala TypeTags improve the (deprecated) Manifests?.
MWE (obs.: I am avoiding to have to instantiate a class every call of c, this is why functions are desired):
object Main extends App {
def a(s:String, i:Int) ={
s + i * i //some complex op that yields String
}
def b(i:Int) ={
i / 3 //another complex op that yields Int
}
def c(f: Any => Any) = {
val L = List(1,2,3,4) //list of complex elements
L map f //apply f within some complex loop
}
println(c(a))
/*
scala: type mismatch;
found : (String, Int) => String
required: Any => Any
println(c(a))
^
*/
println(c(b))
/*
scala: type mismatch;
found : Int => Int
required: Any => Any
println(c(b))
^
*/
}
Maybe an equivalent question would be "Is there some kind of function inheritance?",
like
def f
def fa(i: Int):String extends f
def fb(s: String):Int extends f
What you're trying to do isn't type-safe, since if it were you could pass a String to a function which takes an Int parameter:
e.g.
def c(f: Any => Any) = {
val L = List("a", "b", "c")
L map f
}
c(a)
However you can take a function of type Int => Any, since it is safe to assign a more derived type to Any.
def c(f: Int => Any) = {
val l = List(1,2,3,4)
l.map(f)
}
This is now safe:
val bList: List[Any] = c(b)
You still can't pass a to c however, since it requires two arguments instead of one. You can partially apply the first String argument and pass that:
val cList = c(a("SomeString", _:Int))
If you find yourself using Any,
you are probably doing something wrong, and
you most likely need generics.
In your case
def c[X,Y](f: X => Y) = { ... }
would probably do the trick, depending on what you have inside that complex loop.