Scheme Konventionen


Gestern ist mir beim Durcharbeiten einiger Beispiele aus einer Vorlesung, die ich aufgrund von Krankheit verpasst hatte, eine Kleinigkeit im Skript aufgefallen, eine Benennungskonvention (das Ausrufezeichen im Bezeichner), die mir vorher nicht bekannt war und die unser Professor auch nicht erwähnt hatte. Auf der Suche nach dem Hintergrund habe ich mithilfe von Google einige Regeln gefunden, die mir so teilweise nicht bekannt waren. Also dachte ich mir, fasse ich sie hier doch einmal zusammen. Falls jemand noch weitere Konventionen kennt, freue ich mich natürlich über Kommentare 🙂

Natürlich gilt vorweg das, was für jeden anständigen Programmierer gilt. Das ist in erster Linie, dass man lesbar Programmiert, z.B. indem man kurze, prägnante aber gleichzeitig sprechende Namen für seine Funktionen wählt, seine Programme sauber kommentiert, etc. Hier soll vor allem auf die Besonderheiten bei Scheme eingegangen werden.

1

Bezeichner ausschließlich kleinschreiben!
Scheme ist bis Spezifikation R5RS case insensitive, allerdings gibt es einige Implementierungen, die sich nicht daran halten, und sich case sensitive verhalten. Um die Verwirrung noch größer zu machen, ist seit R6RS die Groß- und Kleinschreibung auf einmal doch nicht mehr egal. Um also auf der sicheren Seite zu sein, bietet es sich an, alle Bezeichner klein zu schreiben. So bleibt der Sourcecode sowohl Abwärtskompatibel als auch Kompatibel zwischen verschiedenen Implementationen.

So sollte man es machen

display
value
pi

So sollte man es nicht machen

Display
Value
PI

2

Zusammengesetzte Bezeichner mit Minus (-) als Trennzeichen!
Binnenmajuskel (auch genannt CamelCase) ist wegen der ersten Konvention nicht möglich. Daher wird ein Minus als Trennzeichen zwischen zwei Worten eines zusammengesetzten Bezeichners ein Minus gesetzt.

So sollte man es machen

write-char
string-append

So sollte man es nicht machen

writechar
writeChar
WriteChar
write_char

3

Konvertierende Prozeduren mit einem Minus-Größer-Pfeil (->) im Bezeichner
Bei Prozeduren, die von einer Einheit/Sorte/… in eine andere Umrechnen, wird ein -> im Bezeichner verwendet, um dies zu zeigen. Der stilisierte Pfeil steht zwischen den Bezeichnern, zwischen denen er konvertiert.

So sollte man es machen

string->list
celsius->fahrenheit

So sollte man es nicht machen

string2list
fromstringtolist
string-to-list

4

Prädikate enden mit einem Fragezeichen (?) im Bezeichner
Bei Prozeduren, die prüfen, ob ein bestimmter Wert bestimmten Kriterien entspricht, und die einen Booleschen Wert zurückgeben (wahr oder falsch), spricht man von Prädikaten. Um ersichtlich zu machen, dass es sich um ein Prädikat handelt, wird der Bezeichner so gewählt, dass ersichtlich ist, welches Kriterium getestet wird. Dieser wird mit einem Fragezeichen abgeschlossen

So sollte man es machen

positive?
empty?
boolean?
string?

So sollte man es nicht machen

is-it-positive
a-number
test-for-string

5

Zuweisungen in Umgebungsvariablen enden auf Ausrufezeichen (!)
Wenn eine Prozedur den Wert einer Variable in einer Umgebung ändert, dann endet der Bezeichner auf ein Ausrufezeichen (!).

So sollte man es machen

set!
append!
account-deposit!

So sollte man es nicht machen

set-variable
append
change-account-amount

6

Leerzeichen wie in Sprachen üblich setzten.
Ähnlich wie man in der Deutschen Sprache plenken kann, kann man dies auch in Scheme. Es gelten insbesondere die Standardregeln bezüglich Klammersetzung, d.h. vor einer öffnenden Klammer folgt ein Leerzeichen in einer Klammer gibt es kein Leerzeichen. Zwischen Bezeichnern wird nicht mehr als ein Leerzeichen verwendet.

So sollte man es machen

(foo (bar baz) quux)
(+ (* 3 (+ 2 3)) 10 (/ 10 1))

So sollte man es nicht machen

(foo(bar baz)quux)
( foo ( bar baz ) quux )
( +(*3 (   +2 3 ) )10(/10    1) )

7

Leerzeile auf aufeinanderfolgenden schließenden Klammern
Wenn mehrere schließende Klammern aufeinander folgen, wird nach der letzten schließenden Klammer zur Übersichtlichkeit ein Zeilenumbruch durchgeführt.

So sollte man es machen

 (let ((a 3)
(b (+ 10 12))
(c (lambda (n) (* n 2))))
(c (+ a b)))

(+ (* 3 (+ 2 3))
10 (/ 10 1))

So sollte man es nicht machen

(let ((a 3)
(b (+ 10 12)) (c (lambda (n) (* n 2)))) (c (+ a b)))

(+ (* 3 (+ 2 3)) 10 (/ 10 1))

8

Zu einem Operator gehörende Operanden werden an vertikal Ausgerichtet
Aufgrund von Konvention Nr. 7 können zu einer Prozedur gehörende Argumente auf mehrere Zeilen verteilt werden. In einem solchen Fall bekommt jeder Operand seine eigene Zeile. Zur besseren Lesbarkeit wird jeder Operand vertikal an der Stelle des ersten Operanden ausgerichtet.
In dem Beispiel wird in Zeile 1 bis 4 ein let-Beispiel aufgeführt. Da der zweite Operand mit mehr als einer Klammer schließt, und noch weitere Operanden folgen, ist es hier angebraucht, und erhöht die Lesbarkeit. Deshalb wird auch schon nach dem ersten Operanden umgebrochen, da sonst der b-Ausdruck in der zweiten Zeile eventuell nicht als eigenständiges zweites Argument erkannt wird. Auch die Zuordnung der 10 (Zeile 7) zum + wird durch die Einrückung deutlicher.

So sollte man es machen

(let ((a 3)
      (b (+ 10 12))
      (c (lambda (n) (* n 2))))
  (c (+ a b)))

(+ (* 3 (+ 2 3))
   10 
   (/ 10 1))

So sollte man es nicht machen

(let ((a 3) (b (+ 10 12))
      (c (lambda (n) (* n 2))))
  (c (+ a 
        b)))

(+ (* 3 (+ 2 3))
        10 
             (/ 10 1))

9

Klammern stehen nie alleine oder werden voneinander getrennt!
Dies ist anders, als in vielen Programmiersprachen. Hier ein typisches Beispiel für Quelltext in Java:

package blog.examples;
public interface Article {
    String getName();
    void setContent(String aContent);
}

In C gibt es noch eine andere typische Einrückung von Klammern:

#include <stdio.h>
 
int main(void)
{
    printf("Hallo Welt!n");
    return 0;
}

In C oder Java sorgt dies dafür, dass der Sourcecode einfacher lesbar wird. Dadurch, dass Scheme sehr viel mehr Klammern hat, und die Klammern auch nicht dazu dienen, den Code zu strukturieren, sondern um Argumente an ihre Prozeduren zu binden, ist eine solche Einrückung in Scheme kontraproduktiv.

So sollte man es nicht machen

(let 
    (
     (a 3)(b 
           (+ 10 12)
          )
          (c (
              lambda (n) 
               (* n 2)
             )
          )
    )
    (c 
     (+ a b)
    )
)

(
 + (
    * 3 
      (+ 2 3)
      )
   10
   (
    / 10 1
      )
   )

Nachtrag
Einen hab ich noch – allerdings trifft der in dieser Form wahrscheinlich nur für den DrRacket-Dialekt, und da spezieller wahrscheinlich sogar nur für Die Macht der Abstraktion zu:

10

Benennung bei selbst-definierten Datentypen
Jeder selbst-definierte Datentyp wird in Scheme (Racket/DMdA) mit der Sonderform (define-record-procedures …) oder (define-record-procedures-parametric …) eingeführt. Dabei kann man die dazugehörigen Operationen beliebig benennen. Konvention ist es, dass der Konstruktor immer make-sortename genannt wird, das Prädikat entsprechend der Konvention 4 sortenname? und die Selektoren sortennamekomponente. Bei parametrisierten Records wird der Signatur-Konstruktor wie folgt benannt: sortenname-of.

So sollte man es machen

(define-record-procedures entry
  make-entry entry?
  (entry-key entry-value))

(define-record-procedures-parametric pair pair-of
  make-pair pair?
  (pair-first pair-second))

So sollte man es nicht machen

(define-record-procedures kuchen
  backe-kuchen gebaeck?
  (teig sahne frucht))
Advertisements

Please comment. I really enjoy your thoughts!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s