How should I implement scala interpreter for tuple? - scala

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.

Related

How to combine ADTs in 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
}

If case class inheritance is prohibited, how to represent this?

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

Scala pattern match is not exhaustive on nested case classes

I've got a case class hierarchy to encode some request and processing errors:
sealed trait OpError
sealed trait RequestErrorType
sealed trait ProcessingErrorType
final case class InvalidEndpoint(reason: String) extends RequestErrorType
final case class InvalidParameters(reason: String) extends RequestErrorType
final case class InvalidFormat(response: String) extends ProcessingErrorType
final case class EntityNotFound(id: Long) extends ProcessingErrorType
final case class RequestError(errorType: RequestErrorType) extends OpError
final case class ProcessingError(errorType: ProcessingErrorType) extends OpError
If I write a simple match across all patterns:
def printMatches(error: OpError): Unit = error match {
case RequestError(InvalidEndpoint(reason)) => //print something
case RequestError(InvalidParameters(reason)) => //print something
case ProcessingError(InvalidFormat(format)) => //print something
case ProcessingError(EntityNotFound(entityId)) => //print something
}
the compiler gives me a warning about missing match:
match may not be exhaustive.
It would fail on the following input: ProcessingError(_)
def printMatches(error: OpError): Unit = error match {
But ProcessingError takes in a ProcessingErrorType with only two extensions: InvalidFormat and EntityNotFound, both which are accounted for in the pattern match. What am I missing?
Even more curious is that if I change the parameter type of InvalidParameters or InvalidEndpoint to a String*, I don't get the error:
final case class InvalidParameters(reason: String*) extends RequestErrorType
Any ideas?
This is a confirmed bug. I filed a bug report for this and it has been since fixed for Scala 2.12.0-M4.
Very interesting! Unfortunately, I haven't found an answer. I've been revolving around http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns but I haven't really found a valid explanation for what's going on.
Here's a simpler demo (hope you don't mind):
sealed abstract class ClassOne
case class ClassOneImpl() extends ClassOne
sealed abstract class ClassTwo()
case class ClassTwoImpl() extends ClassTwo
sealed abstract class Foo
case class FooOne(x: ClassOne) extends Foo
case class FooTwo(x: ClassTwo) extends Foo
def printMatches(st: Foo): Unit = st match {
case FooOne(ClassOneImpl()) => println()
case FooTwo(ClassTwoImpl()) => println()
}
I've observed that each of the following two modifications removes the warning:
1) Changing FooOne and FooTwo signatures so that instead of taking ClassOne and ClassTwo they take ClassOneImpl and ClassTwoImpl
2) Removing FooOne or FooTwo so that there's only one case class extending Foo (which leads to only one case in pattern matching).
Perhaps we could submit an issue and see what they say?
You can help the compiler with an unchecked annotation:
... = (error: #unchecked) match ...
but you should be sure, your match is exhaustive.
I think exhaustiveness matching works on a single inheritance level.
RequestErrorType and ProcessingErrorType are part of the constructor where that exhaustiveness is not checked.
You can see it from the reading of the code, but it seem that compiler does not.

Is it possible to use `case object` with a type parameter?

I'm currently learning Scala, and wanted to replicate this Haskell algebraic data type:
data Tree = Empty
| Leaf Int
| Node Tree Tree
This is what I came up with in Scala:
sealed trait Tree[T]
case class Empty[T]() extends Tree[T]
case class Leaf[T](value: T) extends Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]
However, someone told me that I should use a case object for Empty, which I suppose is true since it doesn't take parameters - but then again it does require a type parameter.
I tried the following but none of them compile:
case object Empty[T] extends Tree[T]
case object Empty extends Tree[T]
case object Empty extends Tree
So I'm wondering if there a way to use case object in this instance or not.
A singleton can't be generic because there's only one of them. If you want Tree to be covariant (i.e. Tree[Int] is a subtype of Tree[Any]), then you can define the types as
sealed trait Tree[+T]
case object Empty extends Tree[Nothing]
Otherwise, leave it as a case class.

Scala's sealed abstract vs abstract class

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.