Is it possible to generically replace arguments in a case class? More specifically, say I wanted a substitute function that received a "find" case class and a "replace" case class (like the left and right sides of a grammar rule) as well as a target case class, and the function would return a new case class with arguments of the find case class replaced with the replace case class? The function could also simply take a case class (Product?) and a function to be applied to all arguments/products of the case class.
Obviously, given a specific case class, I could use unapply and apply -- but what's the best/easiest/etc way to generically (given any case class) write this sort of function?
I'm wondering if there is a good solution using Scala 2.10 reflection features or Iso.hlist from shapeless.
For example, what I really want to be able to do is, given classes like the following...
class Op[T]
case class From(x:Op[Int]) extends Op[Int]
case class To(x:Op[Int]) extends Op[Int]
case class Target(a:Op[Int], b:Op[Int]) extends ...
// and lots of other similar case classes
... have a function that can take an arbitrary case class and return a copy of it with any elements of type From replaced with instances of type To.
If you'll pardon the plug, I think you'll find that the rewriting component of our Kiama language processing library is perfect for this kind of purpose. It provides a very powerful form of strategic programming.
Here is a complete solution that rewrites To's to From's in a tree made from case class instances.
import org.kiama.rewriting.Rewriter
class Op[T]
case class Leaf (i : Int) extends Op[Int]
case class From (x : Op[Int]) extends Op[Int]
case class To (x : Op[Int]) extends Op[Int]
case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int]
case class Target2 (c : Op[Int]) extends Op[Int]
object Main extends Rewriter {
def main (args : Array[String]) {
val replaceFromsWithTos =
everywhere {
rule {
case From (x) => To (x)
}
}
val t1 = Target1 (From (Leaf (1)), To (Leaf (2)))
val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4)))))
println (rewrite (replaceFromsWithTos) (t1))
println (rewrite (replaceFromsWithTos) (t2))
}
}
The output is
Target1(To(Leaf(1)),To(Leaf(2)))
Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4)))))
The idea of the replaceFromsWithTos value is that the rule construct lifts a partial function to be able to operate on any kind of value. In this case the partial function is only defined at From nodes, replacing them with To nodes. The everywhere combinator says "apply my argument to all nodes in the tree, leaving unchanged places where the argument does not apply.
Much more can be done than this kind of simple rewrite. See the main Kiama rewriting documentation for the gory detail, including links to some more examples.
I experimented a bit with shapeless and was able to come up with the following, relatively generic way of converting one case class into another:
import shapeless._ /* shapeless 1.2.3-SNAPSHOT */
case class From(s: String, i: Int)
case class To(s: String, i: Int)
implicit def fromIso = Iso.hlist(From.apply _, From.unapply _)
implicit def toIso = Iso.hlist(To.apply _, To.unapply _)
implicit def convert[A, B, L <: HList]
(a: A)
(implicit srcIso: Iso[A, L],
dstIso: Iso[B, L])
: B =
dstIso.from(srcIso.to(a))
val f1 = From("Hi", 7)
val t1 = convert(f1)(fromIso, toIso)
println("f1 = " + f1) // From("Hi", 7)
println("t1 = " + t1) // To("Hi", 7)
However, I was not able to get the implicits right. Ideally,
val t1: To = f1
would be sufficient, or maybe
val t1 = convert(f1)
Another nice improvement would be to get rid of the need of having to explicitly declare iso-implicits (fromIso, toIso) for each case class.
I don't think you'll really find a better way than just using unapply/apply through pattern matching:
someValue match {
case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c)
// . . .
}
You have to write out the rules to associate FindCaseClass with ReplaceCaseClass somehow, and although you might be able to do it a little more succinctly by somehow just using the names, this has the added benefit of also checking the number and types of the case class fields at compile time to make sure everything matches just right.
There is probably some way to do this automatically using the fact that all case classes extend Product, but the fact that productElement(n) returns Any might make it a bit of a pain—I think that's where reflection would have to come in. Here's a little something to get you started:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
val iter = From(5,"x",Nil).productIterator
val f = To.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
// res0: Any = To(5,x,List())
But really, I think you're better off with the pattern-matching version.
Edit: Here is a version with the relavent code refactored into a method:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
type Curryable = { def curried: _ => _ }
def recase(from: Product, to: Curryable) = {
val iter = from.productIterator
val f = to.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
}
recase(From(5,"x",Nil), To)
// res0: Any = To(5,x,List())
Related
I am reading Functional Programming in Scala from Manning, authored by Paul Chiusano and Runar Bjarnason. In its 3rd chapter, there is a code to create a List and there are assignments to implement various methods of the list. Following is partial implementation of the my List
package src.Cons
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
object List {
//my issue is I do not want to pass a list to sum but want to use objectName.sum notation
def sum(ints:List[Int]):Int = ints match {
case Nil => 0
case Cons(x,xs) => x+sum(xs)
}
}
Question - How can I create my list such that I can call l.sum instead of List.sum(l)?
You can "PmL", as #Gabriele Petronella has suggested, or you can move the sum() method to the Cons class, as #DeadNight wrote, but before either of those can work you have to resolve the current conflict between your List object and your List trait.
The sum() in your List object can only sum a List[Int] but your class definitions use a more generic type member and, as such, you can't use + because the compiler doesn't know how to add two A types.
If you want to restrict your List to only handling numeric types then this will work.
case class Cons[A: Numeric](h:A, t:List[A]) extends List[A] {
def sum: A = List.sum(this)
}
object List {
def sum[A](ints:List[A])(implicit ev: Numeric[A]):A = ints match {
case Nil => ev.zero
case Cons(x,xs) => ev.plus(x, sum(xs))
}
}
val x = Cons(4, Cons(2, Nil))
x.sum // res0: Int = 6
Making sum a member
The problem is, you don't know how to sum the List[A] for every type A, only a List[Int]. If there was a way to allow calls when A is an Int...
Let's take a look at the standard library for that. We're interested in Option#flatten method because:
val o1 = Option(Option(3)).flatten // compiles
val o2 = Option(4).flatten // does not compile
Notice the weird implicit ev: <:<[A, Option[B]]. This is the key here - it's a thing that compiler provides for you, but only if it is known at compile time, that your Option[A] is a subtype of Option[Option[B]] for some type B. This is the trick that we can use.
sealed trait List[+A] {
def sum(implicit ev: A <:< Int): Int = this match {
case Nil => 0
case Cons(x, xs) => x + xs.sum // <- here x is magically converted to Int, so we can use plus
}
}
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
println(Cons(4, Cons(38, Nil)).sum) // 42
ScalaFiddle
Notice that you can write <:<[A, B] as A <:< B.
NB: there's also =:=[A, B] type, for when your A is exactly Int - you can use either of those
Doing better?
Actually, std library has sum method and it's type is even weirder:
def sum(implicit ev: Numeric[A]). Doing so allows it to work on any number-like type like Double and Int, and has the operations for comparison, subtraction, multiplication, etc. So you can make it even more generic. I suggest you do it after reading a chapter about Monoids, tho :)
You can use the so-called "Pimp my Library" pattern.
Define an implicit class ListOps
implicit class ListOps[+A](list: List[A]) {
def sum = List.sum(this)
}
and now you can call list.sum. The implicit conversion will be triggered and the compiler will interpret it as ListOps(list).sum.
Move the definition of sum inside the definition of List trait
You can leave the concrete definitions to Nil & Cons
package src.Cons
sealed trait List[+A] {
def sum: Int
}
case object Nil extends List[Nothing] {
val sum: Int = 0
}
case class Cons[+A](h:A, t:List[A]) extends List[A] {
def sum: Int = h + t.sum
}
Given a function f, that, given a Map[String, MyType], returns a HList:
package net
import shapeless._
sealed trait MyType
case object MyInt extends MyType
case object MyStr extends MyType
object Mapper {
def f(m: Map[String, MyType]): HList = m.foldLeft[HList](HNil){ (acc, elem) =>
val (key, t) = elem
t match {
case MyInt => classOf[Int] :: acc
case MyStr => classOf[String] :: acc
}
}
}
I tested it:
import net._
val list = Map("foo" -> MyInt, "bar" -> MyStr)
scala> Mapper.f(list)
res0: shapeless.HList = class java.lang.String :: int :: HNil
How can I use the above approach (or another one) to build a case class with members matching the String keys, and the types given by the output of f?
So, I'm looking for:
g(Map("foo" -> MyInt, "bar" -> MyStr)) to output case class X(foo: Int, bar: String) where X is arbitrarily chosen, i.e. not important at this point.
I thought of using Generic[X], but I don't know how to get a Generic without a case class first.
The thing you're trying to do can't happen in Scala with compile time verification. The issue here is exactly as you've elaborated, you don't have the definition of the case class you're trying to build in advance. That definition provides the scaffolding to use a Record type to construct the isomorphism.
That said, we might be able to work something with invoke dynamic and reflection but I'm unclear on how you'd even take advantage of that in code. You wouldn't know the field names in advance nor their types. So how would you even write code around them?
I have a function in Scala that matches different case classes, but executes the same code on every match. Is there a possibility to “fallthrough”? Or a other nice way to write the code bellow without code duplication and without defining a function?
symbol match {
case Times(a,b) => //some code using a and b
case Plus(a,b) => //same code as above
case Div(a,b) => //again same code as above
}
This is a pretty similar too the question "Match "fallthrough": executing same piece of code for more than one case?" with the difference that I'm intressted in matching with case classes.
You could write your own extractor that combines the three cases and turns them into a tuple:
object BinOp {
def unapply(op: Op) = op match {
case Times(a, b) => Some(a, b)
case Plus(a, b) => Some(a, b)
case Div(a, b) => Some(a, b)
}
}
symbol match {
case BinOp(a, b) =>
}
No, falltroughs are prohibited in Scala since they are a common source of bugs in other languages. You have two three possibilites:
factor out everything that is identical in a function
try to use less specific matching, i.e., by using wildcards. In your example, this could also mean to introduce a superclass BinaryOperation which gives you the possibility of a more generic match. Note that due to case class inheritance restrictions, you would have to rely on using the fields of this superclass instead of having a super case class.
follow Mirko Stocker's nice suggestions of writing a specific extractor.
I see two possible solutions to your problem
1) unapply
Extending on M. Stocker's answer, you could organize your data like so:
trait Op
trait BinaryOp extends Op {
def a: Int
def b: Int
}
object BinaryOp {
def unapply(op: Op) = op match {
case x: BinaryOp => Some((x.a, x.b))
case _ => None
}
}
case class Times(a: Int, b: Int) extends BinaryOp
case class Plus(a: Int, b: Int) extends BinaryOp
case class Div(a: Int, b: Int) extends BinaryOp
Usage:
symbol match {
case BinaryOp(a, b) => f(a, b)
case _ => //...
}
2) Product
All case classes extends the Product trait
This allows you to do the following matching:
symbol match {
case p: Product if p.productArity == 2 => {
val a = p.productElement(0) //this has type Any, so a cast may be necessary
val b = p.productElement(1)
f(a, b)
}
}
The second case is more generic, but it is also type-unsafe. I recommend the first solution.
Apparently unapply/unapplySeq in extractor objects do not support implicit parameters. Assuming here an interesting parameter a, and a disturbingly ubiquitous parameter b that would be nice to hide away, when extracting c.
[EDIT]: It appears something was broken in my intellij/scala-plugin installation that caused this. I cannot explain. I was having numerous strange problems with my intellij lately. After reinstalling, I can no longer reprodce my problem. Confirmed that unapply/unapplySeq do allow for implicit parameters! Thanks for your help.
This does not work (**EDIT:yes, it does):**
trait A; trait C; trait B { def getC(a: A): C }
def unapply(a:A)(implicit b:B):Option[C] = Option(b.getC(a))
In my understanding of what an ideal extractor should be like, in which the intention is intuitively clear also to Java folks, this limitation basically forbids extractor objects which depend on additional parameter(s).
How do you typically handle this limitation?
So far I've got those four possible solutions:
1) The simplest solution that I want to improve on: don't hide b, provide parameter b along with a, as normal parameter of unapply in form of a tuple:
object A1{
def unapply(a:(A,B)):Option[C] = Option(a._2.getC(a._1)) }
in client code:
val c1 = (a,b) match { case A1(c) => c1 }
I don't like it because there is more noise deviating that deconstruction of a into c is important here. Also since java folks, that have to be convinced to actually use this scala code, are confronted with one additional synthactic novelty (the tuple braces). They might get anti-scala aggressions "What's all this? ... Why then not use a normal method in the first place and check with if?".
2) define extractors within a class encapsulating the dependence on a particular B, import extractors of that instance. At import site a bit unusual for java folks, but at pattern match site b is hidden nicely and it is intuitively evident what happens. My favorite. Some disadvantage I missed?
class BDependent(b:B){
object A2{
def unapply(a:A):Option[C] = Option(b.getC(a))
} }
usage in client code:
val bDeps = new BDependent(someB)
import bDeps.A2
val a:A = ...
val c2 = a match { case A2(c) => c }
}
3) declare extractor objects in scope of client code. b is hidden, since it can use a "b" in local scope. Hampers code reuse, heavily pollutes client code (additionally, it has to be stated before code using it).
4) have unapply return Option of function B => C. This allows import and usage of an ubitious-parameter-dependent extractor, without providing b directly to the extractor, but instead to the result when used. Java folks maybe confused by usage of function values, b not hidden:
object A4{
def unapply[A,C](a:A):Option[B => C] = Option((_:B).getC(a))
}
then in client code:
val b:B = ...
val soonAC: B => C = a match { case A4(x) => x }
val d = soonAC(b).getD ...
Further remarks:
As suggested in this answer, "view bounds" may help to get extractors work with implicit conversions, but this doesn't help with implicit parameters. For some reason I prefer not to workaround with implicit conversions.
looked into "context bounds", but they seem to have the same limitation, don't they?
In what sense does your first line of code not work? There's certainly no arbitrary prohibition on implicit parameter lists for extractor methods.
Consider the following setup (I'm using plain old classes instead of case classes to show that there's no extra magic happening here):
class A(val i: Int)
class C(val x: String)
class B(pre: String) { def getC(a: A) = new C(pre + a.i.toString) }
Now we define an implicit B value and create an extractor object with your unapply method:
implicit val b = new B("prefix: ")
object D {
def unapply(a: A)(implicit b: B): Option[C] = Option(b getC a)
}
Which we can use like this:
scala> val D(c) = new A(42)
c: C = C#52394fb3
scala> c.x
res0: String = prefix: 42
Exactly as we'd expect. I don't see why you need a workaround here.
The problem you have is that implicit parameters are compile time (static) constraints, whereas pattern matching is a runtime (dynamic) approach.
trait A; trait C; trait B { def getC(a: A): C }
object Extractor {
def unapply(a: A)(implicit b: B): Option[C] = Some(b.getC(a))
}
// compiles (implicit is statically provided)
def withImplicit(a: A)(implicit b: B) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
// does not compile
def withoutImplicit(a: A) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
So this is a conceptual problem, and the solution depends on what you actually want to achieve. If you want something along the lines of an optional implicit, you might use the following:
sealed trait FallbackNone {
implicit object None extends Optional[Nothing] {
def toOption = scala.None
}
}
object Optional extends FallbackNone {
implicit def some[A](implicit a: A) = Some(a)
final case class Some[A](a: A) extends Optional[A] {
def toOption = scala.Some(a)
}
}
sealed trait Optional[+A] { def toOption: Option[A]}
Then where you had implicit b: B you will have implicit b: Optional[B]:
object Extractor {
def unapply(a:A)(implicit b: Optional[B]):Option[C] =
b.toOption.map(_.getC(a))
}
def test(a: A)(implicit b: Optional[B]) : Option[C] = a match {
case Extractor(c) => Some(c)
case _ => None
}
And the following both compile:
test(new A {}) // None
{
implicit object BImpl extends B { def getC(a: A) = new C {} }
test(new A {}) // Some(...)
}
sealed class A
class B1 extends A
class B2 extends A
Assuming we have a List of objects of class A :
val l: List[A] = List(new B1, new B2, new B1, new B1)
And we want to filter out the elements of the type B1.
Then we need a predicate and could use the following two alternatives:
l.filter(_.isInstanceOf[B1])
Or
l.filter(_ match {case b: B1 => true; case _ => false})
Personally, I like the first approach more, but I often read, one should use the match-case statement more often (for reasons I do not know).
Therefore, the question is: Are there drawbacks of using isInstanceOf instead of the match-case statement ? When should one use which approach (and which approach should be used here and why) ?
You can filter like that:
l.collect{ case x: B1 => x }
That is much more readable, IMO.
There's no problem using isInstanceOf, as long as you don't use asInstanceOf.
Code that uses both is brittle, because checking and casting are separate actions, whereas using matching you have a single action doing both.
There are no difference
cat t.scala:
class A {
def x(o: AnyRef) = o.isInstanceOf[A]
def y(o: AnyRef) = o match {
case s: A => true
case _ => false
}
}
$ scalac -print t.scala
[[syntax trees at end of cleanup]]// Scala source: t.scala
package <empty> {
class A extends java.lang.Object with ScalaObject {
def x(o: java.lang.Object): Boolean = o.$isInstanceOf[A]();
def y(o: java.lang.Object): Boolean = {
<synthetic> val temp1: java.lang.Object = o;
temp1.$isInstanceOf[A]()
};
def this(): A = {
A.super.this();
()
}
}
}
The advantage of match-case is that you don't have to cast the object in case you want to perform operations on it that depend on its narrower type.
In the following snippet, using isInstanceOf seems to be fine since you don't perform such an operation:
if (obj.isInstanceOf[A]) println(obj)
However, if you do the following:
if (obj.isInstanceOf[A]) {
val a = obj.asInstanceOf[A]
println(a.someField) // someField is declared by A
}
then I'd be in favour of using match-case:
obj match {
case a: A => println(a.someField)
case _ =>
}
It is slightly annoying that you have to include the "otherwise"-case, but using collect (as hinted at by om-nom-nom) could help, at least if you work with collections inherit from Seq:
collectionOfObj.collect{ case a: A => a}.foreach(println(_.someField))