Fandom

Mini-Wikia

Fantom-DE/Kurze Tour

< Fantom-DE

1.771Seiten in
diesem Wiki
Seite hinzufügen
Diskussion0 Share

Störung durch Adblocker erkannt!


Wikia ist eine gebührenfreie Seite, die sich durch Werbung finanziert. Benutzer, die Adblocker einsetzen, haben eine modifizierte Ansicht der Seite.

Wikia ist nicht verfügbar, wenn du weitere Modifikationen in dem Adblocker-Programm gemacht hast. Wenn du sie entfernst, dann wird die Seite ohne Probleme geladen.

Fantom im Schnelldurchgang

Die Sprache Fantom hat viele Ähnlichkeiten mit Java oder C#. Wenn Sie sich mit einer diese Sprachen etwas auskennen, werden Sie auch mit Fantom schnell zurecht kommen. Diese Seite zeigt Ihnen in Kürze, wie die Sprache »funktioniert«, worin sie sich von Java oder C# unterscheidet und welche interessanten zusätzlichen Fähigkeiten sie aufweist.

Hallo Welt

Wir beginnen unsere Wirbelwind-Tour durch die Besonderheiten von Fantom mit dem unvermeidlichen Hello World:

class HelloWorld
{
  static Void main()
  {
    echo("hello world")
  }
}

Es gibt einige kleine Unterschiede zu Java oder C#; hier sind die wichtigsten von ihnen:

  • Alle Typnamen fangen mit Großbuchstaben an, auch Void (in Fantom gibt es weder primitive Typen noch die zugehörigen Schlüsselwörter).
  • Wenn nicht anders angegeben, sind Klassen und Methoden öffentlich.
  • Fantom bietet eine Methode echo für Ausgaben auf der Konsole; alternativ kann man Env.cur.out verwenden.
  • Anweisungen können durch Zeilenwechsel abgeschlossen werden (man kann aber auch jede Anweisung mit einem Semikolon abschließen).
  • Um auf die Aufrufargumente zuzugreifen, können Sie Str[] args deklarieren oder Env.args verwenden.

Literale

Fantom unterstützt dieselben Literale, wie sie in Java und C# für primitive Typen verwendet werden, sowie einige zusätzliche, die man typischerweise von einigen höheren Skriptsprachen her kennt:

Beispiel Typ Bedeutung
true Bool Boolescher Wert
123 Int 64-Bit-Ganzzahl
0xcafe_babe Int Ganzzahl in Hex-Notation, bei Bedarf mit Unterstrich (_) als Separator
'\n' Int einzelnes Zeichen
"hi" Str Zeichenkette
3.4f Float 64-Bit-Fließkommazahl
3.4d Decimal Dezimalzahl mit hoher Genauigkeit
5sec Duration Zeitdauer
`/dir/file.txt` Uri URI
[0, 1, 2] List Liste
[,] List leere Liste
[1:"one", 2:"two"] Map Map (bzw. assoziatives Array)
[:] Map leere Map
Int# Type Typ als Reflection-Objekt
Int#plus Slot Slot als Reflection-Objekt

Ausdrücke

Fantom verwendet im Wesentlichen dieselbe Syntax für Ausdrücke wie Java und C#:

obj.toStr()   // Aufruf der Methode toStr
obj.toStr     // Klammern sind optional, wenn es keine Argumente gibt
obj.field     // Zugriff auf ein Feld
x && y        // logisches UND
x === y       // Gleichheit der Referenz (Identität)
x == y        // Abkürzung für x.equals(y)
x < y         // Abkürzung für x.compare(y) < 0
x <=> y       // Abkürzung für x.compare(y)
x + y         // Abkürzung für x.plus(y)
x = y         // Wertzuweisung
a ? b : c     // ternärer Operator
a is Str      // Instance-of-Operator (Typprüfung)
a isnot Str   // entspricht !(a is Str)
a as Str      // wie as-Operator in C#
a?.func()     // sicherer Aufruf (wie in Groovy)
a ?: b        // Elvis-Operator anstelle von a != null ? a : b

Die meisten Operatoren sind nur Abkürzungen für Methodenaufrufe. Beispiel:

3 + 4  =>  3.plus(4)

Strings

Strings können interpoliert werden, d.h. man kann durch das Dollarzeichen ($) gekennzeichnete Ausdrücke einfügen:

// String-Interpolation:
"$x + $y = ${x+y}"
 
// dasselbe in Langform:
x.toStr + " + " + y.toStr + " = " + (x+y).toStr

Einer einfachen Variablen oder einem Ausdruck mit Punkten braucht nur ein $ vorangestellt werden; kompliziertere Ausdrücke werden zusätzliche in geschweifte Klammern eingeschlossen.

Anweisungen

Anweisungen unterscheiden sich größtenteils nicht von Java oder C#:

Str s := "hello"    // lokale Deklaration einer Variablen
s := "hello"        // Typinferenz
if (x) {} else {}   // if/else-Anweisunge
while (x) {}        // while-Schleife

Der größte sichtbare Unterschied besteht in der Verwendung von := für die Deklaration einer lokalen Variablen. Fantom arbeitet mit Typinferenz, daher kann man bei lokalen Veriablen die Typdeklaration weglassen. Der :=-Operator wird anstelle von = verwendet, um deutlich zu machen, dass hier Deklaration intendiert ist und nicht nur eine einfache Zuweisung. Dadurch werden z.B. Schreibfehler in Variablennamen vermieden.

Für Felder werden automatisch Zugriffsmethoden generiert, ohne dass dazu viele Worte nötig sind:

class Person
{
  Str name
  Int age
}

Das obige Code-Stück ist im Prinzip gleichbedeuten mit folgendem Java-Programm:

// Java
public class Person
{
  public String name() { return name; }
  public void name(String x) { name = x; }

  public int age() { return age; }
  public void age(int x) { age = x; }

  private String name;
  private int age;
}

Java- und C#-Programmierer quälen sich mit derartigem Code herum, weil man ja irgendwann einmal auf die Idee kommen könnte, später das Setzen oder Auslesen eines Felde abfangen zu wollen. In Fantom werden Felder immer über automatisch generierte Getter und Setter gesetzt, die man bei Bedarf überschreiben kann:

class Person
{
  Str name
  Int age { set { checkAge(val); &age = it } }
}

Das Schlüsselwort it bezeichnet den Wert, der zum Setzten des Feldes verwendet wird. Die Syntax &age gibt an, dass wir in den tatsächlichen Speicherort des Feldes age schreiben (und nicht den Setter verwenden) wollen.

Methoden

Zu den Argumente von Methoden können Vorgaberte festgelegt werden:

class Person
{
  Int yearsToRetirement(Int retire := 65) { return retire - age }

  Int age
}

Einmal-Methoden (gekennzeichnet mit once) berechnen ihr Ergebnis nur dann, wenn sie zum ersten Mal aufgerufen werden, und geben bei nachfolgenden Aufrufen nur noch einen den zwischengespeicherten Wert zurück:

once Str fullName() { return "$firstName  $lastName" }

Konstruktoren

Eine Besonderheit von Fantom ist, dass Konstruktoren benannte Methoden sind:

class Point
{
  new make(Int x, Int y) { this.x = x; this.y = y; }
  Int x
  Int y
}

// Wir machen einen Punkt
pt := Point.make(30, 40)    // Langform des Konstruktor-Aufrufs
pt := Point(30, 40)         // Kurzform des Konstruktor-Aufrufs

Konventionsgemäß wird der Primärkonstruktor make (mache) genannt, und weiteren Konstruktoren wird das Präfix make vorangestellt. Wenn Sie kenen Konstruktor schreiben, generiert stattdessen der Compiler einen und nennt ihn make.

Vererbung

Die Syntax für Vererbung sieht aus wie in C#:

class Tier
{
  virtual Void rede() { echo("Reden") }
}

class Hund : Tier
{
  override Void rede() { echo("Bellen") }
}

Achten Sie auf die Verwendung der Schlüsselwörter virtual und override. Anders als in Java müssen Methoden explizit als virtuell gekennzeichnet werden - Methoden müssen also extra dafür vorgesehen sein, überschrieben zu werden.

Mixins

Java und C# implementieren Mehrfachvererbung mit Hilfe von Interfaces. Fantom-Mixins sind wie Interfaces, können zusätzlich aber auch impementierte Methoden enthalten:

mixin Audio
{
  abstract Int volume
  Void incrementVolume() { volume += 1 }
  Void decrementVolume() { volume -= 1 }
}

class Television : Audio
{
  override Int volume := 0
}

Am besten erklärt man, was das obige Fantom-Programm macht, in dem man es auf sein Java-Gegenstück abbildet:

// Java
interface Audio
{
  int volume();
  void volume(int volume);
  void incrementVolume();
  void decrementVolume();
}

class AudioImpl
{
  static void incrementVolume(Audio self) { self.volume(self.volume() + 1); }
  static void decrementVolume(Audio self) { self.volume(self.volume() - 1); }
}

class Television implements Audio
{
  int volume() { return volume; }
  void volume(int x) { volume = x; }

  void incrementVolume() { AudioImpl.incrementVolume(this); }
  void decrementVolume() { AudioImpl.incrementVolume(this); }

  private int volume = 0;
}

Achten Sie auf die Verwendung des abstrakten Feldes. Wie Interfaces können auch Mixins keinen Zustand halten. Allerdings können Mixins abstrakte Felder deklarieren, durch die zwar Getter und Setter, aber kein wirklicher Speicherbereich definiert werden.

Closures

Fantom unterstützt Funktionen und Closures als Erste-Klasse-Objekte; die Standard-APIs machen starken Gebrauch von funktionaler Programmierung. Die Syntax von Closures sieht ein wenig wie Ruby aus. Beispielsweise werden Iterationen über Kollektionen fast immer mit Hilfe von Closures realisiert:

// Eine Liste mit Strings ausgeben
list := ["Rot", "Gelb", "Orange"]
list.each |Str color| { echo(color) }
 
// Die Zahlen 0 bis 9 ausgeben.
10.times |Int i| { echo(i) }

Die Signatur einer Closure-Funktion kann in der Regel aus ihrem Kontext ermittelt werden:

10.times |i| { echo(i) }

It-Blöcke sind eine spezielle Form der Closure mit einem impliziten Parameter it:

10.times { echo(it) }

Häufig wird eine Closure dazu verwendet, ein Stück Code an eine Standardmethode zu übergeben:

// Eine Liste von Dateien nach ihrem Zeitstempel sortieren
files = files.sort |a, b| { a.modified <=> b.modified }

In Wirklichkeit ist eine Closure nur ein Ausdruck, der eine Instanz der Klasse Funk erzeugt:

// Eine Funktion, die zwei Integer-Zahlen addiert
add := |Int a, Int b->Int| { return a + b }
nine := add(4, 5)

Dynamisches Programmieren

Fantom bietet einige wichtige Möglichkeiten, mit denen man sich bei Bedarf von den Fesseln der strengen Typisierung befreien kann. Wenn man auf ein Feld oder eine Methode mit dem Punkt-Operator (.) zugreift, führt der Compiler eine Typprüfung durch. Sie können aber stattdessen auch den dynamischen Aufrufoperator \-> verwenden und damit die Typprüfung umgehen:

obj->foo         // obj.trap("foo", [,])
obj->foo(2, 3)   // obj.trap("foo", [2, 3])
obj->foo = 7     // obj.trap("foo", [7])

In Wirklichkeit ist der Operator \-> nur eine Abkürzung für den Aufruf der Methode Obj.trap, die standardmäßig mit Hilfe von Reflection eine Methode aufruft oder auf ein Feld zugreift. Sie können aber auch die Methode trap überschreiben, und auf diese Weise phantasievoll mit dynamischen Aufrufen umgehen.

Eine andere Eigenheit, die dynamisches Programmieren erleichtert, ist die Verwendung von Obj als Joker. Ein Obj können sie allem zuweisen oder ohne explizites Casten einem Methodenaufruf übergeben:

Obj obj
Str s := obj->foo     // Str s := (Str)obj->foo
Int.fromStr(obj)      // Int.fromStr((Str)obj)

»Nullbare« Typen

Ob ein Typ den Wert null annehmen kann, muss explizit durch ein an den Typbezeichner angehängtes ? festgelegt werden. Einen solchen Typ bezeichnen wir als >>nullbar<<. Ein nicht-nullbarer Typ kann also niemals den Wert null annehmen:

Str   // kann niemals null sein
Str?  // kann null sein

Der Compiler verhindert offensichtliche Fehler wie die Verwendung des null-Literals, wenn ein nicht-nullbarer typ erwartet wird. Zusätzliche Prüfungen werden implizit zur Laufzeit durchgeführt, wenn versucht wird, einen nullbaren Typ auf einen nicht-nullbaren Typ abzubilden. Dadadurch ist es möglich, Nullfehler an der Stelle abzufangen, an der sie eingeführt werden, anstatt sie sich in Programmteile fortpflanzen zu lassen, die damit nicht zu tun haben.

Serialisierung

Die Syntax für die Serialisierung von Fantom ist eine Untermenge der Programmiersprache selbst. Das heißt, Sie können Serialisierung verwenden, um beliebig komplexe Strukturen im Code aufzubauen. Und außerdem erleichtert es uns menschlichen Wesen, serialisierte Objekte zu lesen und zu schreiben:

@serializable class Person
{
  Str name
  Int age
  Person[]? children
}

// Baumstruktur aus Objekten im Code
//(oder aus einer Datei) aufbauen
homer := Person
{
  name = "Homer Simpson"
  age  = 39
  children =
  [
    Person { name = "Bart";   age = 7 },
    Person { name = "Lisa";   age = 5 },
    Person { name = "Maggie"; age = 1 }
  ]
}

// Serialisierte Struktur auf der Konsole ausgeben
Env.cur.out.writeObj(homer, ["indent":2])

Immutable Objekte

Man kann eine Klasse als const deklarieren, wenn sie immutabel sein soll:

const class Point
{
  new make(Int x, Int y) { this.x = x; this.y = y }
  const Int x
  const Int y
}

Const-Klassen enthalten nur Felder, die ihrerseits const sind und im Konstruktor initialisiert werden. Um zu vermeiden, dass es über Threads hinweg gemeinesame Zustände gib, müssen statische Felder immer auch const sein:

const static Point origin := Point(0, 0)

Listen und Maps kann man als immutabel deklarieren, indem man die Methode toImmutable anwendet:

vowels := ['a','e','i','o','u'].toImmutable

Aktoren

Fantom enthält ein Aktor-Framework für den Aufbau von nebenläufigen Anwendungen:

// Aktor starten, der eine Int-Nachricht asynchron hochzählt
actor := Actor(group) |Int msg->Int| { msg + 1 }
  
// Nachrichten an den Aktor senden und auf die Ergebnisse warten
for (i:=0; i<5; ++i) echo(actor.send(i).get)

Auch bei Fandom

Zufälliges Wiki