Scala: Generic isInstanceOf based on sealed hierarchy - scala

I'm trying to generalize some sort of isIntanceOf based on a sealed hierarchy, but haven't been successful. The example below demonstrates what I want to achieve:
sealed abstract class Context
object Context {
case object Context1 extends Context
case class Context2(someInfo: String) extends Context
case object Context3 extends Context
}
case class ContextHolder(id: String, contexts: Set[Context])
import Context._
val holder = ContextHolder("1", Set(Context1, Context2("Other info")))
val contains1Or2 = holder.contexts.contains(Context1) || holder.contexts.exists(_.isInstanceOf[Context2])
val contains3 = holder.contexts.contains(Context3)
println(s"contains1Or2: $contains1Or2")
println(s"contains3: $contains3")
Basically I want to generalize the code of contains1Or2 and contains3 in some sort of:
def containsAnyOf[T <: Context: ClassTag](holder: ContextHolder, contexts: T*): Boolean
And then be able to use it like:
val contains1Or2 = containsAnyOf(holder, Context1, Context2)
val contains3 = containsAnyOf(holder, Context3)
I've already tried a few different approaches, but couldn't get it to work so far. Any ideas? Thanks.

The best I could come up was this:
def containsAnyOf(holder: ContextHolder)(contexts: PartialFunction[Context, Unit]): Boolean = {
val p = contexts.lift.andThen(_.isDefined)
holder.contexts.exists(p)
}
Which you can use like this:
val contains1Or2 = containsAnyOf(holder) {
case Context1 =>
case Context2(_) => // or case _: Contaxt2 =>
}
val contains3 = containsAnyOf(holder) {
case Context3 =>
}
Code running here.

Shapeless's LiftAll type class is very handy here.
import shapeless.ops.hlist.{LiftAll, ToTraversable}
import shapeless.{Generic, HList}
final class ContainsMulti[T <: Product](private val dummy: Boolean = true) extends AnyVal {
def apply[H <: HList, O <: HList](holder: ContextHolder)(implicit
toHList: Generic.Aux[T, H],
classTags: LiftAll.Aux[ClassTag, H, O],
allCtxts: LiftAll[({type E[T] = T <:< Context})#E, H],
toList: ToTraversable.Aux[O, List, ClassTag[_]]
) = classTags.instances.toList
.map(_.runtimeClass)
.forall(klazz => holder.contexts.exists(klazz.isInstance))
}
def containsMulti[T <: Product] = new ContainsMulti[T]
Usage:
val contains1Or2 = containsMulti[(Context1.type, Context2)](holder)
val contains3 = containsMulti[Tuple1[Context3.type]](holder)
val contains1and3 = containsMulti[(Context1.type, Context3.type)](holder)
val check = containsMulti[(Int, String)](holder) //doesn't compile
println(s"contains1Or2: $contains1Or2") //true
println(s"contains3: $contains3") //false
println(s"contains1and3: $contains1and3")//false
See it run.
All the implicits may seem daunting, but it's actually very simple. The extra class is so we can take multiple type parameter lists, where the first is explicitly given, and the second is inferred. Generic turns the inputted tuple into an HList, LiftAll finds the ClassTag instances for each of the types in the tuple, and ToTraversable turns the HList containing those class tags back into a List.
As Luis Miguel Mejía Suárez suggested, I have made ContainsMulti a value class, using the partially applied type trick to avoid object creation. They also pointed out that this approach initially allowed (Int, String) as input, so now allCtxts checks that all the types in the HList extend Context.

Related

How to chain generically chain functions returning Either with an operator like `andThen`?

Problem: Chaining multiple Either returning functions, the Left of which are all failures inheriting from a common sealed trait InternalError. However, the compiler complains that the chain is returning Either[_,Success] instead of Either[InternalError, Success].
Here's the code that does the chaining:
import scala.language.implicitConversions
object EitherExtension {
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
//get ability to chain/compose functions that return aligning Eithers
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
}
As was pointed out in the comments this discards the type of Left. If I change it the below it will not work since the final output can be of type Either[X|Y, C] which resolves to Either[_,C] and I'm back to square one.
implicit class AndThenEither[A,B,X](val e: (A) => Either[X, B]) {
def andThenE[C,Y](f:(B) => Either[Y, C]): (A) => Either[_, C] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
Here's the example showing the compositional failure of type alignment:
import EitherExtension._
object AndThenComposition {
//sample type definitions of failure responses
sealed trait InternalError
case class Failure1() extends InternalError
case class Failure2() extends InternalError
//sample type definitions
case class Id(id: Int)
case class Stuff()
//sample type definitions of successful responses
case class Output1()
case class Output2()
case class InputRequest()
val function1: (InputRequest) => Either[Failure1, Output1] = ???
val function2: (Output1) => Either[Failure2, Output2] = ???
def doSomething(s:Id, l:List[Stuff]): Either[InternalError, Output2] = {
val pipeline = function1 andThenE function2
pipeline(InputRequest()) //Why is this of type Either[_, Output2]
}
}
What am I missing? How can I get the return type to not be Either[Any, Output2] but rather the base/sealed trait? Is this possible to do generically?
You need to preserve the type of the left so we will modify the extension method to do that.
Note that, since both eithers can have different left types, what we will do is use a type bound to ask the compiler to infer the LUB between those types; thanks to Any this is always possibles (although not always helpful).
object EitherExtension {
implicit class AndThenEither[I, L1, R1](private val f: I => Either[L1, R1]) extends AnyVal {
def andThenE[L2 >: L1, R2](g: R1 => Either[L2, R2]): I => Either[L2, R2] =
i => f(i).flatMap(g)
}
}
Which can be used like this:
import EitherExtension._
object AndThenComposition {
sealed trait InternalError
final case object Failure1 extends InternalError
final case object Failure2 extends InternalError
val function1: Int => Either[Failure1.type, String] = ???
val function2: String => Either[Failure2.type, Boolean] = ???
def doSomething(input: Int): Either[InternalError, Boolean] = {
(function1 andThenE function2)(input)
}
}
See the code running here.
In case you're using this in production, and it's not just a learning thing, what you're looking for it's called Kleisli, and fortunately cats-core already implements it.
According to the cats-core docs:
Kleisli enables composition of functions that return a monadic value,
for instance an Option[Int] or a Either[String, List[Double]], without
having functions take an Option or Either as a parameter, which can be
strange and unwieldy.
Since Kleisli composes two functions with the signature A => F[B], you'd need only one abstraction to be able to use Kleisli, which is creating a new type for your operation:
type Operation[A] = Either[InternalFailure, A]
By doing this, you should be able to use Kleisli like this:
import cats.data.Kleisli
val first: Kleisli[Operation, InputRequest, Output1] = Kleisli { request: InputRequest =>
Left(Failure1())
}
val second: Kleisli[Operation, Output1, Output2] = Kleisli { output: Output1 =>
Right(Output2())
}
val composed = first.andThen(second)

Shapeless: Could not find implicit value for updater

I am working on a side project, trying to implement immutable aggregates in Scala. My idea is to have base trait AggregateRoot with some common behaviors. Child classes would be actual aggregates modeled as a case classes. For now, there is one thing that I don't like, and that is that I cannot call copy method from the base trait for two reasons:
I don't have access to copy method inside of the base trait
I have no idea how many parameters copy method will have
I have some basic knowledge of shapeless library and I thought that it might help in this case. My idea is pass list of tagged fields to the base method in the trait which will replace them and return new instance of the case class.
As a step in that direction I am trying to create method that will copy one single field for the beginning using shapeless, but I am getting the same error all the time, that compiler cannot find the implicit for updater.
Here is the simplified code fragment that I am trying to use:
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
import shapeless.record._
import shapeless.syntax.singleton._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
def makeCopy[L <: HList, K, V](ft: FieldType[K, V])(
implicit
labeledGen: LabelledGeneric.Aux[T, L],
updater: Updater.Aux[L, FieldType[K, V], L],
witness: Witness.Aux[K]): T = {
val labeledHList = labeledGen.to(this)
val result = labeledHList.updated(witness, ft)
labeledGen.from(result)
}
}
case class User(id: String, age: Int) extends AggregateRoot[User]()
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.makeCopy(ageChange)
Since I am not experienced shapeless user, I am not sure why it cannot find requested implicit. Version of shapeless is 2.3.3.
As far as I understood from this great answer: How to generically update a case class field using LabelledGeneric? - you can't have Updater in general case, because Shapeless need to derive it for each particular field in case class, which means instead of having general purpose makeCopy you will need to have method for each particular field, like:
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
import AggregateRoot._
def withAge[L <: HList, K, V](age: Int)(implicit
gen: LabelledGeneric.Aux[T, L],
upd: Updater.Aux[L, F, L]
): T = {
val ageField = AgeField(age)
gen.from(upd(gen.to(this), ageField))
}
}
object AggregateRoot {
type AgeField = Symbol with Tagged[Witness.`"age"`.T]
val AgeField = field[AgeField]
type F = FieldType[AgeField, Int]
}
import AggregateRoot._
case class User(id: String, age: Int) extends AggregateRoot[User]
object User {
implicit val gen = LabelledGeneric[User]
}
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.withAge(ageChange)
val test = User("test-id", 20)
println("test.withAge(42) = " + test.withAge(20))
println("test.withAge(12).withAge(42) = " + test.withAge(12).withAge(42))
Scatie: https://scastie.scala-lang.org/kDnL6HTQSEeSqVduW3EOnQ

Matching Type Parameters on Traits

Let's say I have a trait like:
trait MyTrait[T, U <: SomeParentClass] {
def get(data: T): Option[U]
}
and a concrete implementation like:
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
To simplify things, let's also say we have the following types for U <: SomeParentClass:
TypeA
TypeB
TypeC
and some functions:
def str2TypeA(s: String): Option[TypeA] = ...
def str2TypeB(s: String): Option[TypeB] = ...
def str2TypeC(s: String): Option[TypeC] = ...
Then let's say I have:
val mySeq = Seq(
MyStringClass(str2TypeA),
MyStringClass(str2TypeB),
MyStringClass(str2TypeC)
)
What I want to do is filter mySeq based on the return type U. Something like:
mySeq.collect { case a: Function1[_, Option[TypeA]] => a}
I'm running into type erasure issues as expected. I'm curious what approaches might work well here to achieve my goal of filtering based on the type U.
You would usually use a TypeTag to handle cases where erasure gets in your way. They are essentially simple objects that contain all of the information available at compile-time, and they're easy to obtain either by requesting an implicit parameter, or just writing typeTag[T]. For example:
import scala.reflect.runtime.universe._
case class MyStringClass[U <: SomeParentClass](f: String => Option[U])(implicit val utt: TypeTag[U])
extends MyTrait[String, U] {
override def get(data: String) = f(data)
}
mySeq.collect {
case a: MyStringClass[Option[TypeA]] if (a.utt == typeTag[Option[Type[A]]) => a
}

Error of "No implicit ordering defined for U" where a trait which has extended Ordered

After creating a ScalaIDE Worksheet named test.WsTemp, I wrote the code below and am receiving three errors for a single line in trait Enum:
Diverging implicit expansion for type scala.math.Ordering[U] starting with method ordered in trait LowPriorityOrderingImplicits
No implicit Ordering defined for U
Not enough arguments for method sorted: (implicit ord: scala.math.Ordering[U])List[U], Unspecified value parameter ord.
Why isn't this working since it's obvious Val extends Ordered[Val]?
object WsTemp {
trait Val extends Ordered[Val] {
val id: Int
val name: String
final def compare(that: Val) = this.id - that.id
override def toString: String = name
}
trait Enum[U <: Val] {
protected def init: Set[U]
val all = init
val allOrdered = all.toList.sorted // <-- Receiving error here
val byId = all.map(x => (x.id, x)).toMap
val byName = all.map(x => (x.name, x)).toMap
def apply(id: Int) = byId.get(id)
def apply(name: String) = byName.get(name)
}
sealed class Value(val id: Int, val name: String) extends Val
object Suit extends Enum[Value] {
override def init: Set[Value] = //Set()
Set(
new Value(0, "Spade")
, new Value(1, "Club")
, new Value(2, "Diamond")
, new Value(3, "Heart")
)
val Spade = Suit.byId(0)
val Club = Suit.byId(1)
val Diamond = Suit.byId(2)
val Heart = Suit.byId(3)
}
val all = Suit.all
val allOrdered = Suit.allOrdered
val byId = Suit.byId
val byName = Suit.byName
val spade = Suit.Spade
}
Ordering and Ordered are both invariant , so the fact that U is a sub-type of Val (as expressed by the relation U <: Val) does not imply that Ordering[Val] can be used as an Ordering[U].
So even though the fact that Val extends Ordered[Val] means that you implicitly get an Ordering[Val], this means nothing regarding Ordering[U].
So the compiler is right to complain that it cannot find an implicit value of type Ordering[U] (which is required by the call to sorted).
The prolbem is easy to illustrate with a small code snippet that mimics what happens with Ordered and Ordering:
object WsTemp {
trait MyOrdered[T]
trait MyOrdering[T]
object MyOrdering {
implicit def toOrdering[A <% MyOrdered[A]]: MyOrdering[A] = new MyOrdering[A]{}
}
trait Val extends MyOrdered[Val]
def test[T](implicit ord: MyOrdering[T]) {}
trait Enum[U <: Val] {
def callTest() { test[U] }
}
}
Which produces the following error:
<console>:20: error: could not find implicit value for parameter ord: WsTemp.MyOrdering[U]
def callTest() { test[U] }
But if you make MyOrdered and MyOrdering covariant, this compiles fine:
trait MyOrdered[+T]
trait MyOrdering[+T]
...
Obviously, you cannot change scala's Ordering nor Ordered to make them invariant.
Now, one way to solve your problem is to arrange your code so that Val does not extend Ordered[Val], but instead extends Ordered[X] where X is the actual type that you want to have an Ordering for (here, X = U). This can be achieved with F-bounded polymorphism:
trait Val[Self<:Val[Self]] extends Ordered[Self] {
//...
}
trait Enum[U <: Val[U]] {
//...
}
sealed class Value(val id: Int, val name: String) extends Val[Value]
//...
U is now a sub-type of Val[U], which is a sub-type of Ordered[U] (as opposed to a sub-type of Ordered[Val] as before), and so you now implicitly get an Ordering[U], which is (implicitly) passed to sorted. Problem solved.
As Régis Jean-Gilles said, "Ordering and Ordered are both invariant , so the fact that U is a sub-type of Val (as expressed by the relation U <: Val) does not imply that Ordering[Val] can be used as an Ordering[U]. So even though the fact that Val extends Ordered[Val] means that you implicitly get an Ordering[Val], this means nothing regarding Ordering[U]."
A very simple way to fix this is to specify the type to use for the sorted method. Replace:
val allOrdered = all.toList.sorted
with:
val allOrdered = all.toList.sorted[Val]

Elegant way to sort Array[B] for a subclass B < A, when A extends Ordered[A]?

Having defined a class A which extends Ordering[A], and a subclass B of A, how do I automatically sort an Array of Bs? The Scala compiler complains that it "could not find implicit value for parameter ord: Ordering[B]". Here's a concrete REPL example (Scala 2.8), with A = Score and B = CommentedScore:
class Score(val value: Double) extends Ordered[Score] {
def compare(that: Score) = value.compare(that.value)
}
defined class Score
trait Comment { def comment: String }
defined trait Comment
class CommentedScore(value: Double, val comment: String) extends Score(value) with Comment
defined class CommentedScore
val s = new CommentedScore(10,"great")
s: CommentedScore = CommentedScore#842f23
val t = new CommentedScore(0,"mediocre")
t: CommentedScore = CommentedScore#dc2bbe
val commentedScores = Array(s,t)
commentedScores: Array[CommentedScore] = Array(CommentedScore#b3f01d, CommentedScore#4f3c89)
util.Sorting.quickSort(commentedScores)
error: could not find implicit value for parameter ord: Ordering[CommentedScore]
util.Sorting.quickSort(commentedScores)
^
How do I fix this (that is, sort an Array[B] = Array[CommentedScore] "for free", given that I know how to sort Array[A] = Array[Score]), in an elegant manner which avoids boilerplate?
Thanks!
Add the required implicit yourself:
implicit val csOrd: Ordering[CommentedScore] = Ordering.by(_.value)
You can put this in a CommentedScore companion object so that there is no boilerplate at use-site.
Edit: if you want the ordering method to be defined only at the top of the inheritance tree, you still have to provide an Ordering for each subclass, but you can define the compare method of the Ordering in terms of the one in the Score object. i.e.
object Score {
implicit val ord: Ordering[Score] = Ordering.by(_.value)
}
object CommentedScore {
implicit val csOrd = new Ordering[CommentedScore] {
def compare(x: CommentedScore, y: CommentedScore) = Score.ord.compare(x, y)
}
}
if you don't want to re-define this for each sub-class, you can use a generic method to produce the Ordering:
object Score {
implicit def ord[T <: Score]: Ordering[T] = Ordering.by(_.value)
}
This is a bit less efficient since being a def rather than a val, it creates a new Ordering each time one is required. However the overhead is probably tiny. Also note, the Ordered trait and compare method is not necessary now we have Orderings.
You might use Order from scalaz, which is contravariant, so you need not to define it for every subclass. Here is an example:
import scalaz._
import Scalaz._
class Score(val value: Double)
object Score {
implicit val scoreOrd: Order[Score] = orderBy(_.value)
}
trait Comment { def comment: String }
class CommentedScore(value: Double, val comment: String) extends Score(value) with Comment {
override def toString = s"cs($value, $comment)"
}
def quickSort[E: Order](list: List[E]): List[E] = list match {
case Nil => Nil
case head :: tail =>
val (less, more) = tail partition { e => implicitly[Order[E]].order(e, head) == LT }
quickSort(less) ::: head :: quickSort(more)
}
println(quickSort(List(
new CommentedScore(10,"great"),
new CommentedScore(5,"ok"),
new CommentedScore(8,"nice"),
new CommentedScore(0,"mediocre")
))) // List(cs(0.0, mediocre), cs(5.0, ok), cs(8.0, nice), cs(10.0, great))
This works:
val scoreArray: Array[Score] = Array(s, t)
util.Sorting.quickSort(scoreArray)
Or if you are starting from the Array[CommentedScore]:
val scoreArray: Array[Score] = commentedScores.map(identity)
util.Sorting.quickSort(scoreArray)
Note you can sort more simply with:
scoreArray.sorted