I've backed myself into an interesting corner while designing a higher order typed interface.
I want to do something like this
trait SomeTrait {
def higherOrder(f: (Int, A) => List[A]): String
}
object SomeImple extends SomeTrait {
def higherOrder(f: (Int, A) => List[A]): String = {
f(3, "HI").mkString(", ") + f(3, 7).mkString(", ")
}
}
I want to specify that a function takes another higher order function as input that works for any type (in this case A). For instance:
def someFun[A](n: Int, a: A): List[A] =
if (n <= 0) {
List.empty
} else {
a :: (someFun(n - 1, a))
}
However If a add a type parameter to the higherOrder that means the function f can only be used at one type. Is there a way to take parametric functions as inputs without overly constraining them?
You can't parameterize a function like that, but you can parameterize a method:
trait SomeTrait {
def higherOrder(fn: {def apply[A](n: Int, a: A): List[A]}): String
}
object SomeImple extends SomeTrait {
def higherOrder(f: {def apply[A](n: Int, a: A): List[A]}): String = {
f(3, "HI").mkString(", ") + f(3, 7).mkString(", ")
}
}
object someFun {
def apply [A] (n: Int, a: A): List[A] = {
if (n <= 0) {
List.empty
} else {
a :: (someFun(n - 1, a))
}
}
}
Using a structural type (or you can create a trait that can be implemented by the type holding the method), you can request the method take a type param.
Unfortunately, you have to wrap it in an object (or some class) because a regular method can only be "lifted" to a Function and a Function's type parameters are fixed at definition time.
For reference: https://gist.github.com/jdegoes/97459c0045f373f4eaf126998d8f65dc#polymorphic-functions
What's wrong with passing type to your function? Solution:
object HighOrderFunction {
type MyFunction[T] = (Int, T) => List[T]
def main(args: Array[String]): Unit = {
val dupInt: MyFunction[Int] = (n, value) => {
List.fill(n)(value)
}
val dupString: MyFunction[String] = (n, value) => {
List.fill(n)(value)
}
val dupDouble: MyFunction[Double] = (n, value) => {
List.fill(n)(value)
}
execute(dupInt, 5, 1)
execute(dupString, 5, "*")
execute(dupDouble, 5, 3.14)
}
def execute[T](f: MyFunction[T], n: Int, t: T): Unit = {
println(f(n, t))
}
}
I have a sequence of instances where each instance can be implicitly converted to the same type.
What is the best way to convert such sequence?
class A
class B
trait Resolver {
def resolve: String
}
implicit class AResolver(a: A) extends Resolver {
def resolve: String = "a"
}
implicit class BResolver(b: B) extends Resolver {
def resolve: String = "b"
}
def resolveThem(a: Option[A], b: Option[B]): Iterable[String] = {
val resolvers: Seq[Resolver] = a ++ b // type error
val resolvers: Seq[Resolver] = List(a, b).collect{case Some(x: Resolver) => x} // empty
val resolvers: Seq[Resolver] = List(a, b).collect{case Some(x: A) => x} // unexpectedly for me but it is also type error when there is an x:A
val resolvers: Seq[Resolver] = List(a, b).collect{case Some(x: A) => x:Resolver} // works but returns only A as resolver
val resolvers: Seq[Resolver] = List(a, b).collect{case Some(x /*something that can be implicitly converted to Resolver*/) => x:Resolver} // Is it possible?
val resolvers: Seq[Resolver] = List(a.get, b.get) // this bad approach works
resolvers.map(_.resolve) // this is what I want as result
a.map(_.resolve) ++ b.map(_.resolve) // there is another way but if I have more arguments it becomes too long
}
You can only use implicits when exact type is available to compiler. As soon as you put your objects into a simple List their individual types disappear. (You could use HList though.)
For two arguments you just use your working approach.
For more arguments you might want to have a builder with one argument.
trait Builder {
def add[A: Resolver](a: A): Builder = {
use(a.resolve)
this
}
}
If there are only a few classes, you could use runtime match:
def getResolver(any: Any): Resolver = any match {
case a: A => a: Resolver
case b: B => b: Resolver
case _ => throw new IllegalArgumentException(s"$any is not supported"
}
However, this approach is very very bad. It is not extensible.
You could also use type classes instead of implicit conversions.
trait Resolvable[T] {
def resolve(a: T): String
}
implicit class AResolvable extends Resolvable[A] {
def resolve(a: A): String = "a"
}
This is the preferred way, I guess.
Method collect accepts PartialFunction[A, B], which means that function is defined only on a subset of the possible input arguments A and implicit conversions would not be applied.
Conversion should be done either explicitly or beforehand. One way of doing this for your case is a method taking varargs or a sequence:
def resolveThem (resolvers: Option[Resolver]*): Iterable[String] = {
resolvers.flatten.map(_.resolve)
}
resolveThem(Option(new A), Option(new B))
I have simple class with N fields.
case class Book(a: UUID... z: String)
and function:
def sort(books:Seq[Book], fields:Seq[SortingFields]) = {...}
where
case class SortingField(field: String, asc: Boolean)
where field - a field of the Book class, asc - a sorting direction.
So, in advance I dont know which fields (from 0 to N) and sorting orders come into my function to sort a books collection. It may be just a single ID field or all exist fields of a class in a particular order.
How could it be implemented?
I would use the existing Ordering trait for this and use a function that maps from Book to a field, i.e. Ordering.by[Book, String](_.author). Then you can simply sort with books.sorted(myOrdering). If I define a helper method on Book's companion object, getting these orderings is very simple:
object Book {
def by[A: Ordering](fun: Book => A): Ordering[Book] = Ordering.by(fun)
}
case class Book(author: String, title: String, year: Int)
val xs = Seq(Book("Deleuze" /* and Guattari */, "A Thousand Plateaus", 1980),
Book("Deleuze", "Difference and Repetition", 1968),
Book("Derrida", "Of Grammatology", 1967))
xs.sorted(Book.by(_.title)) // A Thousand, Difference, Of Grammatology
xs.sorted(Book.by(_.year )) // Of Grammatology, Difference, A Thousand
Then to chain the ordering by multiple fields, you can create custom ordering that proceeds through the fields until one comparison is non-zero. For example, I can add an extension method andThen to Ordering like this:
implicit class OrderingAndThen[A](private val self: Ordering[A]) extends AnyVal {
def andThen(that: Ordering[A]): Ordering[A] = new Ordering[A] {
def compare(x: A, y: A): Int = {
val a = self.compare(x, y)
if (a != 0) a else that.compare(x, y)
}
}
}
So I can write:
val ayt = Book.by(_.author) andThen Book.by(_.year) andThen Book.by(_.title)
xs.sorted(ayt) // Difference, A Thousand, Of Grammatology
With the nice answer provided by #0__ I've come up to folowing:
def by[A: Ordering](e: Book => A): Ordering[Book] = Ordering.by(e)
with
implicit class OrderingAndThen[A](private val self: Ordering[A]) extends AnyVal {
def andThen(that: Ordering[A]): Ordering[A] = new Ordering[A] {
def compare(x: A, y: A): Int = {
val a = self.compare(x, y)
if (a != 0) a else that.compare(x, y)
}
}
}
next I map name of a class field with a direction to actual ordering
def toOrdering(name: String, r: Boolean): Ordering[Book] = {
(name match {
case "id" => Book.by(_.id)
case "name" => Book.by(_.name)
}) |> (o => if (r) o.reverse else o)
}
using a forward pipe operator:
implicit class PipedObject[A](value: A) {
def |>[B](f: A => B): B = f(value)
}
and finally I combine all the ordering with the reduce function:
val fields = Seq(SortedField("name", true), SortedField("id", false))
val order = fields.map(f => toOrdering(f.field, f.reverse)).reduce(combines(_,_))
coll.sorted(order)
where
val combine = (x: Ordering[Book], y: Ordering[Book]) => x andThen y
An aternate way is to use #tailrec:
def orderingSeq[T](os: Seq[Ordering[T]]): Ordering[T] = new Ordering[T] {
def compare(x: T, y: T): Int = {
#tailrec def compare0(rest: Seq[Ordering[T]], result: Int): Int = result match {
case 0 if rest.isEmpty => 0
case 0 => compare0(rest.tail, rest.head.compare(x, y))
case a => a
}
compare0(os, 0)
}
}
It is possible. But as far as I can see you will have to use reflection.
Additionally, you would have to change your SortingField class a bit as there is no way the scala compiler can figure out the right Ordering type class for each field.
Here is a simplified example.
import scala.reflect.ClassTag
/** You should be able to figure out the correct field ordering here. Use `reverse` to decide whether you want to sort ascending or descending. */
case class SortingField[T](field: String, ord: Ordering[T]) { type FieldType = T }
case class Book(a: Int, b: Long, c: String, z: String)
def sort[T](unsorted: Seq[T], fields: Seq[SortingField[_]])(implicit tag: ClassTag[T]): Seq[T] = {
val bookClazz = tag.runtimeClass
fields.foldLeft(unsorted) { case (sorted, currentField) =>
// keep in mind that scala generates a getter method for field 'a'
val field = bookClazz.getMethod(currentField.field)
sorted.sortBy[currentField.FieldType](
field.invoke(_).asInstanceOf[currentField.FieldType]
)(currentField.ord)
}
}
However, for sorting by multiple fields you would have to either sort the sequence multiple times or better yet compose the various orderings correctly.
So this is getting a bit more 'sophisticated' without any guarantees about correctness and completeness, but with a little test that it does not fail spectacularly:
def sort[T](unsorted: Seq[T], fields: Seq[SortingField[_]])(implicit tag: ClassTag[T]): Seq[T] = {
#inline def invokeGetter[A](field: Method, obj: T): A = field.invoke(obj).asInstanceOf[A]
#inline def orderingByField[A](field: Method)(implicit ord: Ordering[A]): Ordering[T] = {
Ordering.by[T, A](invokeGetter[A](field, _))
}
val bookClazz = tag.runtimeClass
if (fields.nonEmpty) {
val field = bookClazz.getMethod(fields.head.field)
implicit val composedOrdering: Ordering[T] = fields.tail.foldLeft {
orderingByField(field)(fields.head.ord)
} { case (ordering, currentField) =>
val field = bookClazz.getMethod(currentField.field)
val subOrdering: Ordering[T] = orderingByField(field)(currentField.ord)
new Ordering[T] {
def compare(x: T, y: T): Int = {
val upperLevelOrderingResult = ordering.compare(x, y)
if (upperLevelOrderingResult == 0) {
subOrdering.compare(x, y)
} else {
upperLevelOrderingResult
}
}
}
}
unsorted.sorted(composedOrdering)
} else {
unsorted
}
}
sort(
Seq[Book](
Book(1, 5L, "foo1", "bar1"),
Book(10, 50L, "foo10", "bar15"),
Book(2, 3L, "foo3", "bar3"),
Book(100, 52L, "foo4", "bar6"),
Book(100, 51L, "foo4", "bar6"),
Book(100, 51L, "foo3", "bar6"),
Book(11, 15L, "foo5", "bar7"),
Book(22, 45L, "foo6", "bar8")
),
Seq(
SortingField("a", implicitly[Ordering[Int]].reverse),
SortingField("b", implicitly[Ordering[Long]]),
SortingField("c", implicitly[Ordering[String]])
)
)
>> res0: Seq[Book] = List(Book(100,51,foo3,bar6), Book(100,51,foo4,bar6), Book(100,52,foo4,bar6), Book(22,45,foo6,bar8), Book(11,15,foo5,bar7), Book(10,50,foo10,bar15), Book(2,3,foo3,bar3), Book(1,5,foo1,bar1))
Case classes are Products, so you can iterate over all field values using instance.productIterator. This gives you the fields in order of declaration. You can also access them directly via their index. As far as I can see, there is however no way to get the field names. This would have to be done using reflection or macros. (Maybe some library as Shapeless can already do that).
An other way would be to not define fields to sort by with names but with functions:
case class SortingField[T](field: Book => T, asc: Boolean)(implicit ordering: Ordering[T])
new SortingField(_.fieldName, true)
And then declare sort as:
def sort(books: Seq[Book], fields: Seq[SortingField[_]]) = {...}
And use the following compare method to implement the combined ordering:
def compare[T](b1: Book, b2: Book, field: SortingField[T]) =
field.ordering.compare(field.field(b1), field.field(b2))
I thought the following would be the most concise and correct form to collect elements of a collection which satisfy a given type:
def typeOnly[A](seq: Seq[Any])(implicit tag: reflect.ClassTag[A]): Seq[A] =
seq.collect {
case tag(t) => t
}
But this only works for AnyRef types, not primitives:
typeOnly[String](List(1, 2.3, "foo")) // ok. List(foo)
typeOnly[Double](List(1, 2.3, "foo")) // fail. List()
Obviously the direct form works:
List(1, 2.3, "foo") collect { case d: Double => d } // ok. List(2.3)
So there must be a (simple!) way to fix the above method.
It's boxed in the example, right?
scala> typeOnly[java.lang.Double](vs)
res1: Seq[Double] = List(2.3)
Update: The oracle was suitably cryptic: "boxing is supposed to be invisible, plus or minus". I don't know if this case is plus or minus.
My sense is that it's a bug, because otherwise it's all an empty charade.
More Delphic demurring: "I don't know what the given example is expected to do." Notice that it is not specified, expected by whom.
This is a useful exercise in asking who knows about boxedness, and what are the boxes? It's as though the compiler were a magician working hard to conceal a wire which keeps a playing card suspended in midair, even though everyone watching already knows there has to be a wire.
scala> def f[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect {
| case v if t.runtimeClass.isPrimitive &&
| ScalaRunTime.isAnyVal(v) &&
| v.getClass.getField("TYPE").get(null) == t.runtimeClass =>
| v.asInstanceOf[A]
| case t(x) => x
| }
f: [A](s: Seq[Any])(implicit t: scala.reflect.ClassTag[A])Seq[A]
scala> f[Double](List(1,'a',(),"hi",2.3,4,3.14,(),'b'))
res45: Seq[Double] = List(2.3, 3.14)
ScalaRunTime is not supported API -- the call to isAnyVal is just a match on the types; one could also just check that the "TYPE" field exists or
Try(v.getClass.getField("TYPE").get(null)).map(_ == t.runtimeClass).getOrElse(false)
But to get back to a nice one-liner, you can roll your own ClassTag to handle the specially-cased extractions.
Version for 2.11. This may not be bleeding edge, but it's the recently cauterized edge.
object Test extends App {
implicit class Printable(val s: Any) extends AnyVal {
def print = Console println s.toString
}
import scala.reflect.{ ClassTag, classTag }
import scala.runtime.ScalaRunTime
case class Foo(s: String)
val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null)
class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] {
override def runtimeClass = t.runtimeClass
/*
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) &&
x.getClass.getField("TYPE").get(null) == t.runtimeClass)
Some(x.asInstanceOf[A])
else super.unapply(x)
)
*/
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive) {
val ok = x match {
case _: java.lang.Integer => runtimeClass == java.lang.Integer.TYPE
//case _: java.lang.Double => runtimeClass == java.lang.Double.TYPE
case _: java.lang.Double => t == ClassTag.Double // equivalent
case _: java.lang.Long => runtimeClass == java.lang.Long.TYPE
case _: java.lang.Character => runtimeClass == java.lang.Character.TYPE
case _: java.lang.Float => runtimeClass == java.lang.Float.TYPE
case _: java.lang.Byte => runtimeClass == java.lang.Byte.TYPE
case _: java.lang.Short => runtimeClass == java.lang.Short.TYPE
case _: java.lang.Boolean => runtimeClass == java.lang.Boolean.TYPE
case _: Unit => runtimeClass == java.lang.Void.TYPE
case _ => false // super.unapply(x).isDefined
}
if (ok) Some(x.asInstanceOf[A]) else None
} else if (x == null) { // let them collect nulls, for example
if (t == ClassTag.Null) Some(null.asInstanceOf[A]) else None
} else super.unapply(x)
)
}
implicit def mytag[A](implicit t: ClassTag[A]): MyTag[A] = new MyTag(t)
// the one-liner
def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case t(x) => x }
// this version loses the "null extraction", if that's a legitimate concept
//def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case x: A => x }
g[Double](vs).print
g[Int](vs).print
g[Unit](vs).print
g[String](vs).print
g[Foo](vs).print
g[Null](vs).print
}
For 2.10.x, an extra line of boilerplate because implicit resolution is -- well, we won't say it's broken, we'll just say it doesn't work.
// simplified version for 2.10.x
object Test extends App {
implicit class Printable(val s: Any) extends AnyVal {
def print = Console println s.toString
}
case class Foo(s: String)
val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null)
import scala.reflect.{ ClassTag, classTag }
import scala.runtime.ScalaRunTime
// is a ClassTag for implicit use in case x: A
class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] {
override def runtimeClass = t.runtimeClass
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) &&
(x.getClass getField "TYPE" get null) == t.runtimeClass)
Some(x.asInstanceOf[A])
else t unapply x
)
}
// point of the exercise in implicits is the type pattern.
// there is no need to neutralize the incoming implicit by shadowing.
def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = {
implicit val u = new MyTag(t) // preferred as more specific
s collect { case x: A => x }
}
s"Doubles? ${g[Double](vs)}".print
s"Ints? ${g[Int](vs)}".print
s"Units? ${g[Unit](vs)}".print
s"Strings? ${g[String](vs)}".print
s"Foos? ${g[Foo](vs)}".print
}
Promoting a comment:
#WilfredSpringer Someone heard you. SI-6967
I want to the get the manifest of one List's inner type like following and pass it to another function, how can I do that ? Thanks
def f(any: Any) = any match {
case x: Int => println("Int")
case a: List[_] => // get the manifest of List's inner type, and use it in the function g()
}
def g[T:Manifest](list:List[T]) = {}
Add the manifest as an implicit requirement to your method, and tweak the type signature a tiny bit:
def f[T](any: T)(implicit mf: Manifest[T]) = mf match {
case m if m == Manifest[Int] => println("Int")
case m if m == Manifest[List[Int]] => println("List of Ints")
//etc...
}
The Manifest class has a method, typeArguments, that should serve your purpose for finding the "inner type". For example
manifest[List[Int]].typeArguments == List(manifest[Int])
You could tweak #Dylan's answer a bit and try this as well:
object ManifestTest {
def f[T](t: T)(implicit m:Manifest[T]) = t match {
case x: Int => println("Int")
case a: List[Any] =>
val innerType = m.typeArguments.head
println(innerType)
}
def main(args: Array[String]) {
f(1)
f(List("hello", "world"))
f(List(1))
}
}