About
fv_2007
Agile innovative developer with deep insight into lots of platforms, technologies and protocols. Absolute “early adopter” in Web 2.0 technologies and more. Large professional network and eagerly talking about architecture, strategy, design patterns, restful ressources, object-oriented thinking and modeling languages such as PML. Special interest in programminglanguages constructs, knowledge on languages like Smalltalk, Erlang, Java, Clojure, Scala, Ruby... read more
Comments
Language

Code Blocks and Closures September 27, 2010 18:11 about 1 year ago

Java7 fik ikke closures. So what? Lige meget hvor godt du gejler en gammel bil er det stadig en gammel bil. Den kan måske tunes med generics og nye indsprøjtnings systemer men det fundamentale i bilen er stadig gammelt.

Den væsenslige faktor for Java’s sucses er dens udbredning. Tusindvis af virksomheder stoler til daglige på at Java kan håndteret deres forretning. Og med god grund. Java var i sine velmagtsdage et kæmpe spring frem til automatisk hukommelsesstyring og simple syntaks. Java var for masserne og uden mange af de faldgrubber og avancerede sprog konstruktioner som elles fandes i andre sprog.

Da jeg var en del af Javahouse var mit fortrukne våben naturligvis Java. Faktisk mener jeg stadig at Java er et godt sprog, men jeg hader hader Java’s omgivende verden. Java er perfekt til middleware men stinker til applikationer.

Så hvad er det så med den Closure kommer til at mangle i Java7? En EA spurte: hvad betyder det for mine programmøre at closures ikke kommer med i Java7? Ikke andet end dine folk nu ikke skal bruge oceaner at tid på at finde ud af hvordan en closure virker. Det er nemlig utroligt nemt at konsturer et stykke kode der fungere som en closure men utroligt svært at forså hvorfor den virker.

Før man kan forstå en closure må man forstå en kode blok. Derfor har jeg lavet et par forsøg henholdsvis i Ruby og Groovy. Ruby bruger jeg fordi det er langt bedste programmeringssprog at lave eksempler med, det indeholder alle de klassiske egenskaber man kan ønske sig af et objektorienteret sprog, og så er det letlæst. Kun Lisp er mere blok-orienterede end Ruby men Groovy kan faktisk også være med.

Groovy er mit andet valgt som programmeringssprog på JVM’en fordi det stræber efter Ruby og fordi udviklinghastighed altid betyder noget for mig. Hvis Java skal have Closures bør de laves som i Groovy!

Aldrig et eksempel uden en opgave så her er en opgave fra min hverdag. En implementing af en forretningsregel. Data findes i en associativ liste af key values. Altså en hash med en nummerserie for hver nøgle.

# Ruby  / Groovy 
symbols ={“KR”=>[‘100085’, ‘2000084’, ‘3000076’, ‘50000034’], “AU”=>[‘660000039’] }
symbols =[“KR”: [‘100085’, ‘2000084’, "3000076", "50000034"], “AU”:["660000039"] ]

For Java folket kan jeg sige at [:] i groovy er det samme som at skabe en hashmap Java. Næste problem er at forstå en kode blok som er fundament i en closure. Måske kan man aner hvor Guillaume Laforge, Groovy project manager, har fået sin inspiration.

Kode blok

En kode blok er en samling af kode der er bundet sammen til en enhed. I Ruby er en kode blok et objekt, som indeholder kode samt den yder sammenhæng der er nødvendige for at udføre koden. En kode blok er som en metode uden navn. Andre sprog har fx en funktion pointer©, lambdas(Python) og anonyme inderklasser(Perl&Java).

Forretnings koden her udtrykker at elementer af en værdi der er PRE fixed med “50” er false og derfor ikke skal med i den retunerede kode.

# Ruby  / Groovy code block 
{|e| e !~ /^50/ }
{ n -> ! n.startsWith("50")}

I begge sprog er det { braces } der holder den grupperede kode sammen. I Ruby kan man også bruge do end hvis kodeblokken er over flere linjer. En kode blok i selv er ikket noget værd før den bindes til en variable eller omringes af et metode scope.

Metode

En metode er en kode blok der ikke er bundet til lokale variabler. En metode er bundet til en instans af det omgivende objekt og dens instans eller klasse variabler. Herunder bindes forretningskode blokken i en metode som vi kender dem flest med den lokale variable list fra metode signaturen.

def rule(list) 
 list.select {|e| e !~ /^50/ }
end

Metoden select behandler alle elementer i listen og tager som argument en kode blok.

Proc

Et Proc objekt er en kode block der er bundet til et sæt af lokale variabler og ikke et objekt. Når de er bundet kan de kaldes fra forskellig kontekst men beholder samme tilstand som da de blev skabt. Proc er first-class objekter, de kan skabes runtime, være en del af en data struktur og bruges som argument eller return værdi.

# Ruby Proc object
rule = Proc.new { |e| e !~ /^50/ }

Lambda

Et Lambda objekt er en kode blok der er bundet til lokale variabler og ikke til et objekt. En Lambda og en Proc er meget ens men der er betydningfulde forskelle.

  1. En Lambda udfører argument check.
  2. En Lambda returnere fra blokken til den ydre metode.
  3. En Proc returnere direkte tilbage til kalderen og ikke til den ydre meode.

Det siges at være god programmerings etik at holde sig til lambda da den har en mere inturitativ adfærd end et Proc objekt. Brug kun en Proc hvis opgaven kræver en løs adfærd.

# Ruby lambda function
rule = lambda {|e| e !~ /^50/ }

Closure

Closure’s har deres oprindelse fra funktionelle programmeringssprog men findes også i mange andre objekt orienterede sprog som fx Ruby og Groovy. Man kan lave smarte tricks med med og de tillader udskudt eksekvering.

Et Closure objekt er en kode blok der imødekommer tre krav:

  1. den kan blive bundet til en variable
  2. den kan eksekveres af alle der har adgang til variablen
  3. den har adgang til lokale variabler fra den kontekst hvori den blev skabt

I Ruby er en Closure et first-class objekt kan blive send rundt som en almindelig variable. Lad os prøve at lave en closure der kan beregne moms. Der findes 7 måder at skabe en closure i ruby.

faktor = 0.25 # 25%
def beregn_moms(m)
  lambda { |x| (x * m) + x } 
end
p = beregn_moms(faktor)

Metoden beregn_moms(faktor) fabrikkere en closure med den moms faktor som er i det lokale lexical scope og binder closure til variablen p.

moms = 0.30 # 30%
p.call(100) 
p.call(100) 

Nu sætter vi momsen op til 30% og kalder vores closure. Hvis du har læst ovenstående ved du hvad resultatet bliver?

Groovy Closure

I Groovy findes Proc og Lambda ikke og man kan kun skabe en closure ved at binde en kode block til en variable. Et closure objekt i groovy er et sematisk koncept, som en instans der kan defineres men konstrures. Man kan ikke new en closure. Måske er termologien ikke helt korrekt men døm selv.

Samme opgave. Fjern de elementer i listen med PRE fixed 50. Jeg laver en typeløs metode der er en closure i sig selv som retunere en clousre med en fast værdi af den omgivende meode. På den måde kan jeg konstruere en filter metode der tager en værdi, på den pre fix der skal fjernes, og derefter kun kalde den med en liste som argument. Denne closure kan bruges alle steder da den ikke er bundet til en klasse eller instans sålænge der er adgang til variablen filter().

def method = { remove ->
  return { list ->
    list.findAll { e -> ! e.startsWith(remove) }
  }
}
String remove = "50"
def filter = method(remove)

println filter(["14780241", "50111053", "20021469"]) // [14780241, 20021469]
remove = "20"
println filter(["14780241", "50111053", "20021469"]) // [14780241, 20021469]

Den reagere lidt andreledes end en closure i ruby. (læs: moms) Alle variabler fra den omgivende verden er synlige. En groovy closure eksekveres først når den eksplicit kaldes og ikke når den skabes. (Proc/Lambda)

Groovy ser på en closure som en annonym kode blok der ikke kan bindes direkte til en variabel men den kan bindes til den omgivende metode.

En closure kan bruges til at associere en funktionalitet med et sæt af “private” variabler der følger over mange kald af funktionen mens den private variable ikke kan tilgå af den omgivende programkode.

Derfor er clousres velejenet til kontrol strukture og stateful repræsentation hvor information hiding er et issue. Closures er forbundet med late invokation, de skabes først for at eksekveres senere hvilken gør dem meget forskellige fra metoder.

Hvis du absolut vil bruge en closure, brug et andet sprog end Java.

Quick start & style guide for #java developers learning to use #groovy
Closure in computer science
Blocks and Closures in Ruby
Closure design in dynamic languages

Update: En kollega har sendt denne

Der er garanteret en mere idiomatisk måde at gøre det på, men det illustrere vist det samme som dit groovy eksempel.
Opretter en closure der lukker over værdien af r på det tidspunkt hvor closuren bliver lavet.
Altså på dette tidspunkt er remove “50” denne værdi bliver bundet til r.
Dette illustrerer at værdien af remove ikke paavirker vaerdien af r i vores closure.

(def nums ["14780241", "50111053", "20021469"])
(def my-filter (fn [r] (partial filter #(not (.startsWith % r)))))
(def remove "50")
(def without-50s (my-filter remove))
(without-50s nums) 

("14780241" "20021469")
(let [remove "20"] 
  (do
    (println remove)
    (println (without-50s nums))
    (println remove)))

Det er repl’ens skyld at det bliver skrevet ud lidt anderledes. Det er stadig strenge i en “liste”.

Nice, Clojure er det næste punkt på listen jeg skal ha kigget igennem!


By Frank Vilhelmsen - 4 tags: java ruby groovy closure - Add comment