case class and traits - scala

I want create a special calculator. I think that case class is a good idea for operations:
sealed class Expr
case class add(op1:Int, op2:Int) extends Expr
case class sub(op1:Int, op2:Int) extends Expr
case class mul(op1:Int, op2:Int) extends Expr
case class div(op1:Int, op2:Int) extends Expr
case class sqrt(op:Int) extends Expr
case class neg(op:Int) extends Expr
/* ... */
Now I can use match-case for parsing input.
Maybe, I should also use traits (ie: trait Distributivity, trait Commutativity and so on), Is that posible? Is that a good idea?

Before you start adding traits of not-so-clear additional value, you should get the basics right. The way you do it now makes these classes not very useful, at least not when building a classical AST (or "parse tree"). Imagine 4 * (3+5). Before you can use the multiplication operation, you have to evaluate the addition first. That makes things complicated. What you usually want to have is the ability to write your formula "at once", e.g. Mul(4,Add(3, 5)). However that won't work that way, as you can't get Ints or Doubles into your own class hierarchy. The usual solution is a wrapper class for Numbers, say "Num". Then we have: Mul(Num(4),Add(Num(3), Num(5)). This might look complicated, but now you have "all at once" and you can do things like introducing constants and variables, simplification (e.g. Mul(Num(1),x) --> x), derivation...
To get this, you need something along the lines
sealed trait Expr {
def eval:Int
}
case class Num(n:Int) extends Expr {
def eval = n
}
case class Neg(e: Expr) extends Expr {
def eval = - e.eval()
}
case class Add(e1: Expr, e2: Expr) extends Expr {
def eval = e1.eval + e2.eval
}
...
Now you can write a parser that turns "4*(3+5)" into Mul(Num(4),Add(Num(3), Num(5)), and get the result by calling eval on that expression.
Scala contains already a parse library called parser combinators. For an example close to the code above see http://jim-mcbeath.blogspot.com/2008/09/scala-parser-combinators.html

Related

Scala "or" in generic bounds

I know that I can write something like this:
case class Something[T <: Foo with Bar](state: T)
This accepts classes which have the traits (or class and trait) Foo and Bar. This is an AND example where it is needed to extend both Foo and Bar. Is there an option which allows me to pass classes extending Foo OR Bar to pattern match against them?
The use case is that I have multiple Classes with different behaviors which consume states which are of shared types:
trait FooState
trait BarState
trait BazState
case class Foo(state: FooState) // must not accept BarState or BazState
case class Bar(state: BarState) // must not accept FooState or BazState
case class Baz(state: BazState) // must not accept FooState or BarState
case class FooBar(state: FooState or BarState) // must not accept BazState
case class FooBaz(state: FooState or BazState) // must not accept BarState
case class BarBaz(state: BarState or BazState) // must not accept FooState
I know I can create another trait for every compound class, but this would force me to add it to everything that extends any of these previous traits.
Yes, you would usually use a typeclass to achieve what you want, and a context bound. Here's how:
trait Acceptable
object Acceptable {
implicit val fooIsGood = new Acceptable[Foo] {}
implicit val barIsGood = new Acceptable[Bar] {}
}
case class Something[T : Acceptable](state: T)
And you can play with it to implement whatever functionality you want using this pattern. Achieving a real union type bound be done with Either or co-products, but in most scenarios this may be simpler.
One possible way to do this is to use the Either type:
case class FooBar(state: Either[FooState, BarState]) {
def someOperation() = {
state match {
case Left(fooState) => ???
case Right(barState) => ???
}
}
}
What you've described is a union type. The current version of Scala does not support them as you've described them, however it is planned for Dotty.
If you need more flexibility than that (more than two types for example) consider using a Coproduct from a functional programming library. Scalaz, Cats and Shapeless all expose them.

Sealing a class which has many subtypes

I'm porting one of my C++ programs into Scala. In that project, there are hundreds of user-defined classes in an organised hierarchy. If I seal one of the top-level abstract classes, according to Scala rules, I have to put the definition of all subtypes of that class in the same file as the sealed class/trait. For one of my class hierarchies it would mean putting the definition of about 30 classes in that file.
In my C++ program these 30 classes are located in their own header and implementation files making them easier to maintain and read. I fear that if I put the definition of those 30 classes/objects in one file in my Scala application, it will make them hard to maintain and read.
The reason for sealing the class is so that I can do exhaustive searches when pattern matching on those types. Any help to point in me in the right direction with regards to organising Scala class hierarchies would be appreciated.
It's a bit of a pain to do this in separate classes, but it might be less painful than having everything in one huge file. First, you need to make sure you compile all the files together. Then, in your file where you make sure everything is sealed, you do the following:
trait GenericA { def foo: Int }
sealed trait A extends GenericA
case class B() extends A with ImplB {}
case class C() extends A with ImplC {}
...
The trick is that everything in the superclass (and it can be an abstract class instead of a trait if you wish) goes into GenericA. But you never actually use GenericA in your code, you just use A. Now, you can write a bunch of separate files with each implementation, defined like so:
// File #1
trait ImplB extends GenericA { def foo = 7 }
// File #2
trait ImplC extends GenericA { def foo = 4 }
...
Now you have your implementations separated out (at least those parts which can be expressed in terms of GenericA only).
What if you need the case class parameters available also? No problem--just include those as part of the trait.
// Main file
case class D(i: Int) extends A with ImplD {}
// Separate file
trait ImplD {
def i: Int
def foo = i*3
}
It's a bit of extra work since you have to repeat the case class parameters in two spots, but in your case it may be worth it.
Assuming your classes are case-classes which have many methods (which could make your file grow), you can try to separate definition from implementation using type classes (but sometimes it could afffect compiler's performance), like:
Model.scala
sealed trait A
case class A1(a: Int) extends A
case class A2(a: Int) extends A
case class A3(a: Int, b: Int) extends A
...
case class A1(a: Int) extends A
ImplA1.scala
package org.impl
implicit class ImplA1(a: A1) {
def method1() = a.a + a.a
}
ImplA2.scala
package org.impl
implicit class ImplA2(a: A2) {
def method1() = a.a * 2
}
Usage:
import org.impl._
val a1 = new A1
a1.method1()

creating two related ASTs with sealed case classes in Scala

Whenever I've had to create an AST in Scala, I've used the abstract sealed trait/ case class pattern. It's worked really well so far, having compiler checked pattern matching is a big win.
However now I've hit a problem that I cant wrap my head around. What if I have 2 languages where one is a subset of the other? As a simple example consider a lambda calculus where every variable is bound, and another related language where the variables could be bound or free.
First language:
abstract sealed class Expression
case class Variable(val scope: Lambda, val name:String) extends Expression
case class Lambda(val v: Variable, val inner: Expression) extends Expression
case class Application(val function: Expression, val input: Expression) extends Expression
Second Language:
abstract sealed class Expression
case class Variable(val name:String) extends Expression
case class Lambda(val v: Variable, val inner: Expression) extends Expression
case class Application(val function: Expression, val input: Expression) extends Expression
Where the only change is the removal of scope from Variable.
As you can see there is a lot of redundancy. But because I'm using sealed classes, its hard to think of a good way to extend it. Another challenge to combining them would be that now every Lambda and Application needs to keep track of the language of its parameters, at the type level.
This example is not so bad because it is very small, but imagine the same problem for strict HTML/weak HTML.
The classical answer to this problem is to have a single general AST and an additional pass for validation. You'll have to live with ASTs that are well-formed syntactically, but won't pass validation (type-checking).
If you want to distinguish at the type level, the type-checking pass could produce a new AST. You might be able to use path-dependent types for that.
As a side-note, your example seems to have a cycle: to create a Lambda you need a Variable, but to create a Variable you need the outer Lambda.
When deciding how to generalize, it is sometimes helpful to think of an example function that would need to operate on the generalized structure. So, take some operation that you would want to perform on both bound and free trees. Take eta-reduction:
def tryEtaReduce(x: Expression): Option[Expression] =
x match {
case Lambda(v1, Application(f, v2: Variable)) if v1 == v2 => Some(f)
case _ => None
}
For the above function, a generalization like the following will work, although it has an obvious ugliness:
trait AST {
sealed trait Expression
type Scope
case class Variable(scope: Scope, name: String) extends Expression
case class Lambda(v: Variable, inner: Expression) extends Expression
case class Application(function: Expression, input: Expression) extends Expression
}
object BoundAST extends AST {
type Scope = Lambda
}
object FreeAST extends AST {
type Scope = Unit
}
trait ASTOps {
val ast: AST
import ast._
def tryEtaReduce(x: Expression): Option[Expression] =
x match {
case Lambda(v1, Application(f, v2: Variable)) if v1 == v2 =>
Some(f)
case _ =>
None
}
}

What are the disadvantages to declaring Scala case classes?

If you're writing code that's using lots of beautiful, immutable data structures, case classes appear to be a godsend, giving you all of the following for free with just one keyword:
Everything immutable by default
Getters automatically defined
Decent toString() implementation
Compliant equals() and hashCode()
Companion object with unapply() method for matching
But what are the disadvantages of defining an immutable data structure as a case class?
What restrictions does it place on the class or its clients?
Are there situations where you should prefer a non-case class?
First the good bits:
Everything immutable by default
Yes, and can even be overridden (using var) if you need it
Getters automatically defined
Possible in any class by prefixing params with val
Decent toString() implementation
Yes, very useful, but doable by hand on any class if necessary
Compliant equals() and hashCode()
Combined with easy pattern-matching, this is the main reason that people use case classes
Companion object with unapply() method for matching
Also possible to do by hand on any class by using extractors
This list should also include the uber-powerful copy method, one of the best things to come to Scala 2.8
Then the bad, there are only a handful of real restrictions with case classes:
You can't define apply in the companion object using the same signature as the compiler-generated method
In practice though, this is rarely a problem. Changing behaviour of the generated apply method is guaranteed to surprise users and should be strongly discouraged, the only justification for doing so is to validate input parameters - a task best done in the main constructor body (which also makes the validation available when using copy)
You can't subclass
True, though it's still possible for a case class to itself be a descendant. One common pattern is to build up a class hierarchy of traits, using case classes as the leaf nodes of the tree.
It's also worth noting the sealed modifier. Any subclass of a trait with this modifier must be declared in the same file. When pattern-matching against instances of the trait, the compiler can then warn you if you haven't checked for all possible concrete subclasses. When combined with case classes this can offer you a very high level level of confidence in your code if it compiles without warning.
As a subclass of Product, case classes can't have more than 22 parameters
No real workaround, except to stop abusing classes with this many params :)
Also...
One other restriction sometimes noted is that Scala doesn't (currently) support lazy params (like lazy vals, but as parameters). The workaround to this is to use a by-name param and assign it to a lazy val in the constructor. Unfortunately, by-name params don't mix with pattern matching, which prevents the technique being used with case classes as it breaks the compiler-generated extractor.
This is relevant if you want to implement highly-functional lazy data structures, and will hopefully be resolved with the addition of lazy params to a future release of Scala.
One big disadvantage: a case classes can't extend a case class. That's the restriction.
Other advantages you missed, listed for completeness: compliant serialization/deserialization, no need to use "new" keyword to create.
I prefer non-case classes for objects with mutable state, private state, or no state (e.g. most singleton components). Case classes for pretty much everything else.
I think the TDD principle apply here: do not over-design. When you declare something to be a case class, you are declaring a lot of functionality. That will decrease the flexibility you have in changing the class in the future.
For example, a case class has an equals method over the constructor parameters. You may not care about that when you first write your class, but, latter, may decide you want equality to ignore some of these parameters, or do something a bit different. However, client code may be written in the mean time that depends on case class equality.
Are there situations where you should prefer a non-case class?
Martin Odersky gives us a good starting point in his course Functional Programming Principles in Scala (Lecture 4.6 - Pattern Matching) that we could use when we must choose between class and case class.
The chapter 7 of Scala By Example contains the same example.
Say, we want to write an interpreter for arithmetic expressions. To
keep things simple initially, we restrict ourselves to just numbers
and + operations. Such expres- sions can be represented as a class
hierarchy, with an abstract base class Expr as the root, and two
subclasses Number and Sum. Then, an expression 1 + (3 + 7) would be represented as
new Sum( new Number(1), new Sum( new Number(3), new Number(7)))
abstract class Expr {
def eval: Int
}
class Number(n: Int) extends Expr {
def eval: Int = n
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
}
Furthermore, adding a new Prod class does not entail any changes to existing code:
class Prod(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval * e2.eval
}
In contrast, add a new method requires modification of all existing classes.
abstract class Expr {
def eval: Int
def print
}
class Number(n: Int) extends Expr {
def eval: Int = n
def print { Console.print(n) }
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
def print {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}
The same problem solved with case classes.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr
Adding a new method is a local change.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}
}
Adding a new Prod class requires potentially change all pattern matching.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
case Prod(e1,e2) => e1.eval * e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
case Prod(e1,e2) => ...
}
}
Transcript from the videolecture 4.6 Pattern Matching
Both of these designs are perfectly fine and choosing between them is sometimes a matter of style, but then nevertheless there are some criteria that are important.
One criteria could be, are you more often creating new sub-classes of expression or are you more often creating new methods? So it's a criterion that looks at the future extensibility and the possible extension pass of your system.
If what you do is mostly creating new subclasses, then actually the object oriented decomposition solution has the upper hand. The reason is that it's very easy and a very local change to just create a new subclass with an eval method, where as in the functional solution, you'd have to go back and change the code inside the eval method and add a new case to it.
On the other hand, if what you do will be create lots of new methods, but the class hierarchy itself will be kept relatively stable, then pattern matching is actually advantageous. Because, again, each new method in the pattern matching solution is just a local change, whether you put it in the base class, or maybe even outside the class hierarchy. Whereas a new method such as show in the object oriented decomposition would require a new incrementation is each sub class. So there would be more parts, That you have to touch.
So the problematic of this extensibility in two dimensions, where you might want to add new classes to a hierarchy, or you might want to add new methods, or maybe both, has been named the expression problem.
Remember: we must use this like a starting point and not like the only criteria.
I am quoting this from Scala cookbook by Alvin Alexander chapter 6: objects.
This is one of the many things that I found interesting in this book.
To provide multiple constructors for a case class, it’s important to know what the case class declaration actually does.
case class Person (var name: String)
If you look at the code the Scala compiler generates for the case class example, you’ll see that see it creates two output files, Person$.class and Person.class. If you disassemble Person$.class with the javap command, you’ll see that it contains an apply method, along with many others:
$ javap Person$
Compiled from "Person.scala"
public final class Person$ extends scala.runtime.AbstractFunction1 implements scala.ScalaObject,scala.Serializable{
public static final Person$ MODULE$;
public static {};
public final java.lang.String toString();
public scala.Option unapply(Person);
public Person apply(java.lang.String); // the apply method (returns a Person) public java.lang.Object readResolve();
public java.lang.Object apply(java.lang.Object);
}
You can also disassemble Person.class to see what it contains. For a simple class like this, it contains an additional 20 methods; this hidden bloat is one reason some developers don’t like case classes.

Scala - Prefix Unary Operators

I've recently given Scala a second chance, and started with the project I always implement (in functional or pseudo-functional languages): an automated reasoner for propositional logic (and later predicate logic).
Now, I've tried to get the notation of propositional logic in the language itself as pretty as possible, and I've gotten this far - with an implicit conversion (String -> Atom):
("A" and "B") implies "C"
The functions "and" and "implies" (and "or" and "equivalent") are simple methods that call the relevant case class constructor. However, when implementing "not", I get stuck with either of the two following notations:
("A" and "B").not
Not("A" and "B")
Is there a way to trick Scala into accepting the desired:
not("A" and "B")
Preferrably without renaming the class "Not" to "not", because I might like to call it "¬" or something else, in th future.
I noticed on this answer to another question that it appears that one can prefix the operator name with unary_ to achive what you are trying to do. (See unary_!.)
Edit: this article confirms the syntax.
You can define not as a method on a singleton object, like this:
object Logic {
def not(x:Expr) = Not(x)
}
import Logic._
not("A" and "B")
(Where Expr is supposed to be the common superclass of And, Or, Not and Atom)
Edit: Here's an example of how this could be used with only a single import:
object Logic {
abstract class Expr {
def and(e: Expr) = Conjunction(this, e)
def or(e: Expr) = Disjunction(this, e)
def implies(e: Expr) = Implication(this, e)
}
case class Conjunction(e1: Expr, e2: Expr) extends Expr
case class Disjunction(e1: Expr, e2: Expr) extends Expr
case class Implication(e1: Expr, e2: Expr) extends Expr
case class Negation(e: Expr) extends Expr
case class Atom(name: String) extends Expr
def not(e: Expr) = Negation(e)
implicit def string2atom(str: String) = Atom(str)
}
// use site
import Logic._
not("A" and "B") implies (not("A") or not("B"))
Why Not instead of not? There's nothing to stop you from doing this:
object not {
def apply(expr: T) = ...
}
And then use not("A" and "B").
As of Feb 2014, I think the cleanest way to define a prefix'ish not operation on expressions, while avoiding all sorts of extra cruft/wrapping, would be to declare the function directly in the package scope, together with all your other functions, classes, types etc: This is done by defining a package object (Scala doesn't allow you to just put functions at the root level of the .scala file (I'd love to learn why—is it just to follow Java's footsteps?)).
package org.my.logiclib
implicit class Atom(s: String) { ... }
class MyType1
class MyType2
object `package` {
def not(expr: Expr) = ...
}
this way, doing import org.my.logiclib._ will import everything, including not().
The above is the same as
package org.my
package logiclib {
implicit class Atom ...
...
def not(expr: Expr) = ...
}