« Return to Thread: annotations or traits to better scale scala up

annotations or traits to better scale scala up

by Walter Smith :: Rate this Message:

Reply to Author | View in Thread

Hi!

[this is more or less a repost directly to Nabble, as the original post to scala-lang.org didn't work properly, as I was told]

everybody will agree that Scala scales great from medium sized applications to small scripts, much better than esp. Java does. I often hear that it scales up to large systems just as well, but does it? I think it does, but could it be even better?

[This became a long post. If you don't want to read all the blurb, skip ahead to the [*]]

Scala has some very powerful features that alleviate you from a lot of constantly considering best practices. For example: How many equals and hashCode methods have you seen that are just plain wrong (with sometimes very subtle effects)? Case classes do that for free (and a lot more)! How many singletons have you seen that try to be, but are *not* thread safe?!? Scala objects are so right out of the box!

Things like this help a lot in small and big systems alike; they only scale "linearily", i.e. the amount of best practices you don't have to take into account any more by using Scala, stays constant per line of code.

What really does make a difference, though, is that Scala allows you to take best practices out of your business code and encapsulate them directly into your frameworks... and this is especially important for big systems. A seemingly trivial example is that you can move the best practice of using the fully qualified class name for a logger into a very simple trait:

trait Logging {
  import org.slf4j._
  val logger: Logger = LoggerFactory.getLogger(this.getClass)
}

This is not possible in Java, and it makes a real difference!

Some tools and frameworks will just take some time to grow as the community grows: Code formatters (more important to big systems that to small!), code analysis tools, refactoring and other IDE support, to name just a few. BTW: I don't think that the module system suggested by Martin in "the" book replaces the need for a real dependency injection framework: It doesn't take the different build/deploy/run stages into account properly; and it misses one important point: 'inversion of control', i.e. changing from pull to push. But it is an interesting starting point and time will show.

Maybe some things in the language and/or compiler deserve some more pushing for Scala to better scale up. For example type inference is just great for smaller projects and client code, but for frameworks I think it's a good practice to annotate at least the return type of a method -- that additional piece of directly available information can be of great help. Without it you sometimes have to look several method calls deep and/or look very closely. This best practice is adhered to in some Scala library classes (e.g. List), but not in all (e.g. Either). I think it would be a great help, if you could make the compiler issue a warning, if it infers the return type of a method for you. An IDE could even be configured to automatically add it on save.

Another example for language things to scale up better: Some types are not meant to be used in a certain position. For example a HashMap should not be used as the type of a variable, just for construction! At least in the Java world this is a best practice most programmers use.

[*]

To finally get to the point: There could be some annotations or traits or even structural type that helped make some design aspects of a library explicit. If you could make the compiler issue a warning or even an error when violated, then the design of a library would not erode as quickly as it often does and library evolution could become safer. [I don't like the names I use here, they are just the first thing that came to my mind and not well thought about, yet. Read them as placeholders only.] Here they are:

DontAssign: Like HashMap example above, this type is not meant for variables, values, or return types. Just use it for construction. The counterpart "DontInstantiate" is not required: Making a class abstract or defining it as a trait is explicit enough.

DontExtend: This class is meant to just be called by client code; avoid the much stronger coupling by implementing a trait or extending a class. This privilege is left to the library itself.

DontCall: This class or trait is a callback for the client code to extend and the library to call. Don't do that at home.

DontStore: You should not save instances of this class for a longer period of time, be it to a database, to xml, or serialization to a file. Libraries storing objects would have to somehow specify that they don't like such types.

DontSerialize: Stronger than the above... the library is holding references that would get lost (e.g. Hibernate). Libraries serializing objects would have to somehow specify that they don't like such types. There generally should be a way to decouple such objects.

Immutable: The class guarantees that it recursively has no variables and is purely functional. (I think this is a must!)

Mutable (as in C++): An exception to the above, that is: A variable is required for performance reasons (e.g. a cache), but the logical state of the class remains unchanged.

Key: Instances of this class can be used e.g. as a key in a HashMap, i.e. the class overloads == and hashCode.

More thinking would certainly turn up more, but what does the Scala community think about the general point I tried to make? Do you agree that it would relevantly improve the up-scaling of Scala?


Regards
Walter

 « Return to Thread: annotations or traits to better scale scala up