Existentials vs Covariance in Scala - scala

Consider the two codes below. They accomplish the same goal : only such A[T]-s can be stored in the Container where T extends C
However they use two different approaches to achieve this goal :
1) existentials
2) covariance
I prefer the first solution because then A remains simpler. Is there any reason why I ever would want to use the second solution (covariance) ?
My problem with the second solution is that it is not natural in the sense that it should not be A-s responsibility to describe what I can store in a Container and what not, that should be the Container's responsibility. The second solution is also more complicated once I want to start to operate on A and then I have to deal with all the stuff that comes with covariance.
What benefit would I get by using the second (more complicated, less natural) solution ?
object Existentials extends App {
class A[T](var t:T)
class C
class C1 extends C
class C2 extends C
class Z
class Container[T]{
var t:T = _
}
val c=new Container[A[_<:C]]()
c.t=new A(new C)
// c.t=new Z // not compile
val r: A[_ <: C] = c.t
println(r)
}
object Cov extends App{
class A[+T](val t:T)
class C
class C1 extends C
class C2 extends C
class Z
class Container[T]{
var t:T = _
}
val c: Container[A[C]] =new Container[A[C]]()
c.t=new A(new C)
//c.t=new A(new Z) // not compile
val r: A[C] = c.t
println(r)
}
EDIT (in response to Alexey's answer):
Commenting on :
"My problem with the second solution is that it is not natural in the sense that it should not be A-s responsibility to describe what I can store in a Container and what not, that should be the Container's responsibility."
If I have class A[T](var t:T) that means that I can store only A[T]-s and not ( A[S] where S<:T ) in a container, in any container.
However if I have class A[+T](var t:T) then I can store A[S] where S<:T as well in any container.
So when declaring A either to be invariant or covariant I decide what type of A[S] can be stored in a container (as shown above), this decision takes place at the declaration of A.
However , I think, this decision should take place, instead, at the declaration of the container because it is container specific what will be allowed to go into that container, only A[T]-s or also A[S] where S<:T-s.
In other words, changing the variance in A[T] has effects globally, while changing the type parameter of a container from A[T] to A[_<:S] has a well defined local effect on the container itself. So the principle of "changes should have local effects" here favors the existential solution as well.

In the first case A is simpler, but in the second case its clients are. Since there is normally more than one place where you use A, this is often a worthwhile tradeoff. Your own code demonstrates it: when you need to write A[_ <: C] in the first case (in two places), you can just use A[C] in the second one.
In addition, in the first case you can write just A[C] where A[_ <: C] is really desired. Let's say you have a method
def foo(x: A[C]): C = x.t
Now you can't call foo(y) with y: A[C1] even though it would make sense: y.t does have type C.
When this happens in your code, it can be fixed, but what about third-party?
Of course, this applies to the standard library types as well: if types like Maybe and List weren't covariant, either signatures for all methods taking/returning them would have to be more complex or many programs which are currently valid and make perfect sense would break.
it should not be A-s responsibility to describe what I can store in a Container and what not, that should be the Container's responsibility.
Variance isn't about what you can store in a container; it is about when A[B] is a subtype of A[C]. This argument is a bit like saying that you shouldn't have extends at all: otherwise class Apple extends Fruit allows you to store an Apple in Container[Fruit], and deciding that is Container's responsibility.

Related

Type safe dominoes in scala

So I'm trying to write a dominoes game server, and I'm writing my core types, tiles and sets of dominoes and it occurs to me that including type information for the tile pips would allow me to write a much simpler function that creates chains of dominoes, I've started and left this project incomplete several times due to my being unable to figure this out, hoping someone has a simple type safe tile representation that leads to a simple domino chain function. the reason being is my current mental model is a board in a dominoes games is just an initial tile and 1-3 chains of dominoes each beginning and matching the pips on the initial tile.
Thanks so much in advance, and apologies for any imperfections in my question.
sealed case class DoubleSix[L >: Nat, R <: Nat](lPips: Int, rPips: Int) extends Tile[L, R]
object DoubleSixSet {
val zeroZero: DoubleSix[_0, _0] = DoubleSix(0, 0)
}
an older attempt at the type safe chaining function.
trait DominoChain[End] {
// val hasSpinner: Boolean
}
case object End extends DominoChain[Nothing] {
// val hasSpinner = false
}
case class Chain[D0, D1, X](head: Domino[D0, D1], tail: DominoChain[Domino[D1, X]]) extends DominoChain[Domino[D0, D1]] {
def hasSpinner =
if (head.isDouble || rest.hasSpinner) true
else false
}
As you probably noticed expressing dominoes in types is easy:
sealed trait One
sealed trait Two
sealed trait Three
sealed trait Four
sealed trait Five
sealed trait Six
sealed trait Domino[A, B] extends Product with Serializable
object Domino {
case object OneOne[One, One]
case object OneTwo[One, Two]
... // the other types of dominoes
}
If you want to have a linear chain it is also easy:
sealed trait Chain[A, B] extends Product with Serializable
object Chain {
case class One[A, B](domino: Domino[A, B]) extends Chain[A, B]
case class Prepend[A, B, C](head: Domino[A, B], tail: Chain[B, C]) extends Chain[A, C]
}
Things gets tricky if this is not linear though. You might want to make a turn. There is more than one way of doing this:
xxyy
yy
xx
xx
yy
xx
y
y
y
y
xx
and each of them would have to be expressed as a separate case. If you would like to avoid things like:
f <- f tile would have to be over or under bb tile
aabbc f
e c
edd
you would have to somehow detect such case and prevent it. You have 2 options here:
don't express it in type, express it as value and use some smart constructor which would calculate if your move is valid and return chain with added tile or error
express each turn as a different type, express it in type level and require some evidence in order to create a tile. This should be possible but much much harder, and it would require you to know the exact type in compile time (so adding tile dynamically on demand could be harder as you would have to have that evidence prepared upfront for each move)
But in domino besides turns we can also have branches:
aab
bdd
cc
If you wanted to express it in type, now you have a two heads you can prepend to (and one tail you can append to). And during the game you could have more of them, so you would have to somehow express both: how many branches you have but also to which one do you want to add a new tile. Still possible, but but complicates your code even further.
You could e.g. express heads with some sort of HList (if you are using shapeless) and use that representation to provide implicit telling you which element of HList you want to modify.
However at this point you have very little benefits of type-level programming: you have to know you types ahead of time, you would have difficulties adding new tiles dynamically, you would have to persist state in such a way you would be able to retrieve the exact type so that type-level evidence would work...
Because of that I suggest an approach which is still type-safe while much easier to approach: just use smart constructors:
type Position = UUID
sealed trait Chain extends Product with Serializable
object Chain {
// prevent user from accessing constructors and copy directly
sealed abstract case class One private (
domino: Domino,
position: Position
) extends Chain
sealed abstract case class PrependLine private (
domino: Domino,
position: Position,
chain: Chain
)
sealed abstract case class Branch private (
chain1: Chain,
chain2: Chain
)
def start(domino: Domino): Chain
// check if you can add domino at this position, recursively rewrite tree
// if needed to add it at the right branch or maybe even create a new branch
def prepend(domino: Domino, to: Chain, at: Position): Either[Error, Chain]
}
This will still make it impossible to create an "invalid" domino chain. At the same time it will be much easier to add new rules, expand functionalities and persist state between requests (you mentioned that you want to build a server).

How to extend a functionN class in Scala

I am new to Scala.
I have a Class A that extends a Class C. I also have a Class B that also extends a Class C
I want function objects of type A->B to extend C as well (and also other derived types, such as A->(A->B)). But I read in "Programming in scala" following:
A function literal is compiled into a class that when instantiated at runtime
is a function value.
Is there some way of automatically letting A->B extend C, other then manually having to create a new class that represents the function?
Functions in Scala are modelled via FunctionN trait. For example, simple one-input-one-output functions are all instances of the following trait:
trait Function1[-T1, +R] extends AnyRef
So what you're asking is "how can I make instances of Function also become subclasses of C". This is not doable via standard subtyping / inheritance, because obviously we can't modify the Function1 trait to make it extend your custom class C. Sure, we could make up a new class to represent the function as you suggested, but that will only take us so far and it's not so easy to implement, not to mention that any function you want to use as C will have to be converted to your pseudo-function trait first, which will make things horrible.
What we can do, however, is create a typeclass which then contains an implementation for A -> B, among others.
Let's take the following code as example:
trait A
trait B
trait C[T]
object C {
implicit val fa = new C[A] {}
implicit val fb = new C[B] {}
implicit val fab = new C[Function1[A, B]] {}
}
object Test extends scala.App {
val f: A => B = (a: A) => new B {}
def someMethod[Something: C](s: Something) = {
// uses "s", for example:
println(s)
}
someMethod(f) // Test$$$Lambda$6/1744347043#dfd3711
}
You haven't specified your motivation for making A -> B extend C, but obviously you want to be able to put A, B and A -> B under the "same umbrella" because you have, say, some method (called someMethod) which takes a C so with inheritance you can pass it values of type A, B or A -> B.
With a typeclass you achieve the same thing, with some extra advantages, such as e.g. adding D to the family one day without changing existing code (you would just need to implement an implicit value of type C[D] somewhere in scope).
So instead of having someMethod take instances of C, it simply takes something (let's call it s) of some type (let's call it Something), with the constraint that C[Something] must exist. If you pass something for which an instance of C doesn't exist, you will get an error:
trait NotC
someMethod(new NotC {})
// Error: could not find implicit value for evidence parameter of type C[NotC]
You achieve the same thing - you have a family of C whose members are A, B and A => B, but you go around subtyping problems.

How to define a tree data structure that has edge type

I want to define a tree datastructure that has a label for its edges, here is my initial thought:
sealed trait Tree[+N,+E]
case class Branch[N,E](value:N, children:Map[E, Tree[N,E]]) extends Tree[N,E]
case class Leaf[N](value:N) extends Tree[N, Unit]
As you may guess, if I make a tree instance like following, the type of the instance would be Tree[Int, Any]. I also don't want to have a contravariant type for E.
val tree =
Branch(0,
Map(
"a" -> Leaf(1),
"b" -> Branch(2,
Map(
"c" -> Leaf(4))))
)
I want to know what would be a better way to implement this Tre. I've heard of Fix type classes and recursion schemes but I don't know if they can be useful here.
Have Leaf extend Tree[N, Nothing], not Tree[N, Unit]. Nothing is a bottom type, so that means Leaf[N] will be a subtype of Tree[N, E] for any E since you have it covariant.
case class Leaf[N](value:N) extends Tree[N, Nothing]
With that change, your example tree definition works just fine.
Fixpoint types are more complicated but not always necessary. But you could certainly modify this to make use of them. If you do want to go down that road, it might be better to ask it as another more specific question.

Using a Lens on a non-case class extending something with a constructor in Scala

I am probably thinking about this the wrong way, but I am having trouble in Scala to use lenses on classes extending something with a constructor.
class A(c: Config) extends B(c) {
val x: String = doSomeProcessing(c, y) // y comes from B
}
I am trying to create a Lens to mutate this class, but am having trouble doing so. Here is what I would like to be able to do:
val l = Lens(
get = (_: A).x,
set = (c: A, xx: String) => c.copy(x = xx) // doesn't work because not a case class
)
I think it all boils down to finding a good way to mutate this class.
What are my options to achieve something like that? I was thinking about this in 2 ways:
Move the initialization logic into a companion A object into a def apply(c: Config), and change the A class to be a case class that gets created from the companion object. Unfortunately I can't extend from B(c) in my object because I only have access to c in its apply method.
Make x a var. Then in the Lens.set just A.clone then set the value of x then return the cloned instance. This would probably work but seems pretty ugly, not to mention changing this to a var might raise a few eyebrows.
Use some reflection magic to do the copy. Not really a fan of this approach if I can avoid it.
What do you think? Am I thinking about this really the wrong way, or is there an easy solution to this problem?
This depends on what you expect your Lens to do. A Lens laws specify that the setter should replace the value that the getter would get, while keeping everything else unchanged. It is unclear what is meant by everything else here.
Do you wish to have the constructor for B called when setting? Do you which the doSomeProcessing method called?
If all your methods are purely functional, then you may consider that the class A has two "fields", c: Config and x: String, so you might as well replace it with a case class with those fields. However, this will cause a problem while trying to implement the constructor with only c as parameter.
What I would consider is doing the following:
class A(val c: Config) extends B(c) {
val x = doSomeProcessing(c, y)
def copy(newX: String) = new A(c) { override val x = newX }
}
The Lens you wrote is now perfectly valid (except for the named parameter in the copy method).
Be careful if you have other properties in A which depend on x, this might create an instance with unexpected values for these.
If you do not wish c to be a property of class A, then you won't be able to clone it, or to rebuild an instance without giving a Config to your builder, which Lenses builder cannot have, so it seems your goal would be unachievable.

Put method in trait or in case class?

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"