Geeks With Blogs

Christian Maslen

 

Technorati Tags: ,

Introduction

This post gives an overview of Scala from a C# developer’s perspective in light of efforts for a current .Net port coming closer to fruition. By closer I mean they have the compiler done but not the Visual Studio or SharpDevelop plug-in. From what I could find there is also no CLR based REPL available. You are also unable to call CLR code with generic signatures. While all of these issues are being worked on, to have a play I suggest using the JVM implementation and either IntelliJ or Eclipse as the IDE – both of which provide free options. Consider this post a peek at what will (hopefully) be available soon for the CLR.

Why Scala?

On the JVM Scala has a very strong case indeed with Java language level innovation moving at a glacial speed. Things such as Lamda expressions, LINQ, automatic properties, extension methods, the ability to overload an operator are mere wish list items for disgruntled Java developers. With Scala however there is an equivalent to all of those features and more.

On the CLR the case is not so clear cut given its current state of interoperability and F# filling the object / functional hybrid language gap very nicely. The following are some reasons to consider Scala on the CLR once it is a first class .Net language:

  • If you want to write a cross platform application.
  • If you want to provide a DSL like API. Scala’s very flexible syntax allows you to create DSLs in much the same way as you can in Ruby. The difference is that Scala is statically typed.
  • If you would like to take advantage of some of Scala’s advanced APIs available in the Scala library. For example the Actors API.
  • The Scala open source community is very active so ports of popular Scala projects may become viable.

Getting Started

As at the time of writing being productive in Scala still means using the JVM version and using the workaround documented in the Preview of Scala.Net document to get code completion for CLR types in the Java IDE. So grab yourself the current version of the Java JDK, then the Scala Distribution and a Java based IDE. The sample I’ll work through below was put together using IntelliJ and then compiled to the CLR from the command line.

As with many functional languages, Scala offers a REPL from the console where you can experiment.

C:\JavaApps\scala-2.9.0.1\bin>scala
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val numList = List(1,2,3,4,5,6,7,8,9)
numList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> val evensList = numList.filter( x => x % 2 == 0 )
evensList: List[Int] = List(2, 4, 6, 8)

scala> val conciseEvens = numList.filter( _ % 2 == 0 )
conciseEvens: List[Int] = List(2, 4, 6, 8)

scala> val someXML = <outterNode>some content
     | <innerNode attr="value"/>
     | </outterNode>
someXML: scala.xml.Elem =
<outterNode>some content
<innerNode attr="value"></innerNode>
</outterNode>

scala>

In the REPL session above we first create a list of Int object – the numList value. Lists in Scala are linked lists similar to what you might work with in F#. Note how we didn’t have to use the new keyword or specify the generic type directly. Once you type in your expression the REPL responds with its type and value. Generics in Scala use the square bracket instead of the angle bracket. Angle brackets are used for XML literals. The pipe symbols are put there by the REPL when it detects you’ve typed an incomplete expression and a new line. The val keyword is used to define an immutable value. The lists above are all immutable including their content. To define a variable use the var keyword.

The lamda expression used for the evensList value is basically the same as you’d see in C#. A short hand version is also available in Scala that uses the underscore character without the parameters and the => symbol as shown with conciseEvens. The underscore can only be used if a lamda parameter appears once in the expression body. So the mental substitution to make is to add a parameter per underscore character you see in any Scala lamda body.

Language Overview

Scala is a statically typed general purpose language that originated on the JVM. It has had a CLR version for a while but has only recently aimed at supporting the current version. It was designed from the outset to be a scalable language (hence the name Scala), providing the facility to write anything from small scripts to large enterprise systems. The language is conceptually a blend of object-oriented and functional programming allowing the developer to make use of the most appropriate programming style for the problem at hand.

Syntactically it shows its Java heritage with a lot of C style syntax, but it also borrows from Pascal with its variable declarations within methods and functions and from Haskell for many of its functional idioms. Many of the verbose aspects of Java/C# are legal in Scala but are optional and not idiomatic. For example:

   1: def factorial( x: BigInt ): BigInt = {
   2:     if ( x == 0 )
   3:         return 1;
   4:  
   5:     else
   6:         return x * factorial( x - 1);
   7: }

 

can be more idiomatically expressed as

 

   1: def factorial( x: Int ) = 1 to x reduceLeft( _ * _ )

 

As you can see much of the ceremony of C# and Java such as the curly brackets, semicolons and the return statement are unneeded. There’s also more to the above than first appears. Or really it’s less when you consider there are very few language level tokens. The to operator appears to be a key word when in fact its a method (the . can be replaced with a space and the parenthesis are optional for single parameter methods) on the RichInt class that returns an Inclusive instance which is a sequence. This in turn offers the reduceLeft method that takes a function parameter to reduce the sequence 2 elements at a time from the left. Recall that the underscore character is a place marker so above we have 2 parameters that are multiplied by each other giving us our factorial result. The number 1 was converted to a RichInt using an implicit method. Implicit methods are similar to C# extension methods. It’s this kind of language level flexibility that allows Scala APIs to provide a DSL like syntax without the requirement to create a parser.

Traits

One of the most interesting features I found in Scala is traits. I mention them in detail because they feature heavily in the sample code below. Traits are Scala’s fundamental unit of code reuse. A class can mix in any number of traits to implement a contract in the case where a trait defines abstract methods. This is the equivalent of an interface. Or a class can make use of the extra functionality provided by a trait in the case where a trait defines concrete methods.

Traits differ from C# interfaces by encapsulating method and field definitions rather than just methods. The method definitions can either be abstract or contain full implementations. Functionally they are the same as classes with the following differences:

  • Traits cannot have any class parameters. Scala class parameters are the equivalent of constructors with arguments in C#. A specialised syntax exists that allows you to pass a value to an abstract field.
  • In traits, base calls (using the super keyword) are dynamically bound where as with classes they are statically bound. That is if you write super.someMethod() in a trait the exact method that is called is not necessarily known until runtime. This odd behaviour is what allows traits to work as stackable modifications.

The Sample

The sample I’ll work through models a simplistic automated underwriting mechanism from the domain of commercial insurance.

As part of a policy’s lifecycle the details of the policy are evaluated against certain limits or attributes. If the limit is under a certain amount no intervention is needed and, for that part at least, the policy is rated against the base premium. Over a certain amount the policy requires manual attention from an underwriter and is said to require a referral. Over a higher limit and the policy is declined outright again with no manual intervention. The types of things that get insured and therefore the attributes that are the subject of underwriting vary greatly but there are cases where policies of different products are similar.

The example I’ll use is from cargo insurance with the variants of single cargo and annual cargo. We will use traits to model the individual referral rules and inheritance to model the policy types themselves.

We will start with a simplified domain model. I’ve shown all model classes for brevity. As with C# you can place all of these in a single file if you wish.

   1: package com.maslen.scala.policy
   2:  
   3:  
   4: class Policy {
   5:     var referrals = List[ Referral ]()
   6:     var lossHistory = List[ LossHistory ]()
   7: }
   8:  
   9: class CargoPolicy extends Policy {
  10:     var storagePeriod: String = ""
  11: }
  12:  
  13:  
  14: case class LossHistory(lossYear: Int, lossAmount: BigDecimal)
  15:  
  16: case class Referral (code: String, description: String)

 

Class definitions are similar to C# with a few exceptions:

  • Fields are public by default and are always exposed as methods under the covers. Scala doesn’t require the parentheses when accessing a zero parameter method, thus giving the effect of C# properties. So you can later redefine your field as a zero parameter method and no calling code will be broken. If you need to redefine the setter you name the method with a _= suffix. eg def referrals_= ( referrals: List[ Referral ] ).
  • If a class requires no body you don’t need the curly braces.
  • Constructors are represented as parameters to the class as shown in the LossHistory and Referral classes.
  • the case keyword is used to create case classes. In short they allow pattern matching on a type. I’ve used them here because they provide automatic implementations of the toString and equals methods making printing for the example easier. You also don’t need the new keyword to instantiate an object from a case class. This can make your code appear more like a DSL.

Now to the referral implementation. The first of these classes is an abstract class that defines the interface for automated referral for all policies:

   1: package com.maslen.scala.policy.underwriting
   2:  
   3: import com.maslen.scala.policy.{Referral, Policy}
   4:  
   5: abstract class AutoReferral {
   6:   type P <: Policy
   7:   var referrals: List[ Referral ] = List()
   8:  
   9:   def addReferral(referral: Referral) {
  10:     referrals = referral :: referrals
  11:   }
  12:  
  13:   def applyReferral(Policy: P)
  14: }

 

An abstract class definition looks and works similarly to a C# abstract class except abstract methods and fields do not use the abstract key word. Abstract methods simply have no implementation and fields are abstract if they are unassigned. The rest of the class is explained below:

  • Imports in Scala offer a more fine grained mechanism than the C# using declarations. So above we import the Referral and Policy classes from the policy package.
  • Scala allows you to define alias's for a type using the type keyword. Here we define the P alias for any subclass of the Policy class (the use of the <: symbol. This same symbol is also used to indicate restrictions for generic types). Classes that extend AutoReferral can define the type of P providing the ability to override the applyReferral method but at the same time use a concrete parameter type for policy. This then gives us the ability to access properties of the sub type without down casting.
  • Lists in Scala are immutable by default. For a persistable class we're more likely to use a mutable List, but for the example I've chosen to use the immutable List. So to add an element you need to reassign to the original list as you would with say a C# String. The "::" you see that appears to be an operator (pronounced "cons") is in fact a method on the List class. Scala treats methods that end in the ":" character differently in that they are right associative. So the :: method is on the List class not the Referral class. Ultimately the line referrals = referral :: referrals is pre-pending a Referral object to the referrals List. The item is prepended because a Scala list is internally represented as a linked list. Prepending a linked list is a zero cost operation where as appending gets slower as the list grows.
   1: package com.maslen.scala.policy.underwriting
   2:  
   3: import com.maslen.scala.policy.CargoPolicy
   4:  
   5:  
   6: abstract class CargoAutoReferral extends AutoReferral {
   7:     type P = CargoPolicy
   8:  
   9:     override def applyReferral(policy: CargoPolicy) {
  10:         policy.referrals = policy.referrals ::: referrals
  11:     }
  12: }

 

The CargoAutoReferral class extends the AutoReferral Class providing the following:

  • A concrete assignment of the P type alias to CargoPolicy.
  • A concrete implementation of the applyReferral method. This method concatenates the two lists together (the ::: method).

Now we've defined our Cargo Policy specific automated referral class we can create traits that operate on CaroPolicy types and conditionally add referrals. The first of these is the ExceededLossReferral.

   1: package com.maslen.scala.policy.underwriting
   2:  
   3: import com.maslen.scala.policy.{Referral, CargoPolicy}
   4:  
   5: trait ExceededLossReferral extends CargoAutoReferral {
   6:     val exceededLoss: BigDecimal
   7:  
   8:     override def applyReferral(policy: CargoPolicy) {
   9:         val totalLoss = policy.lossHistory
  10:             .foldLeft(BigDecimal(0))(_ + _.lossAmount)
  11:  
  12:         if ( totalLoss > exceededLoss )
  13:             addReferral(new Referral("R001", "Total loss limit exceeded"))
  14:  
  15:         super.applyReferral(policy)
  16:     }
  17: }

 

The lossHistory property of policy is of type List[ LossHistory ]. The List class offers a number of handy methods that have functions as arguments. Simple methods such as filter take a function that takes an argument of T (same type argument passed to the List) and return a Boolean. The more complex fold functions (foldLeft and foldRight) are curried where the first parameter is a parameterised argument (lets call this type S for scalar type), and the second parameter is an argument of type (T, S) => S where type T is the type of the elements in the collection. The folding process takes the scalar value returned by the first function and applies the function in the second parameter set to that value and the first element of the list (in the case of foldLeft or the last for foldRight). The same process is recursively (note the Scala compiler has its own hand rolled tail call optimisation) applied to the rest of the list until there is no more list. So the above shorthand usage can be rewritten as shown below:

   1: val totalLoss = policy.lossHistory
   2:     .foldLeft( seed: BigDecimal )
   3:     ( ( accum: BigDecimal, losses: List[ LossHistory ] ) => accum + losses.amount )


The code can be compressed even more by using the obtuse looking "/:" method. I verbalise this by noting the resemblance of the colon to a fold and the direction of the slash slanting to the left. Note how the argument order is reversed because of the reverse ordering the compiler applies to methods ending in the ":" character. As you'd predict :\ or fold right uses the same ordering as the foldRight method so the order of the method args also matches the direction from which the folding process occurs. I leave it to the reader to decide if this is cool or confusing.

 

   1: val totalLoss = ( BigDecimal( 0 ) /: policy.lossHistory )( _ + _.amount )

 

The last line of the method is a call to super.applyReferral. Recall that calls to super are dynamically bound in traits. In the usage example below we'll see how an object is instantiated with a couple of traits. At that point we can now evaluate what the calls to super.applyReferral will do.

The next trait is InvalidStoragePeriodReferral

   1: package com.maslen.scala.policy.underwriting
   2:  
   3: import com.maslen.scala.policy.{Referral, CargoPolicy}
   4:  
   5: trait InvalidStoragePeriodReferral extends CargoAutoReferral {
   6:     val referrableStorageCode: String
   7:  
   8:     override def applyReferral(policy: CargoPolicy) {
   9:         if ( referrableStorageCode == policy.storagePeriod )
  10:             addReferral(new Referral("R002", "Invalid storage period"))
  11:  
  12:         super.applyReferral(policy)
  13:     }
  14: }

 

The only thing of note above is that Scala defines the == method to mean value equality in all cases. To get reference equality you use the eq method.

An example usage scenario is shown below:

   1: package com.maslen.scala.demo
   2:  
   3: import com.maslen.scala.policy.underwriting.{ExceededLossReferral,
   4: InvalidStoragePeriodReferral, CargoAutoReferral}
   5: import com.maslen.scala.policy.{LossHistory, CargoPolicy}
   6:  
   7: object SampleReferralUsage {
   8:  
   9:     def main(args: Array[ String ]) {
  10:         //
  11:         // Example 1. Scenario where single cargo polices include the
  12:         // InvalidStorage Period and ExceededLoss referral rules.
  13:         // Here we set up the product specific referral rules
  14:         //
  15:         val singleCargoAutoReferral = new CargoAutoReferral
  16:             with InvalidStoragePeriodReferral
  17:             with ExceededLossReferral {
  18:                 val exceededLoss = BigDecimal(10000)
  19:                 val referrableStorageCode = "1 year"
  20:             }
  21:  
  22:         // Setup the policy
  23:         val singleCargoPolicy = new CargoPolicy()
  24:         singleCargoPolicy.storagePeriod = "1 year"
  25:         singleCargoPolicy.lossHistory = LossHistory(2003, 15000) ::
  26:             singleCargoPolicy.lossHistory
  27:  
  28:         // Underwrite the policy
  29:         singleCargoAutoReferral.applyReferral(singleCargoPolicy)
  30:         println("Single Cargo Referrals")
  31:         println(singleCargoPolicy.referrals)
  32:  
  33:         //
  34:         // Example 2. Scenario where annual cargo polices only include the
  35:         // ExceededLoss referral rule
  36:         //
  37:         val annualCargoAutoReferral = new CargoAutoReferral
  38:             with ExceededLossReferral {
  39:             val exceededLoss = BigDecimal(10000)
  40:         }
  41:  
  42:         // Setup the policy
  43:         val annualCargoPolicy = new CargoPolicy()
  44:         annualCargoPolicy.lossHistory = LossHistory(2003, 15000) ::
  45:             annualCargoPolicy.lossHistory
  46:  
  47:         // Underwrite the policy
  48:         annualCargoAutoReferral.applyReferral(annualCargoPolicy)
  49:         println("Annual Cargo Referrals")
  50:         println(annualCargoPolicy.referrals)
  51:     }
  52: }

 

The example above shows a scenario where Cargo Policies are largely similar across single and annual variants.

Example one creates an anonymous object singleCargoAutoReferral value of type CargoAutoReferral with the InvalidStoragePeriodReferral and ExceededLossReferral traits mixed in. Recall the traits had abstract fields defined (not null fields) and so we initialise them here in the object declaration providing an excedededLoss limit of 10,000 and a referralStorageCode of "1 year".

We then create a test policy called singleCargoPolicy in a state that will trigger both referral rules.

Example two is similar except that it mixes in a single trait. The annualCargoPolicy is setup to trigger the one referral rule. The output is shown below

Single Cargo Referrals
List(Referral(R002,Invalid storage period), Referral(R001,Total loss limit exceeded))
Annual Cargo Referrals
List(Referral(R001,Total loss limit exceeded))

Running the demo on the CLR

The instructions for getting a CLR run-able version are here. The sample code only used Scala APIs so there is not requirement to alter any source code. I’ll be the first to admit this is not a straight forward process but here is how I got the code to .Net.

First you need to download the Scala.Net project. At present this means checking out the subversion source tree. I did this from a folder I created locally at c:\svncode\Scala.Net

svn co http://lampsvn.epfl.ch/svn-repos/scala/scala-experimental/trunk/bootstrap

Then you open a VS.Net command window and add the bin folder to the scala.Net compiler to the path variable:

set path=%path%;c:\svncode\scala.net\bin

Then you issue the rather long scala compiler command on a single line. I’ve broken the command here so you can read it. I’d like to think there was a way to specify a source folder and get the compiler to recursively look but I couldn’t find it.

scalacompiler -target:msil -d c:\svncode\ScalaBlogCode\clr-bin 
-Xassem-name SampleReferralUsage 
-Xshow-class com.maslen.scala.demo.SampleReferralUsage 
-Xassem-extdirs C:\svncode\scala.net\bin 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\demo\SampleReferralUsage.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\CargoPolicy.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\LossHistory.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\Policy.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\Referral.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\underwriting\AutoReferral.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\underwriting\CargoAutoReferral.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\underwriting\ExceededLossReferral.scala 
C:\svncode\ScalaBlogCode\src\com\maslen\scala\policy\underwriting\InvalidStoragePeriodReferral.scala

This will generate a text file with the msil code in it. From here you use the .Net supplied msil command from the target clr-bin folder specified above or you can fully path to it.

ilasm /exe SampleReferralUsage.msil

The last step is to copy all of the assemblies from the scala.Net bin folder to your clr-bin folder. Then you are up and running.

Conclusion

This post walked you through a few features of the Scala language and what it takes to get the code to run on the CLR. Having done a 2 year stint recently on Java, I can say Scala was a breath of fresh air compared to the very dated Java language that exists today. Unfortunately even on the JVM – in Australia at least – very few Scala jobs are out there.

If you work with both the JVM and CLR, Scala may be just what you need to support both platforms. If the advanced language features look interesting and you only code on the CLR, this language is worth keeping an eye on.

References

Posted on Sunday, July 31, 2011 5:23 PM | Back to top


Comments on this post: Scala on .Net

Comments are closed.
Comments have been closed on this topic.
Copyright © ChristianMaslen | Powered by: GeeksWithBlogs.net