Hvad er det lige, folk har mod separatortegn?

Der er flere, der nævner det som en fordel ved nye programmeringssprog, at de har en syntaks, hvor man i vidt omfang har elimineret parenteser, semikoloner osv. Det gælder fx Go og Ruby, og senest er jeg stødt på en snak om Perl 6, hvor det nævnes som en fordel, at man kan skrive foo 1, 2, 3 i stedet for foo(1, 2, 3) .

Selvfølgelig kan et programmeringssprog blive for verbost, men kan nogen i dette kvalificerede forum evt. kaste lys over, hvor det er, at man kan hente noget ved at spare parenteser og andre separatortegn væk?

For mig at se har de flere positive kvaliteter; primært at de gør ens kode lettere at læse, fordi man undgår, at den flyder sammen til en uniform grød. Noget er selvfølgelig vane, men afhængigt af hvilken skole indenfor teorier om læsning man er tilhænger af er det også muligt, at de ved rent grafisk at stikke ud gør det hurtigere at genkende de forskellige sprogkonstruktioner. Separatortegnene gør det i øvrigt også ofte lettere at lave en hurtig search+replace (eller regex match+replace) gennem ens kode.

Og jeg oplever intet nævneværdigt overhead, hverken rent praktisk eller kognitivt. IDE'et skriver dem i vidt omfang af sig selv, så de koster ikke tastetryk, og i forhold til hvor meget jeg skal tænke over at lave en hensigtsmæssig model af virkeligheden i min kode kræver det nærmest ingen mental indsats at huske at skrive en parentes.

Mikkel Lauritsens billede
Mikkel er CTO i IntraMed, som laver applikationer til håndtering af behandlingsforløb for patienter med kroniske sygdomme.

Kommentarer (83)

Troels Henriksen

Visse letvægtssyntakser kan gøre det nemmere at udføre lighedsbaseret ræsonnering af den type man kan se i bogen "Functional Pearls". Her benyttes Haskells letvægtsnotation for funktionserklæring til at motivere mange omskrivninger. For nu at tage et særligt trivielt eksempel fra min egen kode:

arrayOuterSize :: ArrayEntry -> Count Elements  
arrayOuterSize entry = product (map Imp.dimSizeToExp (take 1 (entryArrayShape entry)))

Denne funktion er egentlig meget enkel, men man skal lige tjekke at parmeteren 'entry' kun bruges ét sted, i slutningen, og at der ikke foregår noget underligt. Vi kan starte med en omskrivning hvor vi bruger lavprioritets-applikationsoperatoren '$' i stedet for parentesgruppering:

arrayOuterSize :: ArrayEntry -> Count Elements  
arrayOuterSize entry = product $ map Imp.dimSizeToExp $ take 1 $ entryArrayShape entry

Nu er kædningen af funktionsapplikationer helt tydelig, men man skal stadigvæk lige sikre sig at 'entry' kun bruges et sted. Vi kan dog nu trivielt omskrive til funktionskomposition:

arrayOuterSize :: ArrayEntry -> Count Elements  
arrayOuterSize = product . map Imp.dimSizeToExp . take 1 . entryArrayShape

Og pludselig behøver man slet ikke tænke for at forstå hvad der foregår! Her har koncisionen gjort koden mere forståelig.

Jeg er stor tilhænger af koncision der fjerner symbol-støj. Omvendt er jeg modstander af koncision der kræver at man husker på flere operatorprioriteter eller kontekster (som i dit Perl-eksempel). APL er et rigtig fint eksempel på et sprog der ved første øjekast ser totalt ulæseligt ud, men i virkeligheden er mere forståeligt end mange andre sprog, alene fordi alle operatorer har samme prioritet og er højre-associative.

Magnus Lund

Mit største problem med diverse væltede tuborg er at de ligger "langt væk" på et tastatur med dansk keyboard layout. Derudover skal man stadig være stringent med indentering for at det bliver læsbart.

Min favorit tilgang er Pythons - det er lidt en gylden mellemvej, fordi det er letlæseligt med indenteringen, men stadig har separatortegn for fx funktionskald - og kolon efter fx if-statements og definitioner. Jeg giver dig fuldstændig ret i, at foo 1, 3, 5 faktisk kan være forvirrende at læse - især hvis der efterfølgende er et andet funktionskald.

Christian Nobel

Jeg ville ønske at man i tidernes morgen havde opfundet separatortegn som var helt forskellige fra almindeligt forekommende tegn i skriftsprog (men jeg godt klar over at 7 bit ascii ikke giver de store frihedsgrader).

For hvor er det da p... irriterende at man skal til at rode med forskellige måde at lave escape af separatortegn som optræder inde i en tekststreng.

Bare se dette kummerlige forum, hvor man benytter stjerne til at markere kursiv og fed osv.

Torben Mogensen Blogger

Sprog som f.eks. Haskell, Python og F# har layoutfølsom syntaks: Indrykningen bestemmer grupperingen, så man slipper for krølleklammer eller "end"-nøgleord.

Det sparer en del tegn og specielt linjer, der kun indeholder afslutningstegn (som f.eks. "}", ")" eller "end"). Men det giver anledning til nogle fejl, der kan være svære at finde, specielt for begyndere, og jeg synes ikke, at besparelsen er specielt stor. Så jeg foretrækker faktisk eksplicitte sluttegn, som nogle gange kan undværes på grund af præcedensregler.

Men jeg er enig med Troels i, at der ikke skal være for mange præcedensniveauer. C++ er bestemt gået over gevind med 16 niveauer. 7-9. burde række: +, *, <, &, :=, samt 1-2 højere (funktionsanvendelse og lignende) og 1-2 lavere (sætnings/erklæringsstrukturer). Behov for yderligere gruppering kan ske med parentestegn, både runde, firkantede og krøllede. Jeg bryder mig ikke om brug af < og > som parenteser, da de bruges som operatortegn også. Man kan evt. udvide parentessættet med f.eks. « og », hvis man vil have flere forskellige.

Jeg så også gerne, at brugerdefineret operatorpræcedens begrænses. Et program med 20 brugerdefinerede operatorer med forskellig prioritet kan være svært at læse, fordi præcedens kan være svært at huske. Her har O'Caml og F# faktisk en god ide: Brugerdefinerede operatorer har præcedens, der er bestemt af det første tegn i navnet. Så en operator +< har præcedens som +, og <+ har præcedens som <. Alternativt kunne man lade alle operatorsymboler være et tegn, men bruge udvalgte Unicode tegn såsom ≤ og ⊕, og definere præcedens til at være som det symbol fra det ordinære operatorsæt, der ligner. Så ⊕ har præcedens som + osv.

Karsten Nyblad

Sprogdesignere tror, det er en god ide, at gøre det nemt at skrive kode. Det er bare meget vigtigere, at koden er nem at læse, for programmører bruger mange gange så meget tid på at læse kode, som de bruge på at skrive den. En god syntaks er en syntaks, der gør det nemt at læse koden. Hvis vi nu igen tager C og C++ operatorpræcedens som eksempel, så er det min erfaring, at den operatorpræcedens allerede er for kompleks for programmørerne.

Det er også vigtigt, at compileren kan fange så mange fejl som muligt, for det er meget hurtigere, at rette fejl, der er fanget af compileren, end det er at finde dem under test og så debugge sig frem til dem - altså hvis fejlen overhovedet bliver fanget under testen. Jeg har set en del eksempler på fejl, der har overlevet testen, men som kunne have været fanget, hvis programmet havde været skrevet i et strikt sprog som f.eks. Ada.

Et andet sted, hvor sprogdesignere går galt i byen, er når de tillader automatisk typekonvertering. Jeg har selv været nødt til at læse maskinkoden, for at finde en fejl i et 50 liner Pascal program. Jeg havde divideret to heltal med hinanden og gemt resultatet i en 8 bytes flyende tals variable. Problemet var, at programmet foretog divisionen i 4 bytes flyende tal, og det gav ikke den fornødne præcision.

Jeg har også debugget et program, hvor jeg kunne se, at den oprindelige programmør ikke var opmærksom på, at (float)integervariable og (double)integervariable ikke betyder det samme i C og C++. I programmet brugte man integers der var for store til at blive gemt præcist i en almindelig floatingpoint variable, men konverterede med (float)integervariable. Prøv du at finde sådan en fejl, hvis du ikke er opmærksom på forskellen på de to måder at konvertere heltal til flydende tal.

Disse to eksempler viser, at selv så tilsyneladende harmløse konverteringer, som at konvertere fra 4 bytes flydende tal til 8 bytes flydende tal kan skabe problemer, hvis det sker automatisk i sproget.

Nikolaj Brinch Jørgensen

Et andet sted, hvor sprogdesignere går galt i byen, er når de tillader automatisk typekonvertering. Jeg har selv været nødt til at læse maskinkoden, for at finde en fejl i et 50 liner Pascal program. Jeg havde divideret to heltal med hinanden og gemt resultatet i en 8 bytes flyende tals variable. Problemet var, at programmet foretog divisionen i 4 bytes flyende tal, og det gav ikke den fornødne præcision.

Jeg har også debugget et program, hvor jeg kunne se, at den oprindelige programmør ikke var opmærksom på, at (float)integervariable og (double)integervariable ikke betyder det samme i C og C++. I programmet brugte man integers der var for store til at blive gemt præcist i en almindelig floatingpoint variable, men konverterede med (float)integervariable. Prøv du at finde sådan en fejl, hvis du ikke er opmærksom på forskellen på de to måder at konvertere heltal til flydende tal.

Det vil jeg ikke mene er sprogdesignerens problem. Det er altid programmørens ansvar at sætte sig ind i hvordan matematiske operationer bliver oversat, og udført, indenfor konteksten af det konkrete programmeringssprog.
F.eks. er der mange der ikke er opmærksomme på at Groovy og Java divergerer i den sammenhæng, og tror at fordi Groovy er et superset af Java, så udføres matematiske operationer da på samme måde (hint: det gør de bestemt ikke - læs blot på division).

Ligeså for cast af f.eks. long til float - hvilket sågar kan være platform specifikt i C, så den operation du beskriver der går galt, kan gå godt på nogle platforme og implementationer af C (se C datatype reglerne).

PS: det hedder flydende kommatal og ikke flyvende tal :-)

Nikolaj Brinch Jørgensen

Og jeg oplever intet nævneværdigt overhead, hverken rent praktisk eller kognitivt. IDE'et skriver dem i vidt omfang af sig selv, så de koster ikke tastetryk, og i forhold til hvor meget jeg skal tænke over at lave en hensigtsmæssig model af virkeligheden i min kode kræver det nærmest ingen mental indsats at huske at skrive en parentes.

Det er klart at fordi et programmeringssprog indeholder features, er det ikke nødvendigt at man SKAL benytte sig af dem.

Som eksempel er der ikke ret mange af os der skriver programmerne på en linje uden linjeskift, selvom det er muligt i Java, C/C++ osv.

Semikolon ser jeg ikke nogen fordel i. Der er allerede sat en newline, så det er vel overflødigt med endnu et stoptegn. for at afslutte en linje. Flere statements per linje er der vel ikke rigtigt nogen af os der gør.

Ternær operator, den benytter vi alle, selvom den ikke just er den mest læsbare, og de fleste programmeringssprog videreføre den grimme og slemme switch/case (GOTO) konstruktion.

Curly braces er jeg tilhænger af, også selvom der er tale om en single if, fordi det er mere læsbart.

Men når vi så ser på de lidt mere moderne programmeringssprog som f.eks. Groovy, så giver dette nogle muligheder for at lave DSLs, og her giver det rigtig god mening at f.eks. paranteser kan undværes, så man f.eks. kan skrive:

int i = x minus y intdiv z

i stedet for

int i = x.minus(y.intdiv(z))

Vi har lavet diverse DSLs til konfiguration af proxy serverer mm. ligesom der i Groovy allerede findes DSLs til XML, JSON mm. og i Grails til Queries mod diverse datasources.

Se blot på Gradles DSL (ren Groovy kode), som eksempel på hvorfor det KAN være en fordel i nogle situationer at udelade disse tegn.

Ellers har SUN Microsystems på et tidspunkt for længe siden forfattet en kode standard for Java, som er svært at komme udenom. Man kan diskutere linjelængde på de ca. 80 tegn i og med vi alle har bredformatskærme, og nok ikke længere printer koden ud, samt de volumiøse indenteringer (8 tegn). Men det er vel sådan set også det.
Det bedste er at den kan deles ud og siges, sådan her skrive vi kode, uden de store diskussioner, og så kan programmørene bruge deres kreativitet på at løse opgaver i stedet for at sløs om curly braces og spaces vs. tabs.

Nikolaj Brinch Jørgensen

Hvis vi skal være pedantiske, så kan det ikke lade sig gøre.


Nemlig! Og derfor tillades dette f.eks. heller i swift.

Conversions between integer and floating-point numeric types must be made explicit

Altså eksplicit cast ellers vil compileren brokke sig med Int / Int. Det er ret godt gået, da programmøren så ikke får sig en overraskelse, men selv bestemmer hvad der skal ske. I Java trunkeres der og I Groovy laves der implicit cast til BigDecimal ??? Portering fra f.eks. Java til Groovy af beregninger er altså noget man skal være varsom med, og have god testcoverage af.

Mikkel Lauritsen Blogger

Mit største problem med diverse væltede tuborg er at de ligger "langt væk" på et tastatur med dansk keyboard layout.

Hæ, det kan selvfølgelig være, at det er en medvirkende faktor... Jeg bruger selv normalt amerikansk keyboardlayout til programmering, hvor tuborgparenteserne ligger lidt mere lige for hånden, og især / (forward slash) ligger der, hvor den skal.

Derudover skal man stadig være stringent med indentering for at det bliver læsbart.

Jep. Der er jeg så igen forvænt med et IDE, som laver den slags af sig selv.

Baldur Norddahl

Ternær operator, den benytter vi alle, selvom den ikke just er den mest læsbare, og de fleste programmeringssprog videreføre den grimme og slemme switch/case (GOTO) konstruktion.

Kun fordi de har navngivet den ternære operatør mærkeligt. I Scala hedder den simpelthen "if".

//Java

String foo = answer == 42 ? "yes" : "no";

// Scala

val foo = if (answer == 42) "yes" else "no"

Hugo Hansen

I forhold de gode gamle blok-operatorer som begin/end fra Pascal og DataFlex, så synes overheadet helt hen i vejret i forhold C++ og Java, men set i forhold til den den tid der bliver brugt på design, algoritmer og debugging så er det INGEN TID. Man overser aldrig begin/end i forhold til fancy placeret '{' osv.

Det er lige som programmøre som klager over at skulle pakke deres data ind i setter/getter metoder - i forhold til at skulle holde rede på hvem, hvad og hvor der arbejder på med klassens data så er det kun en gevinst.

Set i det store tidsmæssige perspektiv ser jeg kun fordele ved eksplicit seperatorer. Især hvis der kigges på at selve kodningen kun er 20% af det samlet projekt!

Dertil kommer at mange IDE'er tilbyder auto-coding/completion så du skal ikke engang selv skrive dem.

Ivo Santos

Jo enklere en sprog er desto svære er det at få korthuset til at falde sammen.

Jo mere kompliceret en sprog er desto nemmere er det at få korthuset til at falde sammen.

Ivo Santos

Tag f.eks. C++ sproget eller javascript for den skyld, de sprog kan betegnes som sprog som indholder en del komplekse ting som kræver en hel del forståelse for at de kan bruges uden de store problemer.

På den anden side har vi Basic, og lignende som er ret simpelt opbygget og er til dels nemme at forstå, derudover kræver de ikke den helt store forståelse for at kunne bruge dem, dermed ikke sagt at man ikke kan lave dumme ting i f.eks. Visual Basic, for det kan man.

Og så er der C sproget som egentlig er temmelig enkel opbygget.
Jeg indrømmer gerne at arrays og linked lists kan være lidt svært at forstå for nybegynder, men de kræver ikke en forklaring som er lige så stort som dele af f.eks. c++ sproget for at tage et eksempel.

Apropos dynamiske C arrays:
Jeg må tilstå at det ikke er nemt at finde en artikel på internettet som forklarer det punkt, og det er til dels også fordi de eksempler de bruger er en smule avancerede, men når man først forstå hvordan det gøres så er egentlig ikke så svært, men sådan er det vel med mange ting i livet.

Assembler programmering er dog stadig et område for sig selv vil jeg mene, så det område holder jeg mig langt væk fra.

Peter Christiansen

perl5 er også et simpelt sprog, her må man alt og der er mange måder
at gøre det samme på og så har det regulære udtryk direkte bygget ind!

Er der andre her inde der programmerer professionelt i perl?

Palle Simonsen

Kan du nævne eksempler på hvad du opfatter som enkle eller komplicerede sprog?

Et meget enkel sprog:

s_expression = atomic_symbol \  
              / "(" s_expression "."s_expression ")" \  
              / list   
   
list = "(" s_expression < s_expression > ")"  
   
atomic_symbol = letter atom_part  
   
atom_part = empty / letter atom_part / number atom_part  
   
letter = "a" / "b" / " ..." / "z"  
   
number = "1" / "2" / " ..." / "9"  
   
empty = " "

Eksempel:

;;; No separators  
(setf foo (if (eq val 42) "yes" "no"))  
   
;;; No indentation rules  
(setf foo  
  (if (eq val 42)  
      "yes"  
      "no"  
))
Troels Henriksen

Kompliceret sprog er nemt nok: ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

Det her er netop hvad jeg snakker om! Ingen kan vel reelt mene at Brainfuck er kompliceret? Det har otte instruktioner som enhver kan lære på tyve minutter. Er det svært at skrive noget i det? Ja, men det er fordi sproget ikke er så udtryksfuldt, ikke fordi det er kompliceret. Analogien er at en skovl også er mindre kompliceret end en gravko, men stadigvæk mere besværlig hvis man skal grave et kæmpe hul. Eller måske at en fransk hotdog er enklere end en burger? Det er måske mere forståeligt.

Baldur Norddahl

Læs den øverste regel igen

Ja, der står at s_expression enten er atom_symbol, eller noget der starter med en parentes eller en liste (der også starter på en parentes). Atom_symbol er noget der skal starte med et bogstav. Hvordan får du lige et tal (der ikke er et bogstav eller en parentes) eller gåseøjne (der hellere ikke er et bogstav eller en parentes) til at matche det?

Gåseøjne er faktisk slet ikke defineret i dit sprog.

Hugo Hansen

Det har taget mig 20 år at få respekt for Basic og lignende sprog. Personligt er jeg klart til syntactic sugar. Så får jeg ligesom fornemmelse af at skrive kunst og højere datalogisk poesi :-) MEN det er bare blålys og afledning. Ingen tvivl om at det gør det vanskeligere at læse og det tager båndbredden fra at forstå formålet med koden og spotte evt. fejl.

Jeg har fx lige spildt timer på at debugge et java8-streams relateret problem. Closures, lambdas og operator overloading er ikke uden omkostningen. Personligt er jeg helt pjattet med polyformi og strategy-pattern, men det koster også på båndbredden.

At skulle forstå kode på et konceptuelt niveau før man kigger koden efter i sømmene er en disciplin og det koster, så jeg ender tit med at debugge mig igennem mængder af kode for at finde hoved eller hale i den. Den teknik har også en omkostning fordi der er en risiko for at stirer mig blind på detaljen og taber overblikket.

Så der er vel ikke nogen silver bullet - seperator tegn er nogen gange fint, andre gange en hæmsko.

Torben Mogensen Blogger

Et meget enkel sprog:

Ja, syntaksen er enkel, men et sprog er ikke kun syntaks. Man kan sagtens lave et sprog med en meget enkel syntaks men en vildt kompliceret semantik. Det gælder f.eks. for refleksive varianter af LISP, f.eks. 3LISP. Du kan ikke sige, at et sprog er simpelt, uden at angive dets semantik.

Nikolaj Brinch Jørgensen

Kun fordi de har navngivet den ternære operatør mærkeligt. I Scala hedder den simpelthen "if".

//Java

String foo = answer == 42 ? "yes" : "no";

// Scala

val foo = if (answer == 42) "yes" else "no"


Tja, men er det den ternære operator? Er det ikke en one line if, der udnytter at alle udtryk (expressions) i Scala returnere en værdi og det er det som udnyttes i denne one line if?

Baldur Norddahl

Tja, men er det den ternære operator?

Det er det da. Det er den klassiske "if-else" der er afskaffet i Scala.

"if" statements i C er faktisk overflødige da de i alle tilfælde kan omskrives til den ternære operator ligesom i Scala. Det er en forenkling af sproget der kommer ved at indse at alle statements skal returnere en værdi (eventuelt en dummy værdi).

Nikolaj Brinch Jørgensen

"if" statements i C er faktisk overflødige da de i alle tilfælde kan omskrives til den ternære operator ligesom i Scala. Det er en forenkling af sproget der kommer ved at indse at alle statements skal returnere en værdi (eventuelt en dummy værdi).


Statements vs Expressions

Vi må lige være enige om at statements i Scala er som statements i Java, og at de ikke returnerer en værdi. Forskellen er at if er et expression i Scala modsat Java, hvor det er et statement.
Havde if været et statement, ville din kode ikke virke.

Jeg er enig i at dit eksempel ser pænt ud, men egentlig ikke forbedre den ternære operator synderligt.
Problemerne med læsbarheden med disse konstruktioner bliver for alvor store når jeg læser 3-4 nestninger af ternære operatorer, og der ikke er skyggen af tests eller lignende, og metoderne ellers fylder 400-500 linjers branching. Samt indeholder et væld af variable (der benyttes til flag).
Så er vi ude i hampen.

Sprogdesign er svært, for der er altid de der features der bliver indlemmet, når designeren lige vil vise hvor dygtig og smart han kan lave noget, som så viser sig at gøre kode skrevet i sproget meget vanskeligt at tyde.

Baldur Norddahl

Vi må lige være enige om at statements i Scala er som statements i Java, og at de ikke returnerer en værdi.

Hvis vi må være enige om det, så må vi også være enige om at statements ifølge den definition ikke eksisterer i Scala :-)

Alt returnerer en værdi i Scala, inklusiv ting som "if", "for", "while", "match", alle funktionskald ("void" som returkode findes ikke) og så videre. Dog undtaget deklarationer af variabler og funktioner, hvorfor en kode blok ikke kan slutte med en sådan deklaration.

Palle Simonsen

@Torben: Det samme kan siges om brainfuck, men med modsat fortegn - simpel semantik, men kompleks syntaks og et i praksis ubrugeligt sprog udenfor kontekst af 'lad mig imponerer dig med hvad jeg kan' :)

Hvis vi vender tilbage til LISP 1.5 er både syntaks og semantik ret enkel. Selv senere videreudviklinger som Interlisp-D, Zetalisp og Commonlisp (Clojure undtaget) holder en rimelig enkelhed. Dermed synes jeg man generelt kan betegne LISP som et ret enkelt og elegant sprog, der så elegant undgår en række af de syntaktiske 'vorter', der plager ALGOL type sprog og sprog med positionel syntak som f.eks. Visual Basic og Python.

@Baldur - jep, der mangler et par produktioner i syntaksdefinitionen - men den kvikke læser kan hurtigt udvide med det manglende :)

Nikolaj Brinch Jørgensen

Hvis vi må være enige om det, så må vi også være enige om at statements ifølge den definition ikke eksisterer i Scala :-)

Alt returnerer en værdi i Scala, inklusiv ting som "if", "for", "while", "match", alle funktionskald ("void" som returkode findes ikke) og så videre. Dog undtaget deklarationer af variabler og funktioner, hvorfor en kode blok ikke kan slutte med en sådan deklaration.


Det er vi så ikke enige om. En klassedefinition, object definition, trait definition, variable declarations osv. er statements og returnere ikke værdier.

udtrykket

val i = val j

eller

val incorrect = class MyClass(val value: String)

Giver ikke mening og jeg tvivler på compileren ved hvad den skal gøre ved det?

Nikolaj Brinch Jørgensen

De er deklarationer fordi de ikke udfører en operation. Et statement er noget der udfører en handling.


Det er altså ikke en forskel.
Forskellen ligger i statements (og jeg ved godt at expressions er statements) og expressions.
et andet statement er println, som ikke returnerer en værdi men Unit (det er et statement).

Men nok om det, we can agree to disagree :-)

En anden ting er så om Scala har valgt i samme ombæring at indføre den meget nyttige elvis operator ?: fra Groovy?

Altså kan man noget a la

Groovy
i = i ?: new I()

Scala ?
i = if (i) else new I()

Så hvis i ikke er defineret så defineres den?

Troels Henriksen

@Torben: Det samme kan siges om brainfuck, men med modsat fortegn - simpel semantik, men kompleks syntaks og et i praksis ubrugeligt sprog udenfor kontekst af 'lad mig imponerer dig med hvad jeg kan' :)

Brainfuck har både enkel semantik og syntaks. Her er en udtømmende grammatik:

BF ->  
BF -> BFI BF  
BFI -> +  
BFI -> -  
BFI -> [BF]  
BFI -> <  
BFI -> >  
BFI -> ,  
BFI -> .

Der er intet kompliceret ved Brainfuck som sprog. Folk der siger andet har aldrig sat sig ind i hvordan det fungerer! Man skal ikke lade sig skræmme af at et program ved første øjekast ser ulæseligt ud - uanset sproget - ellers kommer man aldrig ud fra sin Algol/krølleparantes-komfortzone, og det er en skam, for der er massevis af spændende sprog at lære. Nok ikke Brainfuck - det er virkelig ikke nyttigt til ret meget, men sådan noget som APL, Forth eller Lisp ser også fremmed ud, men gemmer på virkelig interessante idéer og styrker.

Lisp er iøvrigt også et af de sprog jeg ville pege på som et "enkelt" sprog - måske ikke Common Lisp, men de enklere dialekter, jovist.

Baldur Norddahl

Scalas "Unit" er altså en type modsat Javas "void". Println returnerer derfor en konkret værdi, nemlig et singleton objektet af typen Unit. Dette singleton objekt kan også skrives (). Fordi det er en type, så findes det i type hierarkiet. scala.Unit nederver fra scala.AnyVal der nedarver fra scala.Any.

Da det er en type og der findes en konkret værdi, så kan man eksempelvis lave lister med Units eller bruge dem som values i et Map (der dermed effektivt bliver et kluntet Set).

Med hensyn til elvis operatør, så indeholder idiomatisk Scala kode ikke null tjeks. Hvis der er mulighed for at noget bliver null, så bruger man en Option i stedet. Option har en getOrElse metode, der har samme funktion som elvis operatoren. Option har også en statisk metode der konverter en værdi der muligvis er null til en Option værdi.

Din kode kan derfor oversættes til følgende:

i = Option(i).getOrElse(new I)

Ivan Skytte Jørgensen

Jeg finder det lidt overraskende at ingen endnu har brragt op den datalogisk smukke syntaks for pascal (den oprindelige ISO-pascal (sm implementered af compass/polypascal, ej Borlands afvigende parser), som belyste hvordan syntaksvalg kan have effekt på produktivitet.

For de uindviede så var semikolon brugt til at binde to sætninger sammen, og kun det. Og der var intet no-op/null statement. Det betød at visse konstruktioner blev mere bøvlede, f.eks:

while pop(stack)>0 do  
    begin  
    end;

ikke var gyldigt fordi begin-end skal indeholde et statement. Så man endte tit med:

while pop(stack)>0 do  
    begin  
        dummy := 1  
    end;

(bemærk også at dummy-linien ikke indeholder semikolon for skulle der jo være en sætning til. Arghh...

Og hvis man havde en if-then-begin-end-semikolon, så kunne man ikke blot smække en else-del på fordi semikolonnet jo ville adskille den overliggende if-sætning. Arrghh igen.

Og den sidste end i programmet? Den var speciel fordi den skulle afsluttes med et punktum. Arrggh igen igen.

Så syntaksen var datalogisk enkel og smuk og vidunderlig,men at skrive kode kunne være lettere irriterende.

Man kan så sammenligne med sprog som har semikolon som sætnings-afslutning, og/eller tomt-element somnull statement, f.eks.:

while(pop(stack)>=0)  
    ;

eller hvis man bedre kan lide {}-blokke:

while(pop(stack)>=0)  
{  
}

Det gør syntaksen lidt mere kompleks og kræver lidt mere planlægning, men resultatet er mere programmørvenligt. Det kan dog åbne for trælse fejl:

while(pop(stack)>=0);  
{  
    //et-eller-andet  
}

som dog Flexelint og alle anstændige compilere/fortolkere vil advare om.

Christian Nobel

Det er godt nok modigt at du bringer Pascal på banen, da det jo af de fleste herinde anses for noget inferiørt.

For de uindviede så var semikolon brugt til at binde to sætninger sammen, og kun det.

Men det er jo så en historisk betragtning al den stund det ikke er sådan i FreePascal (og ej heller i Delphi), hvor semikolon jo bruges til at indikere slut på et statement.

Og der var intet no-op/null statement. Det betød at visse konstruktioner blev mere bøvlede, f.eks:

while pop(stack)>0 do
begin
end;

ikke var gyldigt fordi begin-end skal indeholde et statement.

Det er så gyldigt i FPC.

Så man endte tit med:
while pop(stack)>0 do
begin
dummy := 1
end;

(bemærk også at dummy-linien ikke indeholder semikolon for skulle der jo være en sætning til. Arghh...

Og igen i FPC har man bare sine statements inden for begin - end, og hvert statement afsluttes med et semikolon - dog er der imo en lille inkonsistens, da ovennævnte faktisk er tilladt, da det tillades for sidste statement ikke at afslutte med et semikolon.

Jeg gør det nu altid da jeg mener det øger læsbarheden, og da jeg ikke bryder mig om implicitte slutninger.

Ivan Skytte Jørgensen

Det er godt nok modigt at du bringer Pascal på banen, da det jo af de fleste herinde anses for noget inferiørt.

Den oprindelige pascal's syntax fra omkring 1970 var et skridt fremad i forhold til de andre sprog på det tidspunkt, men ret ynkelig i forhold til hvad vi har i dag. Men det betyder ikke at man ikke kan lære hvad der var gode ideer og dårlige ideer. De fleste compilerleverandører lavede også fornuftige udvidelser pga. de uhensigtsmæssigheder den oprindelige syntax havde (null statements, semikolon på sidste end, typeløse pointere, afslapning af typebegrebet mht. strenge og arrays, ...). Det oprindelige sprog var tiltænkt undervisning i i strukturerede programmer (jamen man da bare bruge 3 gotos...) og strukturerede typer (jamen alt er jo bytes og hvis jeg vender bit 4 på offset 7 så ...), og jeg mener at det har bidraget til at der er mindre slamkode i dag end der ellers ville have været.

For at du ikke skal tro at jeg ville hakke på pascal, så lad mig komme med et eksempel fra et af mine favoritsprog, C/C++: typeerklæringer skal afsluttes med semikolon (fint nok) fordi man kan erklære en struct/union samtidig med at man definerer en variabel, som i:

struct A {  
  int x;  
} y;

hvilket er knap så smart, fordi hvis man glemmer semikolonnet efter } i structen i en alm. struct-erklæring så ville typen blive returtypen på den efterfølgende funktion (pre-ANSI). I C++ har man valgt at afsluttende } i namespaces ikke skal have et semikolon. Man så overveje om den forskellighed er smart, for jeg ser tit at folk lystigt sætter semikolon efter namespaces, men det er faktisk en syntax-fejl, fordi det ville være et "null declaration" hvilket C/C++ ikke har.

Torben Mogensen Blogger

Det er godt nok modigt at du bringer Pascal på banen, da det jo af de fleste herinde anses for noget inferiørt.

Du skal passe på med at tale på "de flestes" vegne. Personligt synes jeg, at Pascal havde mange fordele frem for f.eks. C og Java:

  1. Notationen for typeerklæringer er bedre end i C og Java, omend mere ordrig. for eksempel har erklæringen "a : array[5..7] of real" klart separeret typen på den ene side af kolonet, hvor "float a[3]" har en del af typen før navnet og en anden del af typen efter navnet. Det blev så ikke brugt helt konsekvent, da funktionstyper (brugt til funktionsparametre) ikke kunne skrives rent efter et komma.

  2. Subrangetyper som f.eks. 5..7 og -10..10 er bedre end short, int, long og long long, idet man er sikker på, at en implementering har hele det nødvendige interval og ikke mere. Ideelt set burde typen integer så have være ubegrænsede heltal, men sidst i 1960'erne var omkostningerne for store til dette.

  3. Lokalt erklærede funktioner gjorde f.eks. funktionsparametre mere brugbare end i C, hvor en funktionsværdi blot er en kodepointer. Pascal har en form for closures, men de kan (fordi de er stakallokerede) ikke returneres ud af den funktion, hvori de er konstrueret.

  4. Forbuddet mod modifikation af tælleren inde i en for-løkke gør det nemmere at ræsonnere om den. Bl.a. er termination garanteret.

  5. With-sætninger gør det nemmere at arbejde med records/structs, og variant records er en mere typesikker løsning end unions a la C.

  6. Var-parametre er en mere elegant (og sikker) løsning end pointere til variable.

Det er i øvrigt forkert, at Pascal ikke tillader tomme sætninger. Jeg har Pascal User Manual and Report (som definerer sproget) foran mig, og både BNF-grammatikken og syntaksdiagrammerne viser tydeligt, at tomme sætninger er tilladt.

Pascal er dog ikke uden fejl. Syntaksen er meget ordrig, hvilket blandt andet skyldes, at den er designet til en one-pass compiler med simpel recursive-descent parsing uden lookahead. Det er bl.a. derfor, at man skal have forward-erklæringer, hvis man vil kalde en funktion, der er erklæret senere.

Christian Nobel

Du skal passe på med at tale på "de flestes" vegne. Personligt synes jeg, at Pascal havde mange fordele frem for f.eks. C og Java:

Ja, men alligevel omtaler du Pascal i datid.

Ville diskussionen ikke være mere relevant hvis vi forholdt os til nutiden, dvs. en moderne Pascal som FreePascal eller Delphi.

Man sidder jo heller ikke på et bilforum og laver sammenligner af bremser, hvor man for Fords vedkommende tager udgangspunkt i tromlebremser fra midten af tresserne.

Og mht. ordrigheden ser jeg det ikke som noget problem, nok nærmere det modsatte, da det gør (især hvis man skal prøve at dechifrere hvad andre har skrevet) læsningen meget nemmere.

Troels Henriksen

Ja, men alligevel omtaler du Pascal i datid.

Udover let trolling, så er det nok fordi de fleste har anerkendt at Pascal har været døende i et stykke tid, og næppe har meget fremtid - uanset dets fortræffeligheder. At diskutere Pascal versus C er også lidt omsonst - der er ikke mange tilbage der mener at C er designet for fremtidens sprog - og de steder man nutildags bruger C gør man det af eksterne årsager der ikke levner mulighed for Pascal (eller andre sprog).

Og mht. ordrigheden ser jeg det ikke som noget problem, nok nærmere det modsatte, da det gør (især hvis man skal prøve at dechifrere hvad andre har skrevet) læsningen meget nemmere.

Lad os se på følgende to måder at transformere og derefter akkumulere indholdet af en tabel:

// Den første  
acc = 0  
for (int i = 0; i < size(A); i++) {  
  acc = comb(acc, f(A[i]))  
}  
   
// Den anden  
acc = fold(comb, 0, map(f, A)) 

Synes du virkelig den første er nemmere at forstå? Man skal jo kontrollere at der ikke foregår sære ting med indekserne, og at acc ikke bliver modificeret på anden måde end man tror. Det er til gengæld tydeligt med det samme når man har lært hvad fold og map betyder. Det kan også skrives endnu mere kompakt, i APL:

acc = comb/f¨A

Kode bliver læst meget oftere end den bliver skrevet, men af folk der har lært sproget, hvorfor der er fidus i at bruge en kraftfuld og kompakt notation. APL har så naturligvis den ulempe at mange af tegnene ikke findes på almindelige tastaturer, og selvom det kan løses med den rigtige Emacs-mode, så er det stadigvæk moderat nederen.

Man sidder jo heller ikke på et bilforum og laver sammenligner af bremser, hvor man for Fords vedkommende tager udgangspunkt i tromlebremser fra midten af tresserne.

Jeg synes mere det svarer til, at man ikke diskuterer fast-food ved at analysere en McDonalds Big Mac fra 70'erne, men derimod tager ned på Oasen på Jagtvej og får sig en kebab (eller ud på King Kebab i Ballerup, som vi også har opdaget er rigtig god). Hvad mange ikke ved er dog også, at Brøndby Grill House (som ligger meget tæt på Glostrup) laver nogle af de bedste burgere og pomfritter man kan få i Københavnsområdet. Klart et besøg værd, omend der selvfølgelig ikke er så meget andet i området der er værd at besøge. Der har Kebab King i Ballerup en mere strategisk placering lige i nærheden af Dansk Datahistorisk Forenings kælder.

David Rechnagel Udsen

Man sidder jo heller ikke på et bilforum og laver sammenligner af bremser, hvor man for Fords vedkommende tager udgangspunkt i tromlebremser fra midten af tresserne.

Det er godt nok en tynd analogi. Det er vel rimeligt at sammenligne gamle bremser i forhold til nye. Dette er jo betydeligt i forhold til at forstå fordelene ved skivebremse fremfor tromlebremser. Og så skal det jo siges at Ford var nogen af de første i Amerika til at rykke ud med skivebremser allerede i 1964 på deres Thunderbird. Omend cirka 10 år efter de var begyndt at rykke ud på masseproduktionsbiler i Europa.

Desuden er det vel også pænt dumt bare at sammenligne bremser på tværs af mærker. Jeg tvivler på en Ford Ka og en Ford GT40 har de samme bremser.

Et bedre eksempel ville have været at Pascal var en Opel Corsa og C var en Volkswagen Golf. Men i Corsa'ens tilfælde så bruger vi en model fra 1985, mens i Golf'ens tilfælde tager vi en fra 2015. Jeg ved godt hvilken en jeg vil foretrække (en VW Scirocco fra 1977).

Philip Munksgaard
Christian Nobel

Udover let trolling, så er det nok fordi de fleste har anerkendt at Pascal har været døende i et stykke tid, og næppe har meget fremtid

Ud over din holdning er temmelig nedgørende, så ligger Pascal/Object Pascal og roder med en andel så nogen lunde som Perl, Ruby, Visual Basic, og væsentlig højere end Scala, Lisp og andre "små sprog".

Så din italesættelse af Pascals nært forestående død er nok ret så overdrevet.

Synes du virkelig den første er nemmere at forstå?

Havde ikke været mere relevant hvis du havde lavet dit eksempel i Pascal, når nu det er det du er på nakken af?

Jonas Høgh

Synes du virkelig den første er nemmere at forstå? Man skal jo kontrollere at der ikke foregår sære ting med indekserne, og at acc ikke bliver modificeret på anden måde end man tror. Det er til gengæld tydeligt med det samme når man har lært hvad fold og map betyder.


Vi, der har været igennem funktionsprogrammering på studiet, kan hurtigt blive enige om, at højereordensfunktioner er en dejlig ting, men det ændrer ikke på, at en stor del af "dark matter developers" ikke behersker dem, og formentligt vil finde eksempel 2 meget sværere at forstå. Og det har vel som sådan ikke noget at gøre med emnet hvorvidt du kan bruge en bedre abstraktion end en for-løkke, emnet er vel, hvor meget "syntaktisk støj", der er passende for at sikre læsbarheden. En mere retfærdig sammenligning kunne være en C for-løkke og Haskells do-notation over liste-monaden.

Ivan Skytte Jørgensen

Det er i øvrigt forkert, at Pascal ikke tillader tomme sætninger. Jeg har Pascal User Manual and Report (som definerer sproget) foran mig, og både BNF-grammatikken og syntaksdiagrammerne viser tydeligt, at tomme sætninger er tilladt.

Det var interessant. Jeg kan huske at jeg boksede med det i polypascal, og de BNF/EBNF klumper, som jeg kan finde i dag, siger:

compound-statement = 'begin' statement-sequence 'end'  
statement-sequence = statement { ';' statement }

hvilket ikke tilader tomme sætninger. Kan det tænkes at den PUMR, som du sidder med, er nyere end 30 år, eller at producenterne i gamle dage anvendte en simplere EBNF som ovenstående?

Troels Henriksen

Ud over din holdning er temmelig nedgørende, så ligger Pascal/Object Pascal og roder med en andel så nogen lunde som Perl, Ruby, Visual Basic, og væsentlig højere end Scala, Lisp og andre "små sprog".

Det er bare et programmeringssprog - det har ikke nogen personlig stolthed jeg kan være nedgørende overfor. Jeg ser bare, at Pascal virker som om det stadigvæk kæmper C og old-school C++, og slet ikke har noget modsvar til mere moderne og ambitiøse sprog som Rust (eller måske endda Go) der prøver at bruge mere moderne programmeringssprogsteori til at løse nye problemer. Pascal er 60/70'er-perfektion (omend det er blevet forbedret løbende siden, så er det typisk via forfinelse og inklusion af andre faciliteter der blev opfundet i 70'erne).

Havde ikke været mere relevant hvis du havde lavet dit eksempel i Pascal, når nu det er det du er på nakken af?

Jeg er ikke på nakken af Pascal. Det er et sprog jeg vitterligt aldrig støder på i mit arbejde som PhD-studerende og forsker i programmeringssprog, eller i mit virke i open-source projekter, og derfor på ingen måde generer mig (shell script generer mig langt mere). Mit eksempel med foldningen var et modargument til din tese om at ordrighed letter læsbarheden, hvilket virker som en generel påstand, og ikke en baseret udelukkende på Pascal.

Christian Nobel

Jeg er ikke på nakken af Pascal. Det er et sprog jeg vitterligt aldrig støder på i mit arbejde som PhD-studerende og forsker i programmeringssprog

Og det er så måske det der er problemet - jeg er bare en "dum" udvikler, ude i den virkelige verden, som gerne vil lave nogle løsninger, aka programmer, som tilgodeser mine kunders virkelige behov.

I det spil er min erfaring at jeg kan lave solide, stabile og hurtige (eksekveringsmæssigt) programmer på en rimelig tid i FreePascal/Lazarus et al, som tilgodeser kundens ønsker.

Og så er jeg sådan set ligeglad med hvad man teoretisk set anser for "mere moderne" eller "mere ambitiøst" - for mig er det produktiviteten og resultaterne der tæller, og det at jeg også kan læse min egen kode efter et par år - og jeg har ingen interesse i at udvikle min egen compiler, men foretrækker at have en god kompiler som understøtter mig, og fanger de værste kvajerter.

Men jeg er lidt træt af at åndssnobberiet prøver på at tale et godt værktøj ned.

Hvis jeg ellers har forstået dit eksempel, så kan jeg ikke se andet end Comb er en funktion der gør et eller andet ved Acc og en anden funktion, nemlig F - det er i hvert fald ikke statements jeg kan genkende.

Allan Jensen

acc = 0    
for (int i = 0; i < size(A); i++) {    
  acc = comb(acc, f(A[i]))    
}  

Har allerede fået en funktionel programingsskavank. Du ville normalt aldrig skrive sådan i C++ medmindre du er ved at skrive en templete.

acc = 0    
for (int i = 0; i < size(A); i++) {    
  acc += f(A[i])   
}  

Er klarere end

acc := foldr op+ 0 map(f, a)

Som selvom kortere benytter sig af flere abstrakte begreber og teknikker man skal kende for at kunne læse det.

Troels Henriksen

Du har ændret min generelle operator comb til addition. Selvfølgelig kan det udtrykkes nemmere hvis man må antage mere om den givne operator! Haskell:

acc = sum (map f a)

Men der er stadigvæk mistet information. Den funktionelle udtryksform tillader ræsonnering på et langt mere velfunderet fundament. Lad os igen kigge på den funktionelle formulering:

acc = reduce(comb, 0, map(f, a))

Jeg skriver 'reduce' frem for 'fold' for at angive at min operatorer 'comb' er associativ (det er desværre meget svært at gennemtvinge i et typesystem, men mange operatorer er associative). Vi kan nu udnytte en listehomomorfi-identitet for map-reduce til at omskrive således:

acc = reduce(comb, 0, map(\chunk -> reduce(comb, 0, map(f, a)), splitInto(J, a)))

Her opdeler jeg den oprindelige liste 'a' i J dele, og udfører map-reduce operationen på hver del ('chunk'). Jeg ved at det endelige resultat stadigvæk er det samme, og fordi den præcise iterationsrækkefølge er abstraheret væk, så har jeg kunne fokusere på sådanne højniveautransformationer. Fordelen er nu at den indre map-reduce kan sekventialiseres (f.eks. ved at smelte den indre 'map-reduce' sammen til en enkelt 'foldl'), og de ydre J maps kan køres parallelt, og til sidst kombineres via en 'reduce'-operation. Vi kan skrue på køretidsparameteren J for at tilpasse den eksponerede mængde af parallelisme til hvor meget parallelisme vores maskine kan udnytte. Det er et eksempel på hvordan koncis og abstrakt notation kan bruges til at lave højniveauoptimeringer der batter. Ovenstående transformation ville være langt mere nederen at ydtrykke med en eksplicit for-løkke.

Torben Mogensen Blogger

Det var interessant. Jeg kan huske at jeg boksede med det i polypascal, og de BNF/EBNF klumper, som jeg kan finde i dag, siger:

compound-statement = 'begin' statement-sequence 'end'
statement-sequence = statement { ';' statement }

hvilket ikke tilader tomme sætninger. Kan det tænkes at den PUMR, som du sidder med, er nyere end 30 år, eller at producenterne i gamle dage anvendte en simplere EBNF som ovenstående?

Den udgave, jeg har, er fra 1975. Den bid EBNF, du viser, udelukker ikke tomme sætninger. Grammatikken i PUMR indeholder produktionerne

<simple statement> ::= <assignment statement>  
                    | <procedure statememt> | <go to statement> |  <empty statement>  
   
<empty statement> ::= <empty>  
   
<empty> ::=

Det kan sagtens være, at nogle compilerproducenter brugte en enklere grammatik. Det var ikke ualmindeligt, at de udelod f.eks. funktionsparametre eller brugte shallow binding i stedet for deep binding, hvilket kan give forkerte resultater, når man bruger var-parametre eller funktionsparametre.

Leif Neland

Jeg holder nu utroligt meget af at min editor kan vise mig hvor den matchende parentes til den cursoren er på er, så jeg er sikker på at strukturen er som jeg forventer.

Jeg kan ikke se den samme overskuelighed uden parenteser og tuborg.

Henrik Dalsager

acc = reduce(comb, 0, map(\chunk -> reduce(comb, 0, map(f, a)), splitInto(J, a)))

I min optik er det at man overhovedet synes at det her udtryk er gangbart lige præcist problemet.

Målgruppe analyse
Hvem er målgruppen for koden? Ja jeg ved godt det er lidt et humanistisk begreb at smide ind, men helt ærligt. Skriver vi kode for computerens skyld?

Som programmør der primært skriver kode til software, som ingeniører bruger, eller som ingeniører skal lave ændringer i, må jeg dagligt indse at det ofte er vigtigere at mennesker kan læse koden, end at computere kan læse koden.

Problemet er at mennesker i almindelighed ikke tænker og forstår tekst på den mpde som funktionel programmering ligger op til. Jeg elsker personligt lambda-udtryk, og regular expressions, men det er godt nok de færreste (uden en datalog i maven) der kan læse et regular expression og forstå hvad det gør. Lamda udtryk er der lidt flere der forstår da det er en gængs notation fra matematikken, som flere ingeniører kender.

Så hvis de skal have en chance for at forstå/vedligholde min kode, ville den slags næsten altid gøre at der som minimum skulle være en forklarende kommentar om hvad der foregår, og hvorfor. Hvis vi tager de to orignale eksempler der skulle vise hvor meget smartere det funktionelle udtryk er, vil jeg gøre opmærksom på at i praksis ville koden (inklusiv konteksten) se sådan her ud.

/*  
*  Transformer og akkumuler indholdet af A:  
* "map" anvender f() på alle elementer i A, for at transformere hvert enkelt tal,   
*  derefter kombineres alle resultaterne, og foldes sammen til acc.  
*/  
acc = fold(comb, 0, map(f, A));

Hvis jeg skrev det andet eksempel, ville stort set alle kunne forstå koden uden kommentarer. (måske skulle comb lige have en med på vejen)

acc = 0    
for (int i = 0; i < size(A); i++) {    
  acc = comb(acc, f(A[i])) ; //kombinerer resultateterne af f med acc  
} 

Det kan de kun fordi for loopet er så almindeligt, at det stort set er i alle de programmeringssprog som folk ser.

Det ville være endnu bedre hvis man kunne skrive programmet som tekst. Uanset at vi nok ville være nødt til at lære et ret præcist engelsk:

"apply $f on all elements in $A, then combine the outputs and save the results in $acc"

Mikkel Knudsen

Jeg synes godt man kan forvente, at en programmør kan sætte sig ind i, hvad henholdsvis map og fold gør.

Så outreret er funktionel programmering altså heller ikke.

Baldur Norddahl

Skriver vi kode for computerens skyld?

Ja helt ærlig, men det er altså det primære formål med at skrive kode. Det skal blive til noget en computer forstår og kan eksekvere.

Læsbarhed og andre egenskaber ved koden er metode. Man kan vælge at skrive koden særlig læsbar, såfremt det er vigtigt for den måde du udvikler. Der er også anvendelser hvor det er lige meget (hvis det ikke skal bruges igen eksempelvis). I praksis skal du have et kompromis mellem læsbarhed og andre faktorer, som udviklingshastighed og garantier for korrekt kode (eksempelvis et typesystem).

Dit eget eksempel sidst i indlægget viser hvordan læsbarhed ikke alene fører til et brugbart sprog. Det vil simpelthen være alt for kluntet at skrive sådan og det vil gå ud over kodernes produktivitet.

Niels Gustav Westphal Serup

Selvfølgelig kan man gå for meget amok med at introducere og bruge nye, smarte funktioner og notationer (se bare hvad der sker når man slipper rene matematikere løs), men hvis man oplever et mønster igen og igen i sin kode, giver det til alle tider mening at kunne beskrive det mønster så koncist som muligt.

Derfor findes fold, map, filter, scan og andre specielle funktioner. De er ikke kun i farlige sprog som Haskell, men også i nuttede børnesprog som fx Python.

Det vigtige ved kode er rigtignok at fremtidige mennesker skal kunne forstå det. Hvis jeg levede i 2116 og fik som opgave at forstå 100 år gammel kode, ville jeg foretrække kode der ikke gentog sig selv unødigt.

Det er ligesom hvis man går på McDonald's, og personen ved kassen fortæller dig præcis hvordan burgeren bliver lavet i stedet for bare at sige at det er en burger -- det er okay første gang, da jeg på den led lærer hvordan en burger er opbygget, men resten af gangene er jeg egentlig kun interesseret i at det bare er en burger.

Troels Henriksen

I min optik er det at man overhovedet synes at det her udtryk er gangbart lige præcist problemet.

Jeg har desværre for travlt til at komme med en fyldestgørende redegørelse lige nu, men jeg kan da gøre min påstan angående denne transformation lidt tydeligere. Det oprindelige program, skrevet for læsbarhed, ser sådan ud:

acc = reduce(comb, 0, map(f, a))

Hvis man kan det funktionelle programmeringssprog, så kan man også forstå dette. Selvfølgelig skal man lære sproget først - men omvendt, så er det da også underligt at imperative sprog benytter '=' som tildeling, når alle har lært i folkeskolen at det er et udsagn om lighed! Alle programmeringssprog skal læres før programmer skrevet i dem er forståelige.

Under alle omstændigheder gik mit argument ikke på læsbarhed - det gik på at koncisionen tillader transformationer. I ovenstående tilfælde er det en optimering, som man kan anvende hvis den oprindelige kode viser sig ikke at køre hurtigt nok. Hele transformationen kan også pakkes ind i mere letforståelige hjælpefunktioner (en funktion man kan kalde parFold eller hvad man nu vil). Det ændrer ikke på min centrale tese, som var at koncision gør det muligt at fokusere på det essentielle i beregningen, hvilket letter både manuel og automatisk transformation.

Maciej Szeliga
Torben Mogensen Blogger

Er det ikke så'n ca. hvad APL drejede sig om ? ;-)

For jeg er da helt sikker på at jeg ikke er den eneste her som har set et APL tastatur.

Det er ikke separatortegn, APL bruger sit særlige tegnsæt til. Det er operatortegn til f.eks. matrixmultiplikation, sortering, reduktion af vektorer osv. Ideen var/er dels at sikre, at alle operatorer er et enkelt symbol, så man ikke behøver blanktegn eller andre separatortegn, og dels for at undgå at bruge det samme tegn med flere forskellige betydninger (udover, at f.eks. + er overloadet til at virke på tegn, vektorer og matricer).

Hans Schou

Er det brainfuck det der Carsten?


Ja, og det er hurtigere at installere en fortolker, end det er at se på koden hvad den gør.

$ echo '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.' | hsbrainfuck   
Hello World!

Det er meget kompliceret at gennemskue hvad programmet gør. Hvis der er malware i det, tager det lang tid at opdage.

Log ind eller opret en konto for at skrive kommentarer