Java får closures med havelåger

#int(int) doubler = #(int x)(x + x); Sådan kommer closures i Java til at se ud i den næste ombæring.

Det bliver den såkaldte 'strawman"-syntaks, der skal flette closures ind i den kommende udgave af Java.

Closures findes i mange sprog og er en måde at benytte en lille blok af kode som parameter. Muligheden findes i mange sprog, som f.eks. Ruby og C#.

Debatten om hvorvidt Java har brug for closures, og hvordan det skal implementeres, har raset i årevis. Det er tilsyneladende et kommende parallelisme-bibliotek, som har gjort udslaget.

Når collection-klasser skal behandles parallelt, sker det ved at definere en operation, som kan udføres på collection'ens elementer uafhængigt af rækkefølge. Dermed kan operationen udføres på flere processorkerner samtidigt.

Uden closures ville parallelisme-biblioteket kræve 80 nye interfaces i JDK-bibliotekerne.

Udvikler Ole Friis Østergaard fra Trifork ser closures som en god tilføjelse til sproget:

»Jeg synes, det er rigtigt godt, for det gør, at det dagligdags arbejde med Java bliver mere smertefrit. I Ruby har man også closures. Det gør, at éns programmer føles anderledes. For eksempel er det nemmere, hvis man har en liste, hvor man vil hive et enkelt element ud, så skal man bruge meget lidt kode, hvis man har closures,« har han tidligere fortalt Version2.

En række offentliggjorte regressionstest demonstrerer anvendelsen. Her defineres en funktion, der returnerer et heltal plus én, med argumentet 3:

int i3 = #(int x)( x + 1 ).(3);

Havelågetegnet kendes fra Javadoc-dokumentation, hvor det også benyttes til at repræsentere en metode. Det samme kan også skrives med en eksplicit krop, afgrænset af tuborg-parenteser:

int i3 = #(int x){ return x + 1; }.(3);

Når en operation skal udføres på elementerne i en collection, kan det for eksempel se sådan ud:

c.map(#(int x)(x + 3));

Her adderes tallet tre med alle elementer i en heltals-collection.

Der er allerede mange stemmer, som kritiserer syntaksen.

Den danske Rails-opfinder, David Heinemeier Hansson, skriver på Twitter, at der er alt for meget støj i den nye syntaks, og det synspunkt bakkes op mange steder. Andre indvender, at der blot er tale om akademiske eksempler, men at syntaksen ser ganske tilforladelig ud til hverdagsbrug.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Kommentarer (45)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Torben Mogensen Blogger

Det er på tide, at Java får closure. Så er syntaksen mindre vigtig. Jeg synes ikke, at de viste eksempler ser håbløse ud, men hvis der ikke er en fornuftig notation for polymorfe (generiske) funktionstyper, så kan erklæringer af højereordens metoder/funktioner blive ret stygge at se på (og skrive).

Closures fandtes i øvrigt helt tilbage i Algol 60, hvor de hed "thunks". Thunks svarer nogenlunde til parameterløse funktioner, men da man sammen med en thunk kunne overføre referencer til variable, der indgår i thunkens kode (med det såkaldte "Jensens device", se http://en.wikipedia.org/wiki/Jensen%27s_Device), kunne man nemt implementere funktioner med parametre. Notationen var dog ret styg.

  • 0
  • 0
Jesper Louis Andersen

Konceptet er fra 1960'erne, og er først udødeliggjort i Scheme formentlig. Det betyder en inkubationstid på en 40-50 år. Ikke dårligt!

Stort set alle andre sprog, der er værd at tale om, har closures i dag. Selv de gamle Unix-geeks valgte at smide closures i deres Go-sprog.

Syntaxen er jo bare syntax. Det væsentlige er at det ikke fylder for meget, for det dræber notationens anvendelighed. Anonyme klasser er nuttede, men de er ikke korte i notation.

  • 0
  • 0
Casper Bang

Jeg synes det er skruen uden ende, hvordan Java bliver ved med at få hovsa-løsninger fordi man ikke vil gå på kompromis med bagudkompatibilitet (erasure generics anyone?).

Problemet med syntaksen er relevant synes jeg, folk har i forvejen svært ved at læse samt forstå wildcards, upper/lower bounds og co/contravariance ved generics i Java. Nu skal vi så også til at rode 3x throws keywords ind i signaturen. F.eks.:

public <T, throws E> forEach(Block<T, throws E> block) throws E;

For 12 år siden, før han startede på C#, tilføjede Anders Hejlsberg delegates til Java, hvortil Sun lagde sag an mod Microsoft og skrev artikler som denne:
http://java.sun.com/docs/white/delegates.html

Så Oracle/Sun, nu Java endelig får delegates synes i så ikke det ser lidt dumt ud hvad i skrev og gjorde dengang? Nogen vil mene der ikke er noget at sige til at Sun ikke kunne holde sig kørende, de holdt desværre op med at sætte standarden.

  • 0
  • 0
Niels Dybdahl

Er det ikke bare en kortere syntax for hvad man allerede kan i Java?
F.eks de nævnte eksempler:

int i3 = #(int x)( x + 1 ).(3);
c.map(#(int x)(x + 3));

kunne skrives i dag:

interface ci { int cc(int x); }
int i3=new ci() { int cc(int x) { return x+1 }}.cc(3);
c.map(new ci() { int cc(int x) { return x+3 }});

De nye closures er en kortere notation, men funktionelt er der ikke noget nyt. Jeg har dog ikke studeret de nye closures i detaljer, så det kan da være at de kan mere end eksemplerne i artiklen viser.

  • 0
  • 0
Niels Dybdahl

Så Oracle/Sun, nu Java endelig får delegates synes i så ikke det ser lidt dumt ud hvad i skrev og gjorde dengang?

Hvis du læser den artikel som du selv henviser til, så går en del af Suns argumentation at deres implementering med unnamed classes holder koden der hvor den hører hjemme, mens MSs implementation kræver at den bliver lagt et andet sted hen og ovenikøbet skal have et navn.
De nye closures ligger meget tættere op af Suns unnamed classes end det ligger af MSs delegates, så jeg tror ikke at Sun synes at det ser dumt ud hvad de skrev.

  • 0
  • 0
Casper Bang

MSs implementation kræver at den bliver lagt et andet sted hen og ovenikøbet skal have et navn.

Det var kun sådan i den meget tidlige C# 1.0, C# 2.0 introducerede anonymous methods imens C# 3.0 fik understøttelse for fulde lambda udtryk.

For at forstå problemet med Java's krav til interface dispatch og en halvhjertet generics implementation, behøver man blot at have brug for f.eks. at lade en type implementere Comparable<? extends Enum> og Comparable<Integer> for at rende ind i problemer.

  • 0
  • 0
Niels Dybdahl

For at forstå problemet med Java's krav til interface dispatch og en halvhjertet generics implementation, behøver man blot at have brug for f.eks. at lade en type implementere Comparable<? extends Enum> og Comparable<Integer> for at rende ind i problemer.

Er det kun mig der får det dårligt ved at prøve at forestille mig hvad programmøren har tænkt da prøvede at implementere dette?

  • 0
  • 0
Torben Mogensen Blogger

Når man ser problemer, som dem i tråden "Skruen uden ende", skyldes det som regel, at typesystemet ikke fra starten af er designet til at håndtere polymorfi (generics) og højereordensfunktioner.

Man får først rigtig glæde af polymorfi og højereordensfunktioner, hvis der er strukturel ækvivalens, tupeltyper og allerhelst typeinferens. Ellers drukner man i typenotation. Man kan så evt. udvide med typeklasser (som i Haskell) eller effekttyper (så man kan se bl.a. exceptions i typen), men det bør gøres med varsomhed, hvis man ikke har typeinferens, da man ellers nemt kommer til at drukne i typeangivelser.

  • 0
  • 0
Jesper Louis Andersen

Det afgørende ved en closure er at den kan referere det omkringliggende scope. Noget i retning af:

int incr = 5;
int i3 = #(int x)( x + incr ).(3);
c.map(#(int x)(x + incr));

Det er lidt mere besværligt med en interface-løsning, med mindre du dependency-injecter incr eller lignende. Rigtigt sjovt bliver det når closures refererer andre closures, returneres fra funktioner eller lignende ting.

Værktøjet er rart for programmøren - og endnu en ting fra de funktionelle sprog som bliver tilføjet til de imperative.

  • 0
  • 0
Niels Dybdahl

Det afgørende ved en closure er at den kan referere det omkringliggende scope....Det er lidt mere besværligt med en interface-løsning, med mindre du dependency-injecter incr eller lignende.

Unnamed classes har jo også uden videre adgang til det omkringliggende scope, så på det punkt er der ikke en gang tale om kortere syntax (eller kan undvære final i flere tilfælde?)

  • 0
  • 0
Lasse Lindgård

Scala findes jo allerede og har alle de features som java aldrig kommer til at få. Det er java-kompatibelt, typestærkt, har closures og typeinferens.

Eksempel på simple closures:
scala> val c = List(1, 2, 3)
c: List[Int] = List(1, 2, 3)

scala> c.map(_ + 3)
res1: List[Int] = List(4, 5, 6)

scala> c.map(_ + "a")
res3: List[java.lang.String] = List(1a, 2a, 3a)

Jeg vil hellere skifte til at skrive min kode i Scala end at benytte nye features i java.

Én ting er at introducere closures, men det løser jo ikke at hele økosystemet omkring java med indbyggede API'er og eksterne biblioteker jo ikke bruger closures og pga. bagud-kompatibilitetskrav heller ikke kommer til at gøre det forløbigt.
Så er energien bedre brugt på at skifte sprog.

  • 0
  • 0
Torben Mogensen Blogger

Én ting er at introducere closures, men det løser jo ikke at hele økosystemet omkring java med indbyggede API'er og eksterne biblioteker jo ikke bruger closures og pga. bagud-kompatibilitetskrav heller ikke kommer til at gøre det forløbigt.

Det samme var tilfældet for generics, men det lykkedes alligevel at få de fleste til at skifte til at bruge det generiske API, selv om man stadig kunne/kan bruge det "gamle" ikke-generiske API. Så det er ikke umuligt, at man kan få de fleste til at bruge et nyt API med closures.

  • 0
  • 0
Torben Mogensen Blogger

Jeg vil hellere skifte til at skrive min kode i Scala end at benytte nye features i java.

Jovist, men jeg bryder mig ikke meget om Scalas objektorienterede notation. For mig er det væsentligt mere naturligt at skrive

[code=haskell]
map (+ 3) [1,2,3]
[/code]
end
[code=scala]
List(1,2,3).map (_ + 3)
[/code]

  • 0
  • 0
Niels Dybdahl

Så det er ikke umuligt, at man kan få de fleste til at bruge et nyt API med closures.

Og hvis Sun/Oracle implementerer det så closurenotationen kan bruges hvor der ellers forventes en implementation af et interface med kun en funktion, så vil dele af de eksisterende APIer kunne acceptere closures uden ændringer. Eksemplet:

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello, world!");
}
});

Kunne så skrives:

button.addActionListener(#(ActionEvent e) { System.out.println("Hello, world!"); })

  • 0
  • 0
Lasse Lindgård

Syntax er jo smag og behag.
I de fleste tilfælde synes jeg faktisk at Scala's for-comprehensions er lettere at læse en closures.

scala> val c = List(1, 2, 3)
c: List[Int] = List(1, 2, 3)

scala> c.map(_ + 3)
res1: List[Int] = List(4, 5, 6)

scala> for (x <- c) yield x + 3
res5: List[Int] = List(4, 5, 6)

for/yield syntaksen er bare sukker for map. Personligt tror jeg er lettere at "sælge" til alle dem omkring mig som aldrig har set andet end imperativ programmering, især hvis det bliver en tand mere kompliceret end dette eksempel.

  • 0
  • 0
Kasper Henriksen

En anden grund til, at funktionssyntaks (som i ML, Haskell, etc.) for bl.a. map er meget mere naturlig end metodekaldssyntaks (som i Scala) er, at funktionskomposition bliver helt naturlig at skrive. F.eks. i SML:

[code=ocaml]
val revrev = List.rev o (List.map List.rev)
val test = (revrev [[1],[2,3],[4,5,6]] = [[6,5,4],[3,2],[1]])
[/code]

PS: Har ikke lige SML installeret på den her maskine, så koden er skrevet frit efter hukommelsen, men det burde virke...

PPS: Ingen  til ML. :suk:

  • 0
  • 0
Torben Mogensen Blogger

[code=scala]
for (x <- c) yield x + 3
[/code]

Haskell er mere læselig:

[code=haskell]
[x+3 | x<-c]
[/code]

PPS: Ingen <div class="geshifilter"><pre class="text geshifilter-text" style="font-family:monospace;"> til ML. :suk:

 
code=ocaml kan til nød bruges også til SML.

  • 0
  • 0
Lasse Lindgård

Det er rigtigt at det bliver lidt længere i Scala. Lister er ikke hardwired in i sproget, men bliver understøttet af standardbiblioteket.

def revrev(list : List[List[_]]) = for (sublist <- list.reverse) yield sublist.reverse
val test = revrev(List(List(1), List(2, 3), List(4, 5, 6)))

Målet med Scala er ikke at være det mest rendyrkede funktionsprogrammeringssprog i verden. Her gør SML et godt stykke arbejde.

Scala bygger oven på SML og Java og skaber en hybrid som understøtter både funtionel og imperativ tankegang på en måde som gør begge dele naturlige. Kompromisset er så at den funktionelle syntaks bliver lidt længere. Fordelen er så at det er kompatibelt med Java - både rent teknisk og konceptuelt.

Scala er ikke det ultimative sprog (her skal vi nok ud og opfinde ét sprog pr. udvikler), men jeg synes at det er et fantastisk godt bud på et kompromis som man kan bygge systemer på de næste ti år.

  • 0
  • 0
Jan Bendtsen

Hej Niels

bare et lille hurtigt tak for et forståeligt eksempel! Den syntaks kan jeg godt se fornuften i - lidt ligesom da man indførte for(objekt obj : kollektion)-notationen.

Mvh
Jan

  • 0
  • 0
Uffe Seerup

Jeg har fulgt diskussionen på lambda-dev. Sprogdesignerne har specifikt sagt at "havelåge" syntaksen er en pladsholder for den endelige syntaks.

Havelågen er valgt for at gøre det enklere at implementere en parser lige nu. Der er nemlig meget større problemer end syntaks når man vil designe "closures" til Java. Fx. er de bekymrede for at der opstår "huller" i Javas typesystem - hvor oversætteren ikke kan finde og rapportere typefejl (eller mulige typefejl).

Som Casper Bang er inde på lader Javas generics lidt tilbage at ønske. Og ironisk nok er det lige netop samspillet mellem 3 Java "valg" som giver problemer lige nu: Javas [i]array covariance[/i] (som C# desværre adopterede), checked exceptions og [i]type erasure[/i] generics.

Det hører også med, at de forslag der diskuteres nu ikke er fulde closures som de andre sprog implementerer dem, eller som wikipedia definerer [i]closure[/i]: "

In computer science, a closure is a first-class function with free variables that are bound in the lexical environment

".

De closures som diskuteres for Java vil nemlig have samme begrænsning mht. brug af variable fra [i]lexical scope[/i] som anonyme indre klasser har: De kan kun referere [i]final[/i] variable.

  • 0
  • 0
Niels Dybdahl

De closures som diskuteres for Java vil nemlig have samme begrænsning mht. brug af variable fra lexical scope som anonyme indre klasser har: De kan kun referere final variable.

Jeg kan godt se at det giver lidt mere skrivearbejde at de kun kan referere final variable, men er det et problem i praksis?

  • 0
  • 0
Lasse Lindgård

Uffe, nu du har fulgt diskussionen lidt nærmere, kan du så sige om notationen som Niels nævner bliver tilladt:
button.addActionListener(#(ActionEvent e) { System.out.println("Hello, world!"); })

Som jeg forstår det kræver den syntax jo at sproget bliver i stand til at caste et vilkårligt interface med en metode i til en closure - eller er det omvendt?

  • 0
  • 0
Henrik Schmidt

Det ligner lidt den nye closure syntax for Objective C (eller rettere Clang).

NSArray *upper = [arr map:^(id obj) { return (id)[obj uppercaseString]; }];

Yikes! Det er da altid noget, at det er en midlertidig syntax fra Javas side.

  • 0
  • 0
Niels Dybdahl

kan du så sige om notationen som Niels nævner bliver tilladt: button.addActionListener(#(ActionEvent e) { System.out.println("Hello, world!"); })

Jeg skrev ikke at den blev tilladt. jeg skrev at det var en mulighed for Sun/Oracle at implementere det, men jeg har ingen anelse om de vil gøre det.

Som jeg forstår det kræver den syntax jo at sproget bliver i stand til at caste et vilkårligt interface med en metode i til en closure - eller er det omvendt?

Funktionelt har de to ting så meget til fælles at man i mange tilfælde (altid?) burde kunne få en compiler til at konvertere mellem dem eller måske ligefrem generere samme kode for dem.

  • 0
  • 0
Anonym

Bare lige en kort bemærkning fra 'the ol' man'.
Hvor f.... fokuserer man stadig (20-30 år efter) på at minimere kode.

Intet kan være mere ligegyldigt.

Det vigtige er ikke at gøredet nemt for kodeaben, men at gøre det nemt for brugeren.

ROI gøres op i primært brugerglæde, og dermed bedre arbejdsvilkår, og ikke om en bitflækker kan spare 1 eller 2 kodelinier.

Billigt at lave, dyrt at bruge - think about IT!

  • 0
  • 0
Jesper Louis Andersen

Bare lige en kort bemærkning fra 'the ol' man'.
Hvor f.... fokuserer man stadig (20-30 år efter) på at minimere kode.

Der er een overvejende god grund til det: Færre kodelinier giver i sidste ende færre fejl. Nuvel, man kan sagtens få for meget af det gode, som du selv er inde på - men lige præcis closures er generelt en rar ting at have. De kan eliminere en stor del boiler-plate kode, gøre koden mere læselig og åbner op for nogle nye måder at beskrive problemer på.

Jeg ser det som en oplagt mulighed for at kunne bruge mindre tid på at arbejde med koden, så man kan bruge mere tid på at arbejde med brugerfladen, eller tilføje mere brugerhjælpende funktionalitet.

  • 0
  • 0
Per Hansen

Fordi der opstår x % fejl for hver kodelinie (uanset om koden er assembler eller java), så mindre kode = færre fejl som udgangspunkt.

(det kan selvfølgelig blive så kompakt at det bliver ulæseligt, hvilket givetvis trækker i den anden retning)

  • 0
  • 0
Frej Soya

@Torben

Der er nu et punkt hvor gængs OO notation er en fordel. Ikke så meget pga. OO men fordi du skriver sit subject/emne før funktionen. De var heldige
med rækkefølgen i forbindelse med interaktiv hjælp i editoren :).

Ofte er det 'svære' at huske funktionen der skal bruges og ikke den værdi/ variabel som funktionen/metoden skal anvendes på - den er jo i scope og kan ses i editoren og meget ofte har du lige skrevet den.

Det er måske mindre hjælpsomt pga. type-inferens og deraf de mere generiske funktioner som ofte altid kan anvendes, f.eks. 'id' vil altid være anvendelig på en værdi. Der er sikkert mange andre problemer :)

  • 0
  • 0
Henrik Schmidt

Vi bruger i forvejen closures i Java i form af anonyme indre klasser som i det ovenstående. Det er bare ret træls at gøre.

...og det ER vigtigt at gøre det nemt for kodeaben at lave et produkt, som brugerne er tilfredse med, og som kan vedligeholdes på en fornuftig måde.

  • 0
  • 0
Uffe Seerup

kan du så sige om notationen som Niels nævner bliver tilladt:
button.addActionListener(#(ActionEvent e) { System.out.println("Hello, world!"); })

Ja - der arbejdes med implicitte konverteringer mellem funktionstyperne og SAMs (SAM = Single Abstract Method) - dvs. abstrakte klasser med en enkelt abstrakt metode eller et interface med en enkelt metode. Det er mit indtryk at det ikke vil blive samme type, men at disse konverteringer vil blive defineret så det eksisterende klassebibliotek kan udnyttes bedre.

  • 0
  • 0
Uffe Seerup

Jeg kan godt se at det giver lidt mere skrivearbejde at de kun kan referere final variable, men er det et problem i praksis?

Om det bliver oplevet som et problem i praksis vil i høj grad afhænge af indenfor hvilket domæne udvikleren vil anvende disse semi-closures. Det er i høj grad et problem ifht. mange af de områder hvor closures anvendes i andre sprog, f.eks. event-handlers som direkte kan manipulere felter fra samme klasse. Allerede nu har mange javaudviklere vænnet sig til hacks som at placere værdier som skal kunne mutere i enkelt-positions arrays.

Det skal dog nævnes at sprogdesignerne pønser på (iflg. lambda-dev) automatisk at definere de variable/parametre som benyttes af en closure som [i]final[/i] - og give compilerfejl når de bruges på en måde som ikke er kompatibel med "final" (dvs. når værdien forsøges ændret).

Men det primære mål med closures i Java7 er at understøtte parallel programmering. Her er closures [i]meget[/i] anvendelige, men samtidigt er det en ønskværdig egenskab at så mange udtryk som muligt er ikke-muterende. Mit gæt er at det vil føles som en mindre begrænsning indenfor dette område.

  • 0
  • 0
Baldur Norddahl

En anden grund til, at funktionssyntaks (som i ML, Haskell, etc.) for bl.a. map er meget mere naturlig end metodekaldssyntaks (som i Scala) er, at funktionskomposition bliver helt naturlig at skrive. F.eks. i SML:

Syntaksen er godt nok lidt mærkelig, men man kan gøre nogle spændende ting i Scala:

[code=java]
implicit def toRevrevT = new AnyRef { def revrev = o.reverse map (_ reverse) }
[/code]
Herefter har alle lister en metode kaldet revrev:

[code=java]
scala> List(List(1),List(2,3),List(4,5,6)) revrev
res0: List[List[Int]] = List(List(6, 5, 4), List(3, 2), List(1))
[/code]

  • 0
  • 0
Fredrik Wendelboe Løkke

Funktionelt har der ikke været noget nyt, siden det første turing komplette sprog.. ;)

  • 0
  • 0
Fredrik Wendelboe Løkke

"
Jovist, men jeg bryder mig ikke meget om Scalas objektorienterede notation. For mig er det væsentligt mere naturligt at skrive

map (+ 3) [1,2,3]

end

List(1,2,3).map (_ + 3) "

Og hvis du vil kalde en funktion på ovenstående resultat, hvad er så mest naturligt?

  1. filter map (+ 3) [1,2,3]

eller

  1. List(1,2,3).map(_ + 3).filter

(første argument til filter er udeladt i begge eksempler..)

her vil jeg påstå at 2. er mere læseligt, da læseretningen følger evalueringen ( den evaluering der foregår i læserens hoved, ikke nødvendigvis den compileren dikterer, som varierer pga. lazyness etc..)

  • 0
  • 0
Torben Mogensen Blogger

her vil jeg påstå at 2. er mere læseligt, da læseretningen følger evalueringen

Så du foretrækker også at skrive dine regnestykker som "x y z+" i stedet for "x+yz"?

At funktion kommer før argument har skoleelever lært siden syvende klasse, så vil ikke tro, at de vil have problemer med at forstå foranstillede funktioner i programmeringssprog. Derimod kan den omvendte notation, man finder i OO-sprog, være et problem. Desuden har OO notationen en påtvunget assymetri, der ikke altid er naturlig. Hvis du f.eks. skal finde midtpunktet mellem to punkter p og q, skal du kalde p.midpoint(q) i stedet for midpoint(p,q). Alene det, at en to-argumentsfunktion skal være en egenskab ved det ene argument, som bruges på det andet argument, er ikke intuitivt.

  • 0
  • 0
Niels Dybdahl

Så du foretrækker også at skrive dine regnestykker som "x y z+" i stedet for "x+yz"

Det er vist ikke den korrekte skrivemåde.
OO: x.add(y.mult(z))
Fkt: add(x,mult(y,z))

At funktion kommer før argument har skoleelever lært siden syvende klasse,

Det argument er jo svært at hamle op med. Hvis skoleelever lærer det, så må det nødvendigvis være det bedste :)

Man vil naturligvis have nemmest ved at læse det man er vant til at læse. Men personligt er jeg ikke i tvivl om at det er nemmest hvis objektet står før operationen. Det gør det delvist i matematikken: x+y starter med et objekt mens sin(x) starter med funktionen. Og på dansk starter man oftest med objektet.

  • 0
  • 0
Baldur Norddahl

Så du foretrækker også at skrive dine regnestykker som "x y z+" i stedet for "x+yz"?

Arh, skal vi ikke blive enige om at hvis man skriver "x y z+" i et OO sprog, så må man skrive "+x y z" i funktionsnotation...

Begge sprogparadigmer er vist enige om at operatører som + og * kan skrives i infix notation.

I Scala er operatører faktisk funktioner på tal. Så følgende er lovlig Scala kode:

x.+(y.*(z))

Man har så indført at man kan undlade at skrive . og () i funktionskald med kun en parameter. Derfor er det ækvivalent at skrive:

x + y * z

Når man undlader parenteserne træder der regler for prioritet i kraft, så den korrekt ganger y og z før den lægger x til.

  • 0
  • 0
Baldur Norddahl

Det jeg finder fascinerende ved Scalas tilgang er at han er sluppet afsted med at definere relativt få regler, som bruges overalt. Sprogdefinitionen er faktisk meget simpel.

Alt er objekter, også konstanter som for eksempel heltal. Derfor kan man kalde diverse funktioner på dem, og det bliver så brugt til at implementere aritmetiske funktioner.

Oversætteren vil så nødvendigvis snyde og genkende forskellige konstruktioner, men det er en implementationsdetalje.

  • 0
  • 0
Torben Mogensen Blogger

Det er vist ikke den korrekte skrivemåde.
OO: x.add(y.mult(z))
Fkt: add(x,mult(y,z))

Nu var det primært argumentet med, at beregningsrækkefølge = læserækkefølge, jeg kommenterede. Ingen af dine eksempler opfylder dette.

Jeg er bestemt heller ikke tilhænger af rendyrket præfixnotation. Jeg har programmeret i LISP og Scheme, og finder denne notation mindre læselig en blandet notation (som brugt i matematik).

Det argument er jo svært at hamle op med. Hvis skoleelever lærer det, så må det nødvendigvis være det bedste :)

Ikke nødvendigvis. Men man skal have en god grund til at afvige fra den notation, som skolelever har lært. LISP og Scheme har en pointe med. at det hele er mere enkel og ensartet, men det kan man ikke sige om OO notation -- med mindre den er helt rendyrket, så man skal skrive x.+(y) i stedet for x+y. Så er den i det mindste ensartet, men ikke (EMM) enklere.

Desuden er den matematiske notation et produkt af flere hundrede års udvikling, hvor man har tilstræbt læselighed og sproguafhængighed uden hensyn til mekanisering. Igen kræver det et rigtigt godt argument at smide denne notation væk, og erstatte den med noget andet.

  • 0
  • 0
Peter Stricker

ikke (EMM) enklere

Argh, her gik du IMHO for langt i fordanskningen ;-)

  • 0
  • 0
Fredrik Wendelboe Løkke

Folkene bag F# er tilsyneladende blive smittet af . notationen i deres arbejde med sproget på .net platformen, i sådan en grad at operatoren |> er blevet idiomatisk.

eksempel:

(* Print even fibs *)
[1 .. 10]
|> List.map fib
|> List.filter (fun n -> (n % 2) = 0)
|> printlist

, voila, . notation i et overvejende funktionelt sprog :)

i modsætning til:

printlist List.filter (fun n -> (n % 2) = 0) List.map fib [1 .. 10]

, vurder selv hvad der er mest forståeligt...

  • 0
  • 0
Lasse Lindgård

Torben du siger:

Nu var det primært argumentet med, at beregningsrækkefølge = læserækkefølge, jeg kommenterede. Ingen af dine eksempler opfylder dette.

Men Frederik siger jo netop at det er den mentale evaluering der er den vigtigste:

List(1,2,3).map(_ + 3).filter

her vil jeg påstå at 2. er mere læseligt, da læseretningen følger evalueringen ( den evaluering der foregår i læserens hoved, ikke nødvendigvis den compileren dikterer, som varierer pga. lazyness etc..)

Jeg kan kun tale for mig selv, men jeg tænker tag listen, læg 3 til alle elementer og filtrer den for noget.

filter map (+ 3) [1,2,3]
Læsning af overstående kræver det jo at ens hjene er indrettet som en gammel HP lommeregner. ;-)

For mig er vinderargumentet for OO notationen at man kan se hvor funktionerne hører hjemme. Map og filter kommer ikke ud af det blå, men er noget en liste "kan".

Med hensyn til dit eksempel med at:
p.midpoint(q) (eller p midpoint q, hvis man vil)
skulle være mindre naturligt end:
midpoint(p, q)

Her gælder samme argument igen. Jeg vil vide hvor min midpoint funktion kommer fra kun ved at læse den linje.

Hvis ikke man synes at instanser af punkter skal kunne beregne afstande til hinanden, så kan kan man jo bare definere en metode med to argumenter i et andet objekt.

  • 0
  • 0
Lasse Lindgård

Desuden er den matematiske notation et produkt af flere hundrede års udvikling, hvor man har tilstræbt læselighed og sproguafhængighed uden hensyn til mekanisering. Igen kræver det et rigtigt godt argument at smide denne notation væk, og erstatte den med noget andet.

Nu vil det være en overdrivelse at kalde mig for en stor matematiker, men er der nogen faste regler for hvornår man skriver funktionen først og hvornår man bruger en operator mellem argumenterne? Er reglen ikke bare at man gør det som er mest naturligt?

Som i List(1, 2, 3) map (_ + 3)

Her putter man map mellem argumenterne som en operator med det argument at map er en lige så almindelig operation på lister som + er på heltal.

Kan man tage patent på hvilken notation der er mest matematisk?

  • 0
  • 0
Aksel Jensen

Det vigtige er ikke at gøredet nemt for kodeaben, men at gøre det nemt for brugeren.

I dette tilfælde er "kodeaben" brugeren.

  • 0
  • 0
Torben Mogensen Blogger

Map og filter kommer ikke ud af det blå, men er noget en liste "kan".

Det argument viser blot, hvor gennemsyret din tankegang er af OO. Map og filter er noget, man [i]gør med[/i] en liste, ikke noget, den af sig selv [i]kan[/i].

  • 0
  • 0
Log ind eller Opret konto for at kommentere
Jobfinder Logo
Job fra Jobfinder