I though if something like this makes sense in scala:
object CaseClassUnion extends App {
case class HttpConfig(bindUrl: String, port: String)
case class DbConfig(url: String, usr: String, pass: String)
val combined: HttpConfig with DbConfig = ???
//HttpConfig("0.0.0.0", "21") ++ DbConfig("localhost", "root", "root")
//would be nice to have something like that
}
At least this compiles... Is there a way, probably with macros magic to achieve union of two classes given their instances?
In zio I believe there is something like in reverse:
val live: ZLayer[ProfileConfiguration with Logging, Nothing, ApplicationConfiguration] =
ZLayer.fromServices[ProfileConfigurationModule.Service, Logger[String], Service] { (profileConfig, logger) => ???
where we convert ProfileConfiguration with Logging to function of ProfileConfigurationModule.Service, Logger[String] => Service
Several things.
When you have several traits combined with with Scala does a trait linearization to combine them into one class with a linear hierarchy. But that's true for traits which doesn't have constructors!
case class (which is not a trait) cannot be extended with another case class (at all) because that would break contracts like:
case class A(a: Int)
case class B(a: Int, b: String) extends A(a)
A(1) == B(1, "") // because B is A and their respective fields match
B(1, "") != A(1) // because A is not B
B(1, "").hashCode != A(1).hashCode // A == B is true but hashCodes are different!
which means that you cannot even generate case class combination manually. You want to "combine" them, use some product: a tuple, another case class, etc.
If you are curious about ZIO it:
uses traits
uses them as some sort of type-level trick to represent an unordered set of dependencies, where type inference would calculate set sum when you combine operations and some clever trickery to remove traits from the list using .provide to remove dependency from the set
ZLayers are just making these shenanigans easier
so and if you even pass there some A with B you either combined it yourself by using cake pattern, or you passed dependencies one by one. ZIO developer might never be faced with the problem of needing some macro to combine several case classes (.provide(combineMagically(A, B, C, D, ...)) as they could pass implementations of each dependency one by one (.provide(A).provide(B)) and the code underneath would never need the combination of these types as one value - it's just a compile-time trick that might never translate to the requirement of an actual value of type A with B with C with D ....
TL;DR: You cannot generate a combination of 2 case classes; ZIO uses compound types as some sort of type-level set to trace dependencies and it doesn't actually require creating values of the compound types.
Related
Is there a better explanation than "this is how it works". I mean I tried this one:
class TestShortMatch[T <: AnyRef] {
def foo(t: T): Unit = {
val f = (_: Any) match {
case Val(t) => println(t)
case Sup(l) => println(l)
}
}
class Val(t: T)
class Sup(l: Number)
}
and compiler complaints:
Cannot resolve symbol 'Val'
Cannot resolve symbol 'Sup'
Of course if I add case before each of the classes it will work fine. But what is the reason? Does compiler make some optimization and generate a specific byte-code?
The reason is twofold. Pattern matching is just syntactic sugar for using extractors and case classes happen to give you a couple methods for free, one of which is an extractor method that corresponds to the main constructor.
If you want your example above to work, you need to define an unapply method inside objects Val and Sup. To do that you'd need extractor methods (which are only defined on val fields, so you'll have to make your fields vals):
class Val[T](val t: T)
class Sup(val l: Number)
object Val {
def unapply[T](v: Val[T]): Option[T] = Some(v.t)
}
object Sup {
def unapply(s: Sup): Option[Number] = Some(s.l)
}
And which point you can do something like val Val(v) = new Val("hi"). More often than not, though, it is better to make your class a case class. Then, the only times you should be defining extra extractors.
The usual example (to which I can't seem to find a reference) is coordinates:
case class Coordinate(x: Double, val: Double)
And then you can define a custom extractors like
object Polar {
def unapply(c: Coordinate): Option[(Double,Double)] = {...}
}
object Cartesian {
def unapply(c: Coordinate): Option[(Double,Double)] = Some((c.x,c.y))
}
to convert to the two different representations, all when you pattern match.
You can use pattern matching on arbitrary classes, but you need to implement an unapply method, used to "de-construct" the object.
With a case class, the unapply method is automatically generated by the compiler, so you don't need to implement it yourself.
When you write match exp { case Val(pattern) => ... case ... }, that is equivalent to something like this:
match Val.unapply(exp) {
case Some(pattern) =>
...
case _ =>
// code to match the other cases goes here
}
That is, it uses the result of the companion object's unapply method to see whether the match succeeded.
If you define a case class, it automatically defines a companion object with a suitable unapply method. For a normal class it doesn't. The motivation for that is the same as for the other things that gets automatically defined for case classes (like equals and hashCode for example): By declaring a class as a case class, you're making a statement about how you want the class to behave. Given that, there's a good chance that the auto generated will do what you want. For a general class, it's up to you to define these methods like you want them to behave.
Note that parameters for case classes are vals by default, which isn't true for normal classes. So your class class Val(t: T) doesn't even have any way to access t from the outside. So it isn't even possible to define an unapply method that gets at the value of t. That's another reason why you don't get an automatically generated unapply for normal classes: It isn't even possible to generate one unless all parameters are vals.
Part of a current project involves converting from types coupled to a database and a generic type used when serializing the results out to clients via Json, the current implementation in Scala uses type inference to correctly perform the transformation, using Scala's TypeTag:
def Transform[A: TypeTag](objects:Seq[A]):Seq[Children] = typeOf[A] match {
case pc if pc =:= typeOf[ProductCategory] =>
TransformProductCategory(objects.asInstanceOf[Seq[ProductCategory]])
case pa if pa =:= typeOf[ProductArea] =>
TransformProductArea(objects.asInstanceOf[Seq[ProductArea]])
case pg if pg =:= typeOf[ProductGroup] =>
TransformProductGroup(objects.asInstanceOf[Seq[ProductGroup]])
case psg if psg =:= typeOf[ProductSubGroup] =>
TransformProductSubGroup(objects.asInstanceOf[Seq[ProductSubGroup]])
case _ =>
throw new IllegalArgumentException("Invalid transformation")
}
The types used as input are all case classes and are defined internally within the application, for example:
case class ProductCategory(id: Long, name: String,
thumbnail: Option[String],
image:Option[String],
sequence:Int)
This approach, although suitable at the moment, doesn't feel functional or scalable when potentially more DB types are added. I also feel using asInstanceOf should be redundant as the type has already been asserted. My limited knowledge of implicits suggests they could be used instead to perform the transformation, and remove the need for the above Transform[A: TypeTag](objects:Seq[A]):Seq[Children] method altogether. Or maybe there is a different approach I should have used instead?
You can define a trait like this:
trait Transformer[A] {
def transformImpl(x: Seq[A]): Seq[Children]
}
Then you can define some instances:
object Transformer {
implicit val forProduct = new Transformer[ProductCategory] {
def transformImpl(x: Seq[ProductCategory]) = ...
}
...
}
And then finally:
def transform[A: Transformer](objects:Seq[A]): Seq[Children] =
implicitly[Transformer[A]].transformImpl(objects)
Preferably, you should define your implicit instances either in Transformer object or in objects corresponding to your category classes.
I'm not sure how your program is supposed to work exactly, nor do I know if any of your Transforms are self-made types or something you pulled from a library, however I may have a solution anyway
Something match is really good with, is case classes
So rather than manually checking the type of the input data, you could maybe wrap them all in case classes (if you need to bring data with you) or case objects (if you don't)
That way you can do something like this:
// this code assumes ProductCategory, ProductArea, etc.
// all extends the trait ProductType
def Transform(pType: ProductType): Seq[Children] = pType match {
case ProductCategory(objects) => TransformProductCategory(objects)
case ProductArea(objects) => TransformProductArea(objects)
case ProductGroup(objects) => TransformProductGroup(objects)
case ProductSubGroup(objects) => TransformProductSubGroup(objects)
}
By wrapping everything in a case class, you can specify exactly which type of data you want to bring along, and so long as they (the case classes, not the data) all inherit from the same class/trait, you should be fine
And since there's only a little handful of classes that extend ProductType you don't need the default case, because there is no default case!
Another benefit of this, is that it's infinitely expandable; just add more cases and case classes!
Note that this solution requires you to refactor your code a LOT, so bare that in mind before you throw yourself into it.
There are two ways of defining a method for two different classes inheriting the same trait in Scala.
sealed trait Z { def minus: String }
case class A() extends Z { def minus = "a" }
case class B() extends Z { def minus = "b" }
The alternative is the following:
sealed trait Z { def minus: String = this match {
case A() => "a"
case B() => "b"
}
case class A() extends Z
case class B() extends Z
The first method repeats the method name, whereas the second method repeats the class name.
I think that the first method is the best to use because the codes are separated. However, I found myself often using the second one for complicated methods, so that adding additional arguments can be done very easily for example like this:
sealed trait Z {
def minus(word: Boolean = false): String = this match {
case A() => if(word) "ant" else "a"
case B() => if(word) "boat" else "b"
}
case class A() extends Z
case class B() extends Z
What are other differences between those practices? Are there any bugs that are waiting for me if I choose the second approach?
EDIT:
I was quoted the open/closed principle, but sometimes, I need to modify not only the output of the functions depending on new case classes, but also the input because of code refactoring. Is there a better pattern than the first one? If I want to add the previous mentioned functionality in the first example, this would yield the ugly code where the input is repeated:
sealed trait Z { def minus(word: Boolean): String ; def minus = minus(false) }
case class A() extends Z { def minus(word: Boolean) = if(word) "ant" else "a" }
case class B() extends Z { def minus(word: Boolean) = if(word) "boat" else "b" }
I would choose the first one.
Why ? Merely to keep Open/Closed Principle.
Indeed, if you want to add another subclass, let's say case class C, you'll have to modify supertrait/superclass to insert the new condition... ugly
Your scenario has a similar in Java with template/strategy pattern against conditional.
UPDATE:
In your last scenario, you can't avoid the "duplication" of input. Indeed, parameter type in Scala isn't inferable.
It still better to have cohesive methods than blending the whole inside one method presenting as many parameters as the method union expects.
Just Imagine ten conditions in your supertrait method. What if you change inadvertently the behavior of one of each? Each change would be risked and supertrait unit tests should always run each time you modify it ...
Moreover changing inadvertently an input parameter (not a BEHAVIOR) is not "dangerous" at all. Why? because compiler would tell you that a parameter/parameter type isn't relevant any more.
And if you want to change it and do the same for every subclasses...ask to your IDE, it loves refactoring things like this in one click.
As this link explains:
Why open-closed principle matters:
No unit testing required.
No need to understand the sourcecode from an important and huge class.
Since the drawing code is moved to the concrete subclasses, it's a reduced risk to affect old functionallity when new functionality is added.
UPDATE 2:
Here a sample avoiding inputs duplication fitting your expectation:
sealed trait Z {
def minus(word: Boolean): String = if(word) whenWord else whenNotWord
def whenWord: String
def whenNotWord: String
}
case class A() extends Z { def whenWord = "ant"; def whenNotWord = "a"}
Thanks type inference :)
Personally, I'd stay away from the second approach. Each time you add a new sub class of Z you have to touch the shared minus method, potentially putting at risk the behavior tied to the existing implementations. With the first approach adding a new subclass has no potential side effect on the existing structures. There might be a little of the Open/Closed Principle in here and your second approach might violate it.
Open/Closed principle can be violated with both approaches. They are orthogonal to each other. The first one allows to easily add new type and implement required methods, it breaks Open/Closed principle if you need to add new method into hierarchy or refactor method signatures to the point that it breaks any client code. It is after all reason why default methods were added to Java8 interfaces so that old API can be extended without requiring client code to adapt.
This approach is typical for OOP.
The second approach is more typical for FP. In this case it is easy to add methods but it is hard to add new type (it breaks O/C here). It is good approach for closed hierarchies, typical example are Algebraic Data Types (ADT). Standardized protocol which is not meant to be extended by clients could be a candidate.
Languages struggle to allow to design API which would have both benefits - easy to add types as well as adding methods. This problem is called Expression Problem. Scala provides Typeclass pattern to solve this problem which allows to add functionality to existing types in ad-hoc and selective manner.
Which one is better depends on your use case.
Starting in Scala 3, you have the possibility to use trait parameters (just like classes have parameters), which simplifies things quite a lot in this case:
trait Z(x: String) { def minus: String = x }
case class A() extends Z("a")
case class B() extends Z("b")
A().minus // "a"
B().minus // "b"
I'm trying to build an internal DSL in Scala. I have the following types:
case class A(name:String)
case class Group(list:A*) // it can also be list:List[A]
Creating a group of A's using the normal syntax is as follows:
val group1 = Group(A("a1"), A("a2"), ...)
which is quite ugly. I would like to present a group as (A("a1"), A("a2"), ...) and possibly later ("a1", "a2", ...), if possible.
I could not figure it out myself how to convert (A("a1"), A("a2"), ...) to Group(A("a1"), A("a2"), ...). It would be nice if we can convert (A("a1"), A("a2"), ...) to an instance of class Group. (I don't care if I can't specify unlimited number of A's inside. Maximum 8 A's will suffice)
So my question is: Is there a way to convert a tuple to a specific instance of a class? If not, how would you solve this problem?
First of all, solving something for tuples of any arity in Scala is nasty because the tuple classes are distinct, so you'd have to have 8 or 22 or whatever arity you want to support conversions.
But anyways, tuples are for heterogeneously typed things, whereas you here require a collection of a common type. So while tuple syntax may look nice, I would recommend not to try to use it for this case in your DSL. Stick to collections or just alias your Group type, even at the price of an additional character, e.g.
object A {
implicit def fromString(name: String) = A(name)
}
case class A(name: String)
case class Group(elem: A*)
val G = Group
G("a1", "a2")
If you really want to support tuples, the following will do:
object Group {
implicit def fromTuple2[A1 <% A, A2 <% A](t: (A1, A2)) = Group(t._1, t._2)
}
case class Group(elem: A*)
("a1", "a2"): Group
Squeryl defines a trait KeyedEntity which overrides equals, checking for several conditions in an if and calling super.equals in the end. Since super is Object, it will always fail.
Consider:
trait T { override def equals(z: Any):Boolean = super.equals(z)} }
case class A(a: Int) extends T
val a = A(1); val b = A(1)
a==b // false
Thus, if you declare
case class Record(id: Long, name: String ...) extends KeyedEntity[Long] { ... }
-- and you create several Record instances but do not persist them, their comparison will break. I found this by implementing both Salat and Squeryl back ends for the same class, and then all Salat tests fail since isPersisted from KeyedEntity is false.
Is there a design whereby KeyedEntity will preserve case class equality if mixed into a case class? I tried self-typing and parameterizing BetterKeyedEntity[K,P] { self: P => ... } for the case class type as P but it causes infinite recursion in equals.
As things stand right now, super is Object so the final branch of the overridden equals in KeyedEntity will always return false.
The structural equality check usually generated for case classes seems not to be generated if there is an equals override. However some subtleties have to be noted.
Mixing the concept of id-based equality falling back to structural equality might not be a good idea, since I can imagine that it may lead to subtle bugs. For example:
x: A(1) and and y: A(1) are not yet persisted, so they are equal
then they get persisted, and since they are separate objects, the persistence framework may persist them as separate entities (I don't know Squeryl, maybe not an issue there, but this is a thin line to walk)
after persisting, they are suddenly not equal since the id differs.
Even worse, if x and y get persisted to the same id, the hashCode will differ before and after persisting (the source shows that if persisted it is the hashCode of the id). This breaks immutability, and will lead to very bad behavior (when put in maps for example). See this gist in which I demonstrate the assert failing.
So don't mix structural and id-based equality implicitly. Also see this explained in the context of Hibernate.
Typeclasses
It have to be noted that others pointed out (ref needed) that the concept of method-based equality is flawed, for such reasons (there is not only one way two thing can be equal). Therefore you can define a typeclass which describes Equality:
trait Eq[A] {
def equal(x: A, y: A): Boolean
}
and define (possibly multiple) instances of that typeclass for your classes:
// structural equality
implicit object MyClassEqual extends Eq[MyClass] { ... }
// id based equality
def idEq[K, A <: KeyedEntity[K]]: Eq[A] = new Eq[A] {
def equal(x: A, y: A) = x.id == y.id
}
then you can request that things are members of the Eq typeclass:
def useSomeObjects[A](a: A, b: A)(implicit aEq: Eq[A]) = {
... aEq.equal(a, b) ...
}
So you can decide which notion of equality to use by importing the appropriate typeclass in scope, or passing the typeclass instance directly as in useSomeObjects(x, y)(idEq[Int, SomeClass])
Note that you might also need a Hashable typeclass, similarly.
Autogenerating Eq instances
This situation is pretty similar to the Scala stdlib's scala.math.Ordering typeclass. Here is an example for auto-deriving structural Ordering instances for case classes using the excellent shapeless library.
The same would easy to be done for Eq and Hashable.
Scalaz
Note that scalaz has Equal typeclass, with nice pimp patterns with which you can write x === y instead of eqInstance.equal(x, y). I'm not aware it has Hashable typeclass, yet.