De senaste åren har Scala hypats hårt bland såväl utvecklare som inom media. Epitet som ”Javadödaren” och ”The next one” har stått som spön i backen. Personligen har jag svårt att se att något språk inom de närmaste åren kommer ta död på Javas tio miljoner utvecklare, de tre miljarder mobiltelefoner som idag kör Java eller de 1,4 miljarder nya ”Javakort” som ges ut varje år. Däremot tror jag säkert att Scala går en ljus framtid till mötes och inte minst att Java-communityn kommer att snegla en hel del åt Scala-hållet när ny funktionalitet ska läggas till i språket. Lambdas som förhoppningsvis dyker upp i Java 8 är ett exempel där jag är övertygad att bland annat Scalas framfart haft stor betydelse.
Under de senaste månaderna har jag till min frus stora förtret ägnat en hel del tid åt att fördjupa mig i Scala och det finns mycket i språket som är väldigt bra. Allt kommer garanterat inte att hitta hela vägen till Java men det skulle glädja mig om i alla fall något av nedanstående skulle komma med i framtida versioner.
Nullhantering
Problemet med ”nullpointers” är i princip ett icke-problem i Scala. Den stora anledningen till det är något som kallas Option, kort sammanfattat fungerar det som ett typsäkert null. Till option finns det två subklasser, ”Some” och ”None” och i de fall där Java returnerar null returnerar Scala objektet ”None” istället:
scala> map.get("existingKey")
res1: Option[String] = Some(existingValue)
scala> map.get("nonExistingKey")
res2: Option[String] = None
Traits
Precis som interfaces i Java så används traits till att specificera metoder och dess signaturer. Traits har dessutom den extra finessen att det går att definiera en defaultimplementation till en eller flera metoder:
trait Glass {
def isEmpty(x: Any): Boolean
def isNotEmpty(x: Any): Boolean = !isEmpty(x)
}
Kanske ett något krystat exempel men det som händer ovan är att när Glass implementeras så måste även isEmpty() implementeras. isNotEmpty() finns det däremot redan en konkret implementation av och den ärvs därmed direkt från Glass om utvecklaren inte väljer att göra en egen implementation. Detta skulle kunna vara ett steg tillbaka till det multipla arvets ”diamond problem” men Scala undviker det genom att specificera vilken implementation som ska väljas, nämligen från höger till vänster i definitionen.
I nedanstående exempel är det alltså C:s implementation som gäller i första hand, B:s i andra hand och A:s i sista hand:
class Concret extends A with B with C
Higher order functions
En ”Higher order function” är en funktion som kan ta en annan funktion som parameter eller vars resultat är en funktion. När det presenteras med denna torra beskrivning låter det inte särskilt sexigt men tillsammans med exempelvis Scalas möjlighet att använda funktionell programmering och rekursion går det att göra extremt kraftfulla och eleganta lösningar:
def test(f: Int => String, i: Int) = f(i)
Vad som händer ovan är vi har en funktion, test, som tar två parametrar, dels f som är en annan funktion och i som är en vanlig integer. Det test sedan gör är att ta värdet i och använda det som argument till funktionen f.
Pattern matching
Pattern matching skulle kunna beskrivas som en ”switch”-sats på anabola. Till skillnad från Java där det endast går att göra switch på primitiver, enums och några enstaka klasser går det i Scala att göra pattern matching på i princip allt. Plötsligt blir det då också väldigt användbart och allt som oftast betydligt mer läsbart än motsvarande kod med if/else-satser. Det skulle tveklöst gå att skriva en bloggpost bara om pattern matching men jag nöjer mig med ett litet kodexempel som förhoppningsvis talar sitt tydliga språk:
def patternDemo(inputToMatch: Any): Any = inputToMatch match {
case 0 => "zero"
case "one" => 1
case x: Int => "Integer"
case _ => "Anything but the above"
}
Functional programming
Att som gammal Javautvecklare grotta ned sig i Scala utan att stöta på patrull när det kommer till funktionell programmering är nog i det närmaste omöjligt. Jag nöjer mig med att konstatera att applicerat på rätt typ av problem kan funktionell programmering ge väldigt elegant kod där motsvarande lösningar i exempelvis Java, eller ”icke-funktionell” Scala för den delen, blir väldigt långdragna.
Finns det då inget dåligt med Scala?
Om man bortser från de naturliga nackdelarna med alla mindre spridda språk, som att det sannolikt är svårare att få tag i rätt kompetens, så kan jag se en stor nackdel med såväl Scala som andra språk där det ses som en uttalad styrka att producera mindre kod. I vissa fall är det tveklöst en fördel med färre rader kod men jag upplever också att läsbarheten många gånger sjunker med antalet rader. Följande kod, som jag helt fräckt snott från RosettaCode, är en romerska siffror-omvandlare:
def toRoman( v:Int ) : String = {
val romanNumerals = List(1000->"M",900->"CM",500->"D",400->"CD",100->"C",90->"XC",50->"L", 40->"XL",10->"X",9->"IX",5->"V",4->"IV",1->"I")
var n = v
romanNumerals.foldLeft(""){(s,t) => {val c = n/t._1; n = n-t._1*c; s + (t._2 * c) } }
}
Det tog rätt länge innan jag trodde mig förstå vad som händer. Sedan tog det ytterligare en stunds experimenterande till dess att jag faktiskt förstod på riktigt. Läser jag motsvarande lösning på samma sida för såväl Ada som Java och C# så är det inga bekymmer att greppa lösningen relativt omgående. Om romanNumerals.foldLeft-raden är elegant eller ett aber att underhålla beror nog på vem man frågar. Lättläst är den hur som helst inte för någon annan än möjligen Jonas Bonér.