Scala order implicit conversions? - scala

I have some problem in scala order implicit, can someone help me?
Below are some classes definition, and what I want to do is to compare Leaf and Node through it's 'popularity'.
class Tree
case class EmptyTree() extends Tree
case class Leaf(label: String, popularity: Double) extends Tree
case class Node(popularity: Double, left: Tree, right: Tree) extends Tree
for exampel:
val a = Leaf("a",10)
val b = Leaf("b",20)
val c = Node(30,a,b)
if we want to compare a and b through it's popularity, it is easy to do by adding the implicit transformations, like that:
implicit val leavesOrder = new Ordering[Leaf] {
override def compare(x: Leaf, y: Leaf) =
implicitly[Ordering[Double]].compare(x.popularity, y.popularity)
}
but if I want to compare a and c through it's popularity, I am confused about it and don't konw how to add the implicit transformations?
Can someone help me?

You can create an implicit Ordering for Tree if you want to compare Leafs and Nodes.
Here's some (incomplete) code, on how you could do that:
implicit val treeOrder = new Ordering[Tree] {
override def compare(x: Tree, y: Tree) = (x,y) match {
case (Leaf(_,xP), Leaf(_,yP)) => xP compare yP
case (Node(xP,_,_), Leaf(_,yP)) => xP compare yP
case (Node(xP,_,_), Node(yP,_,_)) => xP compare yP
case (EmptyTree(), _) => -1
/* Add the rest of the cases here */
}
}
Bonus points for changing Tree to be a sealed trait, so that the compiler can tell you when your pattern matching is incomplete :)

Id do something like this. Change your Tree class to a sealed trait this means the pattern matching is exhaustive so the compiler can tell you if your missing something. Then you need to match on each of the types that a Tree can be. Not all of them have a popularity.
sealed trait Tree
case object EmptyTree extends Tree
case class Leaf(label: String, popularity: Double) extends Tree
case class Node(popularity: Double, left: Tree, right: Tree) extends Tree
implicit val treeOrdering = new Ordering[Tree] {
private val doubleOrdering = implicitly[Ordering[Double]]
def compare(a: Tree, b: Tree): Int = {
(a, b) match {
case (Node(p1, _, _), Node(p2, _, _)) => doubleOrdering.compare(p1, p2)
case (Leaf(_, p1), Node(p2, _, _)) => doubleOrdering.compare(p1, p2)
case (Node(p1, _, _), Leaf(_, p2)) => doubleOrdering.compare(p1, p2)
case (Leaf(_, p1), Leaf(_, p2)) => doubleOrdering.compare(p1, p2)
case (EmptyTree, _) => -1
case (_, EmptyTree) => 1
}
}
}

Related

Functional patterns for better chaining of collect

I often find myself needing to chain collects where I want to do multiple collects in a single traversal. I also would like to return a "remainder" for things that don't match any of the collects.
For example:
sealed trait Animal
case class Cat(name: String) extends Animal
case class Dog(name: String, age: Int) extends Animal
val animals: List[Animal] =
List(Cat("Bob"), Dog("Spot", 3), Cat("Sally"), Dog("Jim", 11))
// Normal way
val cats: List[Cat] = animals.collect { case c: Cat => c }
val dogAges: List[Int] = animals.collect { case Dog(_, age) => age }
val rem: List[Animal] = Nil // No easy way to create this without repeated code
This really isn't great, it requires multiple iterations and there is no reasonable way to calculate the remainder. I could write a very complicated fold to pull this off, but it would be really nasty.
Instead, I usually opt for mutation which is fairly similar to the logic you would have in a fold:
import scala.collection.mutable.ListBuffer
// Ugly, hide the mutation away
val (cats2, dogsAges2, rem2) = {
// Lose some benefits of type inference
val cs = ListBuffer[Cat]()
val da = ListBuffer[Int]()
val rem = ListBuffer[Animal]()
// Bad separation of concerns, I have to merge all of my functions
animals.foreach {
case c: Cat => cs += c
case Dog(_, age) => da += age
case other => rem += other
}
(cs.toList, da.toList, rem.toList)
}
I don't like this one bit, it has worse type inference and separation of concerns since I have to merge all of the various partial functions. It also requires lots of lines of code.
What I want, are some useful patterns, like a collect that returns the remainder (I grant that partitionMap new in 2.13 does this, but uglier). I also could use some form of pipe or map for operating on parts of tuples. Here are some made up utilities:
implicit class ListSyntax[A](xs: List[A]) {
import scala.collection.mutable.ListBuffer
// Collect and return remainder
// A specialized form of new 2.13 partitionMap
def collectR[B](pf: PartialFunction[A, B]): (List[B], List[A]) = {
val rem = new ListBuffer[A]()
val res = new ListBuffer[B]()
val f = pf.lift
for (elt <- xs) {
f(elt) match {
case Some(r) => res += r
case None => rem += elt
}
}
(res.toList, rem.toList)
}
}
implicit class Tuple2Syntax[A, B](x: Tuple2[A, B]){
def chainR[C](f: B => C): Tuple2[A, C] = x.copy(_2 = f(x._2))
}
Now, I can write this in a way that could be done in a single traversal (with a lazy datastructure) and yet follows functional, immutable practice:
// Relatively pretty, can imagine lazy forms using a single iteration
val (cats3, (dogAges3, rem3)) =
animals.collectR { case c: Cat => c }
.chainR(_.collectR { case Dog(_, age) => age })
My question is, are there patterns like this? It smells like the type of thing that would be in a library like Cats, FS2, or ZIO, but I am not sure what it might be called.
Scastie link of code examples: https://scastie.scala-lang.org/Egz78fnGR6KyqlUTNTv9DQ
I wanted to see just how "nasty" a fold() would be.
val (cats
,dogAges
,rem) = animals.foldRight((List.empty[Cat]
,List.empty[Int]
,List.empty[Animal])) {
case (c:Cat, (cs,ds,rs)) => (c::cs, ds, rs)
case (Dog(_,d),(cs,ds,rs)) => (cs, d::ds, rs)
case (r, (cs,ds,rs)) => (cs, ds, r::rs)
}
Eye of the beholder I suppose.
How about defining a couple utility classes to help you with this?
case class ListCollect[A](list: List[A]) {
def partialCollect[B](f: PartialFunction[A, B]): ChainCollect[List[B], A] = {
val (cs, rem) = list.partition(f.isDefinedAt)
new ChainCollect((cs.map(f), rem))
}
}
case class ChainCollect[A, B](tuple: (A, List[B])) {
def partialCollect[C](f: PartialFunction[B, C]): ChainCollect[(A, List[C]), B] = {
val (cs, rem) = tuple._2.partition(f.isDefinedAt)
ChainCollect(((tuple._1, cs.map(f)), rem))
}
}
ListCollect is just meant to start the chain, and ChainCollect takes the previous remainder (the second element of the tuple) and tries to apply a PartialFunction to it, creating a new ChainCollect object. I'm not particularly fond of the nested tuples this produces, but you may be able to make it look a bit better if you use Shapeless's HLists.
val ((cats, dogs), rem) = ListCollect(animals)
.partialCollect { case c: Cat => c }
.partialCollect { case Dog(_, age) => age }
.tuple
Scastie
Dotty's *: type makes this a bit easier:
opaque type ChainResult[Prev <: Tuple, Rem] = (Prev, List[Rem])
extension [P <: Tuple, R, N](chainRes: ChainResult[P, R]) {
def partialCollect(f: PartialFunction[R, N]): ChainResult[List[N] *: P, R] = {
val (cs, rem) = chainRes._2.partition(f.isDefinedAt)
(cs.map(f) *: chainRes._1, rem)
}
}
This does end up in the output being reversed, but it doesn't have that ugly nesting from my previous approach:
val ((owls, dogs, cats), rem) = (EmptyTuple, animals)
.partialCollect { case c: Cat => c }
.partialCollect { case Dog(_, age) => age }
.partialCollect { case Owl(wisdom) => wisdom }
/* more animals */
case class Owl(wisdom: Double) extends Animal
case class Fly(isAnimal: Boolean) extends Animal
val animals: List[Animal] =
List(Cat("Bob"), Dog("Spot", 3), Cat("Sally"), Dog("Jim", 11), Owl(200), Fly(false))
Scastie
And if you still don't like that, you can always define a few more helper methods to reverse the tuple, add the extension on a List without requiring an EmptyTuple to begin with, etc.
//Add this to the ChainResult extension
def end: Reverse[List[R] *: P] = {
def revHelp[A <: Tuple, R <: Tuple](acc: A, rest: R): RevHelp[A, R] =
rest match {
case EmptyTuple => acc.asInstanceOf[RevHelp[A, R]]
case h *: t => revHelp(h *: acc, t).asInstanceOf[RevHelp[A, R]]
}
revHelp(EmptyTuple, chainRes._2 *: chainRes._1)
}
//Helpful types for safety
type Reverse[T <: Tuple] = RevHelp[EmptyTuple, T]
type RevHelp[A <: Tuple, R <: Tuple] <: Tuple = R match {
case EmptyTuple => A
case h *: t => RevHelp[h *: A, t]
}
And now you can do this:
val (cats, dogs, owls, rem) = (EmptyTuple, animals)
.partialCollect { case c: Cat => c }
.partialCollect { case Dog(_, age) => age }
.partialCollect { case Owl(wisdom) => wisdom }
.end
Scastie
Since you mentioned cats, I would also add solution using foldMap:
sealed trait Animal
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
case class Snake(name: String) extends Animal
val animals: List[Animal] = List(Cat("Bob"), Dog("Spot"), Cat("Sally"), Dog("Jim"), Snake("Billy"))
val map = animals.foldMap{ //Map(other -> List(Snake(Billy)), cats -> List(Cat(Bob), Cat(Sally)), dogs -> List(Dog(Spot), Dog(Jim)))
case d: Dog => Map("dogs" -> List(d))
case c: Cat => Map("cats" -> List(c))
case o => Map("other" -> List(o))
}
val tuples = animals.foldMap{ //(List(Dog(Spot), Dog(Jim)),List(Cat(Bob), Cat(Sally)),List(Snake(Billy)))
case d: Dog => (List(d), Nil, Nil)
case c: Cat => (Nil, List(c), Nil)
case o => (Nil, Nil, List(o))
}
Arguably it's more succinct than fold version, but it has to combine partial results using monoids, so it won't be as performant.
This code is dividing a list into three sets, so the natural way to do this is to use partition twice:
val (cats, notCat) = animals.partitionMap{
case c: Cat => Left(c)
case x => Right(x)
}
val (dogAges, rem) = notCat.partitionMap {
case Dog(_, age) => Left(age)
case x => Right(x)
}
A helper method can simplify this
def partitionCollect[T, U](list: List[T])(pf: PartialFunction[T, U]): (List[U], List[T]) =
list.partitionMap {
case t if pf.isDefinedAt(t) => Left(pf(t))
case x => Right(x)
}
val (cats, notCat) = partitionCollect(animals) { case c: Cat => c }
val (dogAges, rem) = partitionCollect(notCat) { case Dog(_, age) => age }
This is clearly extensible to more categories, with the slight irritation of having to invent temporary variable names (which could be overcome by explicit n-way partition methods)

Merging elements of a list of case classes

I have the following case class:
case class GHUser(login:String, contributions:Option[Int])
And a list of such elements:
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
And now I would like to merge all elements such that all contributions are added together for the same user. At first I thought I could apply a Monoid to my case class, like this:
trait Semigroup[A] {
def combine(x: A, y: A): A
}
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
case class GHUser(login: String, contributions: Option[Int])
object Main extends App {
val ghMonoid: Monoid[GHUser] = new Monoid[GHUser] {
def empty: GHUser = GHUser("", None)
def combine(x: GHUser, y: GHUser): GHUser = {
x match {
case GHUser(_, None) => GHUser(y.login, y.contributions)
case GHUser(_, Some(xv)) =>
y match {
case GHUser(_, None) => GHUser(x.login, x.contributions)
case GHUser(_, Some(yv)) => GHUser(x.login, Some(xv + yv))
}
}
}
}
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
val b = list.groupBy(_.login)
val c = b.mapValues(_.foldLeft(ghMonoid.empty)(ghMonoid.combine))
println(c.valuesIterator mkString("\n"))
// GHUser(a,Some(10))
// GHUser(b,Some(310))
}
An it works, but I feel like I am not following Monoid Laws, as it is required that all user have the same login (For that reason I did the groupBy call.
Is there a cleaner solution?
Update
Rereading my question, it seems like I do not want a Monoid but a Semigroup, am I right?
groupMapReduce() (Scala 2.13) handles most of what you need.
list.groupMapReduce(_.login)(_.contributions){case (a,b) => a.fold(b)(n => Some(n+b.getOrElse(0)))}
.map(GHUser.tupled)
//res0 = List(GHUser(a,Some(10)), GHUser(b,Some(310)))
The Reduce part is a bit convoluted but it gets the job done.
Here is a simple solution:
list.groupBy(_.login).map{
case (k, v) =>
GHUser(k, Some(v.flatMap(_.contributions).sum))
}
This will give Some(0) for users with no contributions. If you want None in this case it looks more ugly:
list.groupBy(_.login).map{
case (k, v) =>
val c = v.flatMap(_.contributions)
GHUser(k, c.headOption.map(_ => c.sum))
}

Collapse similar case statements in Scala

Is there an elegant way to do something like the following example using just one case statement?
foobar match {
case Node(Leaf(key, value), parent, qux) => {
// Do something with parent & qux
}
case Node(parent, Leaf(key, value), qux) => {
// Do something with parent & qux (code is the same as in the previous case)
}
// other cases
}
For an understanding of what is going on here: foobar is a node of a binary tree, and I match the cases when one of the node's ancestors is a Leaf node. These are the classes used:
abstract class Tree
case class Node(left: Tree, right: Tree, critBit: Int) extends Tree
case class Leaf(key: String, value:String) extends Tree
You can use a custom extractor to abstract the matching part away from the logic part:
object Leafed {
def unapply(tree: Tree) = tree match {
case Node(Leaf(_, _), parent, qux) => Some((parent, qux))
case Node(parent, Leaf(_, _), qux) => Some((parent, qux))
case _ => None
}
}
And then you can define methods like this:
def doSomething(tree: Tree): Int = tree match {
case Leafed(parent, qux) => qux
case _ => -100
}
Which you can use like this:
scala> val leaf = Leaf("foo", "bar")
leaf: Leaf = Leaf(foo,bar)
scala> doSomething(leaf)
res7: Int = -100
scala> doSomething(Node(leaf, Node(leaf, leaf, 5), 10))
res8: Int = 10
scala> doSomething(Node(Node(leaf, leaf, 5), leaf, 10))
res9: Int = 10
Otherwise you're out of luckā€”as Marth notes above, pattern alternatives aren't going to help you here.

I want my function to return a Stream[T], but I can't figure out how to make it type check

I'm playing with some basic programming exercises in order to learn Scala better, but I'm stuck on trying to figure out why my code won't type check.
The sticking point is the possibilities function. I want a function which returns a stream containing all possible arrangements of numbers and math operators given a list of numbers.
I'm confused because changing the return type of the function to read Stream[Object] type-checks just fine and returns results that appear to be Streams of Equations. However, the version included below does not type check with the return type of possibilites set to Stream[Equation].
As a side note, I understand that appending opsMix with cards doesn't put the Operations in the correct order, but I'd like to solve this part of the problem first. I think I'll use flatMap or zipAll with flatten to accomplish that part.
Also - this is not a homework assignment!
abstract class Operation
case class Add() extends Operation
case class Subtract() extends Operation
case class Multiply() extends Operation
case class Divide() extends Operation
case class Num(val valu: Float) extends Operation
type Equation = List[Operation]
def calc(equa: Equation): Float =
equa match {
case Num(x) :: List() => x
case Num(x) :: y :: Num(z) :: xs => y match {
case Add() => calc( Num(x + z)::xs )
case Subtract() => calc( Num(x - z)::xs )
case Multiply() => calc( Num(x * z)::xs )
case Divide() => calc( Num(x / z)::xs )
}
case _ => 0
}
// from http://stackoverflow.com/questions/1070859/listing-combinations-with-repetitions-in-scala
def mycomb[T](n: Int, l: List[T]): List[List[T]] =
n match {
case 0 => List(List())
case _ => for(el <- l;
sl <- mycomb(n-1, l dropWhile { _ != el } ))
yield el :: sl
}
def comb[T](n: Int, l: List[T]): List[List[T]] = mycomb(n, l.removeDuplicates)
val ops = List(Add, Subtract, Multiply, Divide)
def possibilities(cards: List[Num]) : Stream[Equation] =
{ for {
hand <- cards.permutations
opMix <- comb(cards.length-1, ops)
} yield hand ++ opMix
}.toStream
// test value:
val ppp = possibilities(List(Num(20), Num(3), Num(7), Num(100)))
The problem is that you declared your operation case classes as Add() etc., but in val ops you use just List(Add, ...). If you try declaring ops with the correct type:
val ops: List[Operation] = List(Add, Subtract, Multiply, Divide)
you'll see the errors. (This is why it's often helpful to add types yourself instead of relying on the type checker - it helps to find errors.)
I suggest you to update your class hierarchy to use case object for singleton operations:
abstract class Operation
case object Add extends Operation
case object Subtract extends Operation
case object Multiply extends Operation
case object Divide extends Operation
case class Num(val valu: Float) extends Operation
Of course you'll need to update your patterns as well:
def calc(equa: Equation): Float =
equa match {
case Num(x) :: List() => x
case Num(x) :: y :: Num(z) :: xs => y match {
case Add => calc( Num(x + z)::xs )
case Subtract => calc( Num(x - z)::xs )
case Multiply => calc( Num(x * z)::xs )
case Divide => calc( Num(x / z)::xs )
}
case _ => 0
}
Then possibilities works as expected without any changes.
Alternatively, you can keep the classes the way you have them, just change ops to
val ops: List[Operation] =
List(Add(), Subtract(), Multiply(), Divide())
Update:
Concerning interleaving, you could do something like:
def interleave[T](xs: List[T], ys: List[T], padX: T, padY: T): List[T] =
xs.zipAll(ys, padX, padY).flatMap(pair => List(pair._1, pair._2))
but note the result will always have an even number of elements. Perhaps a better solution would be to implement interleave yourself, something like:
def interleave[T](xs: List[T], ys: List[T]): List[T] = {
import collection.mutable._;
#annotation.tailrec
def f(xs: List[T], ys: List[T], r: Buffer[T]): Buffer[T] =
xs match {
// By swapping the arguments, we get interelaving:
case x :: xrest => f(ys, xrest, r += x);
case Nil => r ++= ys;
}
return f(xs, ys, new ArrayBuffer[T]).toList;
}
However, I'd say that even better solution would be not to mix operations and numbers. Instead, you could declare a special class for well-formed expression formed from your symbols, something like (untested):
sealed abstract class Symbol
sealed abstract class Operation extends Symbol
case object Add Operation
case object Subtract extends Operation
case object Multiply extends Operation
case object Divide extends Operation
case class Num(val valu: Float) extends Symbol
sealed abstract class Expression;
case class App(val op: Operation, val arg1: Expression, val arg2: Expression)
extends Expression;
case class Const(val n: Num)
extends Expression;
and instead of creating an interleaved list, create an instance of Expression.

Folding on case classes

I have a situation where I have a couple of case classes where all of their variables are optional.
Let's say I have:
case class Size(width: Option[Int], height: Option[Int])
case class Foo(a: Option[String], b: Option[Boolean], c: Option[Char])
Given a collection of the same type of case class I would like to fold over them comparing the option values and keep the values which are defined. I.e. for Size:
values.foldLeft(x) { (a, b) =>
Size(a.width.orElse(b.width), a.height.orElse(b.height))
}
I would like to do this in a more general way for any of the case classes like the ones above. I'm thinking about doing something with unapply(_).get etc. Does anyone know a smart way to solve this?
Ok, consider this:
def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C)
(coll: Seq[C]): C = {
coll.tail.foldLeft(coll.head) { case (current, next) =>
apply(unapply(current).get orElse unapply(next).get)
}
}
case class Person(name: Option[String])
foldCase(Person.unapply, Person.apply)(List(Person(None), Person(Some("Joe")), Person(Some("Mary"))))
One could overload foldCase to accept two, three, or more parameters, one version of f for each arity. It could then be used with any case class. Since there's the tuple-thing to worry about, below's one way to make it work with case classes or two parameters. Expanding it to more parameters is then trivial, though a bit tiresome.
def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C)
(coll: Seq[C]): C = {
def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) =
apply(current._1 orElse next._1, current._2 orElse next._2)
coll.tail.foldLeft(coll.head) { case (current, next) =>
thisOrElse(unapply(current).get, unapply(next).get)
}
}
val list = Person(None, None) :: Person(Some("Joe"), None) :: Person(None, Some(20)) :: Person(Some("Mary"), Some(25)) :: Nil
def foldPerson = foldCase(Person.unapply, Person.apply) _
foldPerson(list)
To use it overloaded, just put all definitions inside one object:
object Folder {
def foldCase[C,T1](unapply: C => Option[Option[T1]], apply: Option[T1] => C)
(coll: Seq[C]): C = {
coll.tail.foldLeft(coll.head) { case (current, next) =>
apply(unapply(current).get orElse unapply(next).get)
}
}
def foldCase[C,T1,T2](unapply: C => Option[(Option[T1], Option[T2])], apply: (Option[T1], Option[T2]) => C)
(coll: Seq[C]): C = {
def thisOrElse(current: (Option[T1], Option[T2]), next: (Option[T1], Option[T2])) =
apply(current._1 orElse next._1, current._2 orElse next._2)
coll.tail.foldLeft(coll.head) { case (current, next) =>
thisOrElse(unapply(current).get, unapply(next).get)
}
}
}
When you do this, however, you'll have to explicitly turn apply and unapply into functions:
case class Question(answer: Option[Boolean])
val list2 = List(Question(None), Question(Some(true)), Question(Some(false)))
Folder.foldCase(Question.unapply _, Question.apply _)(list2)
It might be possible to turn it into a structural type, so that you only need to pass the companion object, but I couldn't do it. On #scala, I was told the answer is a definitive no, at least to how I approached the problem.
[Code updated]
Here is an solution which requires only one abstract class per "arity":
abstract class Foldable2[A,B](val a:Option[A], val b:Option[B]) {
def orElse[F <: Foldable2[A,B]](that: F)(implicit ev: this.type <:< F) =
getClass.getConstructor(classOf[Option[A]], classOf[Option[B]]).newInstance(
this.a.orElse(that.a), this.b.orElse(that.b)
)
}
case class Size(w: Option[Int], h: Option[Int]) extends Foldable2(w, h)
println(Size(Some(1),None).orElse(Size(Some(2),Some(42))))
//--> Size(Some(1),Some(42))
Note that the implicit <:< argument will give a compile time error when other case classes with the same constructor arguments are passed to the method.
However, a "well formed" constructor is required, else the reflection code will blow up.
You can use productElement or productIterator (on scala.Product) to generically retrieve/iterate the elements of case classes (and tuples), but they're typed as Any, so there will be some pain.