[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 ;)

Plötsligt händer det!

2014-11-09

Kommer ni ihåg den där gamla reklamfilmen när en person står i en lång kö till kassan i en mataffär då plötsligt en ny kassa som personen blir först i kön till öppnas? "Plötsligt händer det!". Jag kommer inte ihåg vad filmen gör reklam för, men igår inträffade i alla fall en sådan "plötsligt händer det"-händelse.

Jag satt och kodade på matteappen fram på kvällskvisten. Höll på med en egen grafisk View i vilken användaren skall kunna ange ett klockslag genom att ställa visarna på en analog klocka. Jag hade kommit så långt att jag hade ritat ut en urtavla med siffror och minutmarkeringar samt den långa visaren. Den korta visaren tänkte jag vänta ett slag med. Det jag jobbade med för tillfället var funktionalitet för att kunna flytta den långa visaren runt urtavlan genom att dra fingret över surfplattan. Det som gjorde det hela icke-trivialt (relativt sett....) var att eftersom den långa visaren alltid skall gå från mitten av urtavlan till dess perferi men fingret som styr den i de flesta fall inte kommer att befinna sig på urtavlans rand måste jag projecera fingrets position på rätt ställe på randen. Inte superkomplicerat så klart, men det kräver lite trixande med trigonometri. Sen måste man hålla koll på i vilken kvadrant fingret befinner sig också för att veta hur man skall justera koordinaterna på visarens slutpunkt.

Vad var då det där "plötsligt händer det"-ögonblicket nu då? Jo, det var att det funkade precis som jag hade tänkt mig med en gång! Inte en enda omkompilering pga att det var något jag missat eller något fall jag glömt eller så. Det funkade direkt. Helt otroligt. Det händer ju i princip aldrig. Inte mig i alla fall.

Blev så uppspelt att jag gick bort till frugan som satt och såg på TV i soffan och visade upp den fina nya funktionaliteten. Kommentaren blev ett förstrött "Vad bra.", med undertonen "gå nu så kan jag se klart på programmet". Vissa förstår inte hur ovanligt det är att något i mjukvaruvärlden funkar på en gång ;)

Varför är det på detta viset då? Att det är så få gånger som man skriver något funkar med en gång. En förklaring kan vara att det vi sysslar med som programmerare är så komplext att det är svårt att få det rätt på första försöket. En annan, mindre välvillig, förklaring kan vara att tiden det tar att genomgå en write-compile-(deploy)-run-test-cyklel ofta är så kort så att det inte är lönt mödan att tänka igenom sitt program ordentligt. Det är lättare att bara köra på och låta testerna avgöra om programmet är korrekt eller inte. Antagligen är det väl en kombination av de två. Egot vill dock gärna propagera för den första förklaringen ;)

Skönt var det i alla fall att ha ett "plötsligt händer det"-ögonblick. Får väl se när det händer nästa gång. Misstänker att det lär dröja...