How to combine ADTs in Scala? - scala

I have two layers in my app: domain and application. Each layer has its own "error" ADT. For instance:
package com.domain.person
sealed trait DomainError
case object NoPermission extends DomainError
final case class Person(hasPermission: Boolean): Either[DomainError, ???] {
def doSomething() = {
if (!hasPermission)
Left(NoPermission)
else
...
}
}
and in my application layer (another package):
package com.application.person
sealed trait ApplicationError
case object PersonNotFound extends ApplicationError
case object UnexpectedFatalError extends ApplicationError
// and a function f :: Either ApplicationError Something
The issue is, since DomainError lives in another package, I can't just simply extend my ApplicationError trait:
sealed trait ApplicationError extends DomainError // compilation error
I could create yet another case object to wrap DomainError:
sealed trait ApplicationError
// n list of errors, and then:
final case class WrappedDomainError(d: DomainError) extends ApplicationError
but that solution is suboptimal at best.
And also, what if I want to be more specific in my doSomething() and, instead of returning a whole DomainError, a different subset?
doSomething :: Either DoSomethingErrors ???
I would have to account for all cases in each of my domain layer's functions.
Is there any way I can do a proper sum type in Scala?
Thanks

Wrapping your domain error in application error is not a bad idea, TBH. It's what I would've done in your situation. A few more options to consider:
make your DomainError and ApplicationError extends a common supertype Error, CommonError, Failure, etc. My personal preference is to extend Throwable - this way your error ASTs can become isomorphic to exceptions which can come in handy for Java interop reasons.
error channel also being composed of unions. Your final type will look somewhat like Either[ApplicationError Either DomainError, A]. It's a bit mouthful but you can make it look less ugly by introducing aliases.
type Result[+A] = Either[ApplicationError Either DomainError, A]
def doSomething: Result[???]
replace either with your own AST or use scalaz or other library's alternatives to Either3
sealed trait Result[+A]
case class Success[A](a: A) extends Result[A]
case class ApplicationErr(err: ApplicationError) extends Result[Nothing]
case class DomainErr[A](err: DomainErr) extends Result[Nothing]
def doSomething: Result[???]
Interpret DomainErrors into ApplicationErrors
val maybeDomainErrorVal: Either[DomainError, ???] = ???
val maybeApplicationErrorVal: Either[ApplicationError, ???] =
maybeDomainErrorVal.leftMap {
case NoPermission => UnexpectedFatalError
}

Related

How to make type classes resolve for base/abstract class?

Say I've a base abstract class and 2 case classes extending it.
sealed abstract class Base extends Product with Serializable
case class A(d: String) extends Base
case class B(d: Int) extends Base
And I have also a type class on A and B, for example
trait Show[T] {
def show(t: T): String
}
object Show {
def apply[T](t: T)(implicit show: Show[T]): String = show.show(t)
implicit val showA: Show[A] = new Show[A] {
def show(t: A): String = "A"
}
implicit val showB: Show[B] = new Show[B] {
def show(t: B): String = "B"
}
}
The problem I have is that, in my code I get A and B from deserialization and they have type Base. In this case scala fail to resolve the typeclasses because there's not type classes defined on Base.
I could solve this problem by defining an instance on Base and do a pattern match but IMO in this way we'd better not use typeclasses at all.
Is there any tricks that we can make scala resolve type classes for a base class?
Thanks.
No, there is no such trick. Type has to be statically known because it is the compiler who resolves the typeclass instance.
As you said an instance for Base that will resolve proper instance has to be created. It can be done manually but there might be better ways to do it. Maybe someone can provide a better answer on how to do it nicely, but fundamentally the instance for Base is needed.

Is it possible to accept a Sealed Trait with WebSocket.acceptOrResult in Play! scala

I would like to use a kind of typed WebSocket with Play! Scala.
I would like that WebSocket.acceptOrResult accepts some commands like:
case class Message(msg: String)
case class CommandA(c: String)
etc.
I could use
WebSocket.acceptOrResult[JsValue, JsValue]
and try to convert them to the different case classes (commands) sequentially but I find it ugly.
A really better way to do it if possible would be to use a Sealed Trait
ChatEvent and extend the command classes i.e.
case class Message(msg: String) extends ChatEvent
case class CommandA(c: String) extends ChatEvent
But if I use it as following, the commands are not accepted:
implicit val acommandFlowTransformer =
MessageFlowTransformer.jsonMessageFlowTransformer[ChatEvent, ChatEvent]
def openSocket = WebSocket.acceptOrResult[ChatEvent, ChatEvent]
I have used
import julienrf.json.derived
implicit val chatEventMessage: Format[ChatEvent] = derived.oformat[ChatEvent]
that allows me to do:
Json.toJson(CommandA("aaa")).validate[ChatEvent] ---> JsSuccess[CommandA]
IMO the problem is that the format of ChatEvent leads to the class that extends ChatEvent and acceptOrResult is waiting for a ChatEvent.
Maybe the solution would be to use something like:
def openClientSocket = WebSocket.acceptOrResult[_ :< ChatEvent, _ :< ChatEvent]
But I don't find how to make it compile.

May mix-in traits extend case classes from a technical perspective?

I read repeatedly on SO that case classes shall not be extended because a case class implements an equality method by default and that leads to issues of equality. However, if a trait extends a case class, is that also problematic?
case class MyCaseClass(string: String)
trait MyTrait extends MyCaseClass
val myCT = new MyCaseClass("hi") with MyTrait
I guess it boils down to the question, whether MyTrait is only forced to be mixable only into instantiations of MyCaseClass or whether MyTrait is inheriting the class members (field values and methods) of MyTrait and thus overwriting them. In the first case it would be okay to inherit from MyCaseClass, in the latter case it would not be okay. But which one is it?
To investigate, I advanced my experiment with
trait MyTrait extends MyCaseClass {
def equals(m: MyCaseClass): Boolean = false
def equals(m: MyCaseClass with MyTrait): Boolean = false
}
val myC = new MyCaseClass("hi")
myCT.equals(myC) // res0: Boolean = true
letting me to believe that the equals of MyCaseClass was used, not the one of MyTrait. This would suggest that it is okay for a trait to extend a case class (while it is not okay for a class to extend a case class).
However, I am not sure whether my experiment is legit. Could you shed some light on the matter?
Basically, trait can extend any class, so it's better to use them with regular classes (OOP-style).
Anyway, your equals contract is still broken regardless of your trick (note that standard Java's equals is defined on Any, that is used by default let's say in HashMap or even ==):
scala> trait MyTrait extends MyCaseClass {
| override def equals(m: Any): Boolean = false
| }
defined trait MyTrait
scala> val myCT = new MyCaseClass("hi") with MyTrait
myCT: MyCaseClass with MyTrait = MyCaseClass(hi)
scala> val myC = new MyCaseClass("hi")
myC: MyCaseClass = MyCaseClass(hi)
scala> myC.equals(myCT)
res4: Boolean = true
scala> myCT.equals(myC)
res5: Boolean = false
Besides, Hashcode/equals isn't the only reason...
Extending case class with another class is unnatural because case class represents ADT so it models only data - not behavior.
That's why you should not add any methods to it (in OOD terms case classes are designed for anemic approach). So, after eliminating methods - a trait that can only be mixed with your class becomes nonsense as the point of using traits with case classes is to model disjunction (so traits are interfaces here - not mix-ins):
//your data model (Haskell-like):
data Color = Red | Blue
//Scala
trait Color
case object Red extends Color
case object Blue extends Color
If Color could be mixed only with Blue - it's same as
data Color = Blue
Even if you require more complex data, like
//your data model (Haskell-like):
data Color = BlueLike | RedLike
data BlueLike = Blue | LightBlue
data RedLike = Red | Pink
//Scala
trait Color extends Red
trait BlueLike extends Color
trait RedLike extends Color
case class Red(name: String) extends RedLike //is OK
case class Blue(name: String) extends BlueLike //won't compile!!
binding Color to be only Red doesn't seem to be a good approach (in general) as you won't be able to case object Blue extends BlueLike
P.S. Case classes are not intended to be used in OOP-style (mix-ins are part of OOP) - they interact better with type-classes/pattern-matching. So I would recommend to move your complex method-like logic away from case class. One approach could be:
trait MyCaseClassLogic1 {
def applyLogic(cc: MyCaseClass, param: String) = {}
}
trait MyCaseClassLogic2 extends MyCaseClassLogic {
def applyLogic2(cc: MyCaseClass, param: String) = {}
}
object MyCaseClassLogic extends MyCaseClassLogic1 with MyCaseClassLogic2
You could use self-type or trait extends here but you can easily notice that it's redundant as applyLogic is bound to MyCaseClass only :)
Another approach is implicit class (or you can try more advanced stuff like type-classes)
implicit class MyCaseClassLogic(o: MyCaseClass) {
def applyLogic = {}
}
P.S.2 Anemic vs Rich. ADT is not precisely anemic model as it applies to immutable (stateless) data. If you read the article, Martin Fowler's approach is OOP/OOD which is stateful by default - that's what he assumes in most of the part of his article by implying that service layer and business layer should have separate states. in FP (at least in my practice) we still separate domain logic from service-logic, but we also separate operations from data (in every layer), which is another matter.
Extending case classes is a bad practice (generally), because it has concrete meaning -- "data container" (POJO / ADT). For example, Kotlin does not allow to do that.
Also, if you really want some trait to extend case class, you'd better use requires dependency (to avoid pitfalls with cases classes inheritance):
scala> case class A()
defined class A
scala> trait B { self: A => }
defined trait B
scala> new B{}
<console>:15: error: illegal inheritance;
self-type B does not conform to B's selftype B with A
new B{}

Default type class implementation in trait for sub-classes in Scala

I have the following relations:
trait Instrument
trait EquityOption extends Instrument { ... }
case class CallEquityOption(...) extends EquityOption
case class PutEquityOption(...) extends EquityOption
trait Priceable[I <: Instrument] { def price(I : Instrument) }
I can use exactly the same implementation of Priceable for the case classes CallEquityOptionand PutEquityOption. By having a match case to differentiation between the Call... and Put.... However, if I try to implement it directly as Priceable[EquityOption] under object EquityOption, the implicit cannot be found since it doesn't exactly match the type.
How can I make it work without needing to duplicate code?
You'll have to prove that you can provide an instance for every subtype of EquityOption.
implicit def allEquityOptions[T <: EquityOption]: Pricable[T] = ???

Abstracting ADT base logic that uses macros into trait

I recently stumbled on a rather awesome piece of code from Travis Brown # Iteration over a sealed trait in Scala?. It turns Scala's sealed trait + case class/object based ADTs into something that is closer to true enums by allowing enumeration of the ADT "constructors" (in Haskell's terms). So I removed 2 of the 3 deprecation warnings Travis' code was producing and ended up with http://pastebin.com/L1gYGJWh which I then used as follows:
sealed trait KeywordType
case object Crime extends KeywordType
case object Gambling extends KeywordType
case object Porn extends KeywordType
object KeywordType {
/** Maps e.g. "Crime" to Crime */
def withName(x: String): Option[KeywordType] =
adt.enumerate[KeywordType].find(_.toString === x)
}
However, I quickly needed to reuse the same bit of withName logic so I tried to write the following ADT base trait:
trait ADT[T] {
def all: Set[T] = enumerate[T]
def withName(name: String): Option[T] =
all.find(_.toString === name)
}
However, this does not compile giving:
Can only enumerate values of a sealed trait or class
Although I understand why this error occurs, I do not have a clue as to how to go about telling the type system to "defer" that check until something actually inherits from ADT.
Is this even possible with the current macro system in Scala 2.11.2 or do I have to find a (partial) workaround?