[x^2 for x in lst]

Fula closures i Groovy

2014-11-06

Jag har börjat försöka lära mig lite mer om Groovy. Eftersom Groovy används i vissa delar av byggmiljön på jobbet så har jag varit inne och nosat lite på språket då och då, men eftersom det bara rört sig om punktinsatser så har jag inte lärt mig språket "på riktigt". Nu tänkte jag ändra på det och lära mig det lite grundligare. Vet inte riktigt varför egentligen. Tycker att det kan vara kul att kunna ett annat språk som är utvecklat för JVM:en. Det kan också ha att göra med att jag spontanköpte Programming Groovy 2 i våras och har sedan dess haft dåligt samvete för att jag inte läst den. Nu håller jag i alla fall på och läsa den och sitter och kodar ihop lite övningsprogram på bussresorna fram och tillbaks från jobbet.

Igår började jag kolla på closures i Groovy. Efter att ha spenderat en stor del av sommaren med att gräva ner mig i lambda-uttryck i Java 8 så ter sig closures i Groovy inte så obekanta. Idén är givetvis den samma, men syntaxen skiljer sig lite åt.

Hur ser då ett closure i Groovy ut? Det är helt enkelt en kodsnutt omsluten av måsvingar. Ex: {<insertSomeGroovyCodeHere>}.

En reflektion är att i Groovy, eller i alla fall i Programming Groovy 2, så verkar även kodsnuttar som inte refererar till något i det omgivande scopet kallas för closures. Inte helt renlärigt kanske, men sak samma. Man fattar ju vad som avses. Som en parantes kan nämnas att i Java 8 kallas bara de lambdauttryck som har åtkomst till ett värde från det omgivande scopet där de skapades för closures.

Vad är det då jag tycker är fult med closures i Groovy? Egentligen inte något alls med själva closure:t (fin böjning där va?). Det är mer hur man kan skicka med ett closure till en metod som jag tycker är fult.

Ett av sätten att skicka med ett closure till en metod är helt enkelt att skicka med det på samma sätt som en vanlig parameter. Om vi till exempel har en metod som tar emot ett closure som en parameter enligt följande:

def metodTakingClosureAsParam(closure) {
  // Just invoke the closure.
  closure()
}

Anropar vi metoden med ett closure på samma plats som en vanlig parameter skulle skickas med kan det se ut som följer:

// Call the method supplying a closure in the same place as a regular parameter would go.
metodTakingClosureAsParam({println "Inside the closure."})
...
Inside the closure.

Det andra, fula, sättet att skicka med ett closure till en metod är att ange det efter argumentslistans slutpararantes. Ovanstående exempel skulle då se ut så här:

metodTakingClosureAsParam() {println "Inside another closure."}
...
Inside another closure.

Varför tycker jag så det är fult? För att jag tycker att det är för likt en funktionsdefinition. Titta på följade kodsnutt:

def aMethod() {println "Hopp!" }
aMethod() {println "Hopp!"}
Den första raden, def aMethod() {println "Hopp!" }, definierar en funktion aMethod() medan den andra raden, aMethod() {println "Hopp!"}, anropar en funktion aMethod() med ett closure som argument. Dessa rader gör helt olika saker, men de ser väldigt lika ut.

När jag läser kod som den ovan måste jag stanna upp och tänka efter vilket av ovastående fall det handlar: är det en funktionsdefinition eller ett closure. Jag vet att man säkert vänjer sig, men jag vill inte behöva vänja mig. Jag vill att det skall framgå klart och tydligt vad en kodsnutt gör. Det är ofta tillräckligt krångligt att lista ut vad snutten för funktionsmässigt och att då behöva fundera på vilken typ av språkkonstruktion som används tillför bara onödig komplexitet. För att citera The Zen of Python: There should be should be one -- and preferably only one -- obvious way to do it och jag hade föredragit om det bara hade gått att anropa closures i Groovy på det sätt som inte är så lätt att förväxla med en funktionsdefinition.

När jag ändå är igång och klagar så kan jag väl spy lite galla över att man i Groovy inte behöver deklarera en explicit parameter till ett closure om closuret i fråga endast har ett argument. Om så är fallet kan man nämligen referera till parametern med hjälp av det magiska variabelnamnet it. Exempel:

def bClosure = {println it}
Detta är alltså en definition av ett closure som tar en parameter (av någon okänd typ) och skriver ut denna på kommandoraden. Notera av parametern it inte är deklarerad någonstans. Den bara magiskt finns där. Man kan testa closure:t t.ex. genom följande kodsnutt:
bClosure("This is the value of the parameter to the closure.")
...
This is the value of the parameter to the closure.

Varför tycker jag illa om it då. För att den är "hemlig". Man kan inte direkt genom att läsa koden lista ut var den är för något. Man måste veta att det finns en magisk variabel som heter it som man kan referera till inne i ett en-parameters closure. Den är implicit och, för att citera The Zen of Python igen: "Explicit is better than implicit".

För övrigt tycker jag att closures i Groovy är ganska bra ;)