I am trying to create the case classes as explained in this article
sealed abstract case class Exp()
case class Literal(x:Int) extends Exp
case class Add(a:Exp, b:Exp) extends Exp
case class Sub(a:Exp,b:Exp) extends Exp
However, I am getting the following error in IntelliJ. I understand why it is prohibited (Why case-to-case inheritance is prohibited in Scala). What is the alternate way here?
Error:(2, 13) case class Literal has case ancestor A$A34.A$A34.Exp, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
case class Literal(x:Int) extends Exp
^
Exp shouldn't use the case keyword. That is, a sealed abstract case class will rarely, if ever, make sense to use.
In this specific case, the only extra thing you get from sealed abstract case class Exp() is an auto-generated companion object Exp that has an unapply method. And this unapply method won't be very useful, because there isn't anything to extract from the generic Exp. That is, you only care about decomposing Add, Sub, etc.
This is just fine:
sealed abstract class Exp
case class Literal(x: Int) extends Exp
case class Add(a: Exp, b: Exp) extends Exp
case class Sub(a: Exp, b: Exp) extends Exp
Related
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
}
I'm implementing scala interpreter and I have some problem.
To begin with, I want to implement tuple class
In my Value.scala, followed class exist:
case class TupleV(values: List[Type]) extends Value
Also, in my Expr.scala, followed class exist
case class TupleE(expressions: List[Type] extends Expr
Type is also in Expr.scala as followed:
sealed trait Type
case object IntT extends Type
case object BooleanT extends Type
case object TupleT extends Type
case object ListT extends Type
case object FunctionT extends Type
So, I implemented as followed:
case TupleE(values)=>TupleV(values)
but it says List[Expr] found, List[Value] is required. What is the problem in my code? what should I to to fix it?
Also I tried to implement in different way using ConsE and ConsV class which enable me to divide list into head and tail:
case class ConsE(head: Expr, tail: Expr) extends Expr//in Expr.scala
case class ConsV(head: Value, tail: Value) extends Value//in Value.scala
case ConsE(head, tail)=>ConsV(interp(head), interp(tail)//my implementation for ConsV interpreter&works well now
Using that, I tried
case TupleE(expression)=>expression match{
case ConsE(head, tail)=>ConsV(head, tail)
}
But it returns: "constructor cannot be instantiated to expected type" error for ConsE. How should I fix it?
Try to replace
case class TupleV(values: List[Type]) extends Value
case class TupleE(expressions: List[Type]) extends Expr
with
case class TupleV(values: List[Value]) extends Value
case class TupleE(expressions: List[Expr]) extends Expr
(Are those definitions given to you in an exercise or are they your own definitions that you can modify?)
Based on
case TupleE(values)=>TupleV(values)
you're writing interpreter
def interpret(expr: Expr): Value
i.e. continue what you started in How should I implement "add" interpreter in scala?
Then the definitions of TupleV, TupleE should be latter above. Former their definitions above make less sense to me.
Type is needed when you typecheck an Expr
def typecheck(expr: Expr): Type // or Option[Type]
Then you'll have one more hierarchy
case class TupleT(types: List[Type]) extends Type
So, I implemented as followed:
case TupleE(values)=>TupleV(values)
but it says List[Expr] found, List[Value] is required. What is the
problem in my code? what should I to to fix it?
This is irreproducible. The code compiles
https://scastie.scala-lang.org/KWlyFOYDRHOgN6UibVY1pw
Using that, I tried
case TupleE(expression)=>expression match{
case ConsE(head, tail)=>ConsV(head, tail)
}
But it returns: "constructor cannot be instantiated to expected type"
error for ConsE.
This error is clear: in TupleE(expression) expression has type List[Type], it can't match ConsE, it can match only ordinary scala List.
I wrote some code for Logic Expressions in Scala and I was wondering if I could find a way to mix in a trait into an abstract class, so I don't have to extend it in every subclass. Here is what I've got:
abstract class LogicExpression
case class Literal(lit:String, not:Boolean) extends LogicExpression with Ordered[Literal]
case class Conjunction(lits:Seq[Literal]) extends LogicExpression with Ordered[Conjunction]
...
I want every case class to be only Comparable to another instance of itself (Literal with Literal, Conjunction only with Conjunction, etc) but I would like to extend the trait in the abstract class so I don't have to repeat it on every subclass. Is this possible?
I tried
abstract class LogicExpression extends Ordered[LogicFormula]
but that would also allow comparing Literal with Conjunction for example.
You need something like this:
sealed trait LogicExpression[T <: LogicExpression[T]] extends Ordered[T]
case class SomeExpression() extends LogicExpression[SomeExpression] {
override def compare(that: SomeExpression) = ???
}
While trying to learn Akka, I often find examples with a class hierarchy similar to this:
sealed trait Message
case class TextMessage(user: String, text: String) extends Message
case class StatusMessage(status: String) extends Message
However, in the Scala docs there's a following example:
abstract class Notification
case class Email(sourceEmail: String, title: String, body: String) extends Notification
case class SMS(sourceNumber: String, message: String) extends Notification
case class VoiceRecording(contactName: String, link: String) extends Notification
What's the difference in using a sealed trait vs. an abstract class (or sealed abstract class in this case) as a base class without constructor parameters for a class hierarchy? Are there some advantages in using one over the other?
Edit:
Specifically, if both, the trait and the abstract class are sealed, I can't extend them outside the file, right? In that case I couldn't inherit from them in Java either? If that's the case, being sealed would render most of the arguments found in the suggested duplicate useless since they refer to inheritance outside the file.
In this particular case there are no differences except that you can't extend multiple abstract classes but you can extend multiple traits.
You should check other answers (as mentioned in the comments) to see the actual differences between abstract classes and traits. If you are just going to use an abstract class or a trait to define the type hierarchy as in this case, then there are no differences.
E.g. you could to the following:
trait A
trait B
case class C(a: Int) extends A with B
but you can't do:
abstract class A
abstract class B
case class C(a: Int) extends A with B
What is the difference between sealed abstract and abstract Scala class?
The difference is that all subclasses of a sealed class (whether it's abstract or not) must be in the same file as the sealed class.
As answered, all directly inheriting subclasses of a sealed class (abstract or not) must be in the same file. A practical consequence of this is that the compiler can warn if the pattern match is incomplete. For instance:
sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf[T](value: T) extends Tree
case object Empty extends Tree
def dps(t: Tree): Unit = t match {
case Node(left, right) => dps(left); dps(right)
case Leaf(x) => println("Leaf "+x)
// case Empty => println("Empty") // Compiler warns here
}
If the Tree is sealed, then the compiler warns unless that last line is uncommented.