Scala: pattern matching on a type with a given "path"? - scala

Lets say I have:
object myObject{
class myClass1(){
class myClass11(){
class myClass111
}
class myClass12(){
class myClass121
}
}
}
class myClass{
class myClass2(){
object myObj21
object myObj22
}
}
I want to pattern match on the types and objects introduced above and get instances of:
myClass1 only (nested type inside an object)
myClass11 and myClass12 (doubly nested types inside aObject/aClass)
myClass2 only (nested type inside a class)
myObj21 , myObj22 only. (doubly nested types inside aClass/aClass)
myClass111, myClass121. ( nested types in different branches but the same nest level and same "root")
ie. different variations of object/class and same level types or same sublevel types.
Can I do this with a type "regex" equivalent instead of spelling the type paths explicitly?

No, there is no nice way to do this. Pattern matching on nested types at all is iffy, let alone complex matches about them.
You might be able to write some custom unapply to do this kind of stuff using reflection, or perhaps even a macro. But thinking about that rabbit hole makes me wonder why this is necessary in the first place. Maybe you could add some traits that represent what actual behavior you care about, and pattern match on those?

Related

Using Enumerations in Scala Best Practices

I have been using sealed traits and case objects to define enumerated types in Scala and I recently came across another approach to extend the Enumeration class in Scala like this below:
object CertificateStatusEnum extends Enumeration {
val Accepted, SignatureError, CertificateExpired, CertificateRevoked, NoCertificateAvailable, CertChainError, ContractCancelled = Value
}
against doing something like this:
sealed trait CertificateStatus
object CertificateStatus extends {
case object Accepted extends CertificateStatus
case object SignatureError extends CertificateStatus
case object CertificateExpired extends CertificateStatus
case object CertificateRevoked extends CertificateStatus
case object NoCertificateAvailable extends CertificateStatus
case object CertChainError extends CertificateStatus
case object ContractCancelled extends CertificateStatus
}
What is considered a good approach?
They both get the job done for simple purposes, but in terms of best practice, the use of sealed traits + case objects is more flexible.
The story behind is that since Scala came with everything Java had, so Java had enumerations and Scala had to put them there for interoperability reasons. But Scala does not need them, because it supports ADTs (algebraic data types) so it can generate enumeration in a functional way like the one you just saw.
You'll encounter certain limitations with the normal Enumeration class:
the inability of the compiler to detect pattern matches exhaustively
it's actually harder to extend the elements to hold more data besides the String name and the Int id, because Value is final.
at runtime, all enums have the same type because of type erasure, so limited type level programming - for example, you can't have overloaded methods.
when you did object CertificateStatusEnum extends Enumeration your enumerations will not be defined as CertificateStatusEnum type, but as CertificateStatusEnum.Value - so you have to use some type aliases to fix that. The problem with this is the type of your companion will still be CertificateStatusEnum.Value.type so you'll end up doing multiple aliases to fix that, and have a rather confusing enumeration.
On the other hand, the algebraic data type comes as a type-safe alternative where you specify the shape of each element and to encode the enumeration you just need sum types which are expressed exactly using sealed traits (or abstract classes) and case objects.
These solve the limitations of the Enumeration class, but you'll encounter some other (minor) drawbacks, though these are not that limiting:
case objects won't have a default order - so if you need one, you'll have to add your id as an attribute in the sealed trait and provide an ordering method.
a somewhat problematic issue is that even though case objects are serializable, if you need to deserialize your enumeration, there is no easy way to deserialize a case object from its enumeration name. You will most probably need to write a custom deserializer.
you can't iterate over them by default as you could using Enumeration. But it's not a very common use case. Nevertheless, it can be easily achieved, e.g. :
object CertificateStatus extends {
val values: Seq[CertificateStatus] = Seq(
Accepted,
SignatureError,
CertificateExpired,
CertificateRevoked,
NoCertificateAvailable,
CertChainError,
ContractCancelled
)
// rest of the code
}
In practice, there's nothing that you can do with Enumeration that you can't do with sealed trait + case objects. So the former went out of people's preferences, in favor of the latter.
This comparison only concerns Scala 2.
In Scala 3, they unified ADTs and their generalized versions (GADTs) with enums under a new powerful syntax, effectively giving you everything you need. So you'll have every reason to use them. As Gael mentioned, they became first-class entities.
It depends on what you want from enum.
In the first case, you implicitly have an order on items (accessed by id property). Reordering has consequences.
I'd prefer 'case object', in some cases enum item could have extra info in the constructor (like, Color with RGB, not just name).
Also, I'd recommend https://index.scala-lang.org/mrvisser/sealerate or similar libraries. That allows iterating over all elements.

Scala type alias with companion object

I'm a relatively new Scala user and I wanted to get an opinion on the current design of my code.
I have a few classes that are all represented as fixed length Vector[Byte] (ultimately they are used in a learning algorithm that requires a byte string), say A, B and C.
I would like these classes to be referred to as A, B and C elsewhere in the package for readability sake and I don't need to add any extra class methods to Vector for these methods. Hence, I don't think the extend-my-library pattern is useful here.
However, I would like to include all the useful functional methods that come with Vector without having to 'drill' into a wrapper object each time. As efficiency is important here, I also didn't want the added weight of a wrapper.
Therefore I decided to define type aliases in the package object:
package object abc {
type A: Vector[Byte]
type B: Vector[Byte]
type C: Vector[Byte]
}
However, each has it's own fixed length and I would like to include factory methods for their creation. It seems like this is what companion objects are for. This is how my final design looks:
package object abc {
type A: Vector[Byte]
object A {
val LENGTH: Int = ...
def apply(...): A = {
Vector.tabulate...
}
}
...
}
Everything compiles and it allows me to do stuff like this:
val a: A = A(...)
a map {...} mkString(...)
I can't find anything specifically warning against writing companion objects for type aliases, but it seems it goes against how type aliases should be used. It also means that all three of these classes are defined in the same file, when ideally they should be separated.
Are there any hidden problems with this approach?
Is there a better design for this problem?
Thanks.
I guess it is totally ok, because you are not really implementing a companion object.
If you were, you would have access to private fields of immutable.Vector from inside object A (like e.g. private var dirty), which you do not have.
Thus, although it somewhat feels like A is a companion object, it really isn't.
If it were possible to create a companion object for any type by using type alias would make member visibility constraints moot (except maybe for private|protected[this]).
Furthermore, naming the object like the type alias clarifies context and purpose of the object, which is a plus in my book.
Having them all in one file is something that is pretty common in scala as I know it (e.g. when using the type class pattern).
Thus:
No pitfalls, I know of.
And, imho, no need for a different approach.

Java: help understanding the use of interfaces as a data type?

I am having trouble understanding with some of the code snippets about this part of the Java tutorial: http://docs.oracle.com/javase/tutorial/java/IandI/interfaceAsType.html
public Object findLargest(Object object1, Object object2) {
Relatable obj1 = (Relatable)object1;
Relatable obj2 = (Relatable)object2;
if ((obj1).isLargerThan(obj2) > 0)
return object1;
else
return object2;
}
and:
public interface Relatable {
// this (object calling isLargerThan)
// and other must be instances of
// the same class returns 1, 0, -1
// if this is greater than,
// equal to, or less than other
public int isLargerThan(Relatable other);
}
In the first example, why am I downcasting Object types into Relatable types? What happens if the first method doesn't include the first two statements?
Let's say I wrote a Rectangle class that implements the Relatable interface and has the "findLargest" method. If I know that I'm comparing two Rectangle objects, why not just make the first method downcast the objects into Rectangles instead?
You cast the Objects into Relatable types because otherwise you cannot use the methods declared in the Relatable interface. Since Object does not have the isLargerThan method, you would get a compiler error without casting. Honestly, in my opinion the findLargest method as shown here was not very well designed; a better illustration of the purpose of Interfaces would be to ask for Relatable objects as the parameters like so:
public Object findLargest(Relatable object1, Relatable object2) {
//implementation not shown to save space
}
This way, the user must pass Relatable objects, but they can pass any object whose class implements Relatable (such as Rectangle)
"If I know that I'm comparing two Rectangle objects..."True, if you know that you are comparing two Rectangle objects, there is little use for an interface, but the purpose of interfaces is to allow you to create a generic "type" of object that can be used to define common features of several different classes.For example, what if you also had a Circle class and a Square class (both of which implemented Relatable)? In this case, you do not necessarily know the exact type of object you have, but you would know that it is Relatable, so it would be best to cast to type Relatable and use the isLargerThan method in a case like this.
Interfaces define a set of methods which every class which the interface implements has to implement. The downcast is necessary to get access to these methods.
You don't know if you are comparing rectangles with this interface. You could get any Relatble passed. This is one of the cases generics come in handy.
1.In the first example, why am I down casting Object types into Relatable types? What happens if the first method doesn't include the first two statements?
Answer
Every object has some basic functionality and you want a specific object write now. You are down casting your object into a "Relatable" so you can use the "isLargerThan" method(an object wont have it since it has only basic common stuff).
If you didn't down cast, you would not pass compilation.
2.Let's say I wrote a Rectangle class that implements the Relatable interface and has the "findLargest" method. If I know that I'm comparing two Rectangle objects, why not just make the first method downcast the objects into Rectangles instead?
Answer
Since you want to create something generic.
Lets say you have a Student and a Driver. Both of them are People. You can create an interface called IPeople and make both the Student and the driver implement it.
IPeople will have a method called "getAge()" that each of them will implement.
IPeople will have all the functionality that you need for "People". That's how you create cross object functionality under the "same hat".

How to properly set up a class that inherits from another in Scala?

I have been looking at examples online, and tutorials, and I cannot find anything that explains how this (inheritance) differs from java. Simple example:
class Shape {
String type;
Shape(String type) {
this.type = type;
}
...
}
class Square extends Shape {
Square(String name){
Super(name);
}
....
}
Whats confusing me is in the above example I need to call the super class in order to set the 'type' variable, as well as to access it to tell me the Box objects' type as well. In Scala, how can this be done? I know scala uses traits interfaces as well, but is the above example omitted completely from scala? Can anyone direct me to a good example or explain it. I really appreciate it.
You can write almost exactly the same thing in Scala, much more concisely:
class Shape(var `type`: String)
class Square(name: String) extends Shape(name)
In the first line, the fact that type is preceded by var makes the compiler add getters and setters (from "5.3 Class Definitions" in the specification):
If a formal parameter declaration x : T is preceded by a val or
var keyword, an accessor (getter) definition (§4.2) for this parameter is implicitly added to the class. The getter introduces a value member x of class c that is defined as an alias of the parameter. If the introducing keyword is var, a setter accessor x _= (§4.2) is also implicitly added to the class.
In the second line name is not preceded by val or var, and is therefore just a constructor parameter, which is this case we pass on to the superclass constructor in the extends clause. No getters or setters are added for name, so if we created an instance square of Square and called square.name, it wouldn't compile.
Note also that type is a keyword in Scala, so I've had to surround it by backticks in both the definition and the example above:
Example 1.1.2 Backquote-enclosed strings are a solution when one needs to access Java identifiers that are reserved words in Scala.
There are many, many resource that you can read for more information about inheritance in Scala. See for example Chapters 4 and 5 of Programming Scala.

Creating an arraylist in Scala

I am a teachers assistant for a class that teaches Scala. As an assignment, I want the students to implement an arraylist class.
In java I wrote it like:
public class ArrayList<T> implements List<T>{....}
Is there any equivalent List trait that I should use to implement the arraylist?
The name ArrayList suggests that you should mix-in IndexedSeq. Actually you probably want to get all the goodies that are provided by IndexedSeqLike, i.e.
class ArrayList[A] extends IndexedSeq[A] with IndexedSeqLike[A, ArrayList[A]]
This gets you concrete implementations of head, tail, take, drop, filter, etc. If you also want map, flatMap, etc. (all the methods that take a type parameter) to work properly (return an ArrayList[A]), you also have to provide a type class instance for CanBuildFrom in your companion object, e.g.
def cbf[A, B] = new CanBuildFrom[ArrayList[A], B, ArrayList[B]] {
// TODO Implementation!
}
The scala collection library is very complex. For an overview on the inheritance take a look at these pictures:
scala.collection.immutable: http://www.scala-lang.org/docu/files/collections-api/collections.immutable.png
scala.collection.mutable: http://www.scala-lang.org/docu/files/collections-api/collections.mutable.png
Also the scaladoc gives a good overview about all the classes and traits of the collection library.
Be aware, that in Scala a List is a real list, meaning it is a LinearSeq, in Java a List is more like an IndexedSeq in Scala.
In Scala there are many Interfaces. First, they are separated in mutable and immutable ones. In Java ArrayList is based on an array - thus it is an indexed sequence. In Scala the interface for this is IndexedSeq[A]. Because ArrayList is also mutable, you can choose scala.collection.mutable.IndexedSeq otherwise scala.collection.immutable.IndexedSeq. Instead of mutable.IndexedSeq you can also choose scala.collection.mutable.Buffer, which does not guarantee an access time of O(1).
If you wanna have a more functional approach you can prefer Seq[A] as interface or Iterable[A] if you want to be able to implement more than Sequences.
That would be Seq[T], or maybe IndexedSeq[T] - or even List[T].