Ich habe weder einen Doktortitel noch einen anderen Abschluss, weder in CS noch in Mathematik noch in einem anderen Bereich. Ich habe keine Vorkenntnisse mit Scala oder einer ähnlichen Sprache. Ich habe keine Erfahrung mit Systemen, die auch nur aus der Ferne vergleichbar sind. In der Tat, die einzige Sprache , die ich mehr als nur eine oberflächliche Kenntnis von der selbst hat eine Art System ist Pascal, nicht gerade für sein hoch entwickelten Typ - System bekannt. (Es gibt zwar Bereichstypen, die AFAIK so gut wie keine andere Sprache hat, aber das ist hier nicht wirklich relevant.) Die anderen drei Sprachen, die ich kenne, sind BASIC, Smalltalk und Ruby, von denen keine sogar ein Typensystem hat.
Und dennoch habe ich keinerlei Probleme, die Signatur der von map
Ihnen geposteten Funktion zu verstehen . Es sieht für mich so ziemlich nach der gleichen Signatur aus, map
die ich in jeder anderen Sprache gesehen habe. Der Unterschied ist, dass diese Version allgemeiner ist. Es sieht eher nach einer C ++ STL-Sache aus als beispielsweise nach Haskell. Insbesondere wird vom konkreten Auflistungstyp abstrahiert, indem nur das Argument verlangt wird IterableLike
, und vom konkreten Rückgabetyp wird abstrahiert, indem nur eine implizite Konvertierungsfunktion benötigt wird, die etwas aus dieser Sammlung von Ergebniswerten aufbauen kann . Ja, das ist ziemlich komplex, aber es ist wirklich nur ein Ausdruck des allgemeinen Paradigmas der generischen Programmierung: Nehmen Sie nichts an, was Sie eigentlich nicht müssen.
In diesem Fall muss die Sammlung map
nicht unbedingt eine Liste sein oder bestellt oder sortierbar sein oder ähnliches. Das einzige, was map
wichtig ist, ist, dass es nacheinander auf alle Elemente der Sammlung zugreifen kann, jedoch in keiner bestimmten Reihenfolge. Und es muss nicht wissen, was die resultierende Sammlung ist, es muss nur wissen, wie man sie erstellt. Das ist es also, was seine Typensignatur erfordert.
Also statt
map :: (a → b) → [a] → [b]
Dies ist die traditionelle Typensignatur, für die map
keine konkrete List
, sondern nur eine IterableLike
Datenstruktur erforderlich ist
map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j
Dies wird dann weiter verallgemeinert, indem nur eine Funktion benötigt wird, die das Ergebnis in die vom Benutzer gewünschte Datenstruktur konvertieren kann :
map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c
Ich gebe zu, dass die Syntax etwas klobiger ist, aber die Semantik ist dieselbe. Grundsätzlich geht es von
def map[B](f: (A) ⇒ B): List[B]
Das ist die traditionelle Signatur für map
. (Beachten Sie, dass der Eingabelistenparameter aufgrund der objektorientierten Natur von Scala verschwindet, da er nun der implizite Empfängerparameter ist, den jede Methode in einem OO-System mit einem Versand hat.) Dann wurde er von einem konkreten List
zu einem allgemeineren verallgemeinertIterableLike
def map[B](f: (A) ⇒ B): IterableLike[B]
Jetzt ersetzt es die IterableLike
Ergebnissammlung durch eine Funktion, die wirklich fast alles erzeugt.
def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
Was ich wirklich glaube, ist nicht so schwer zu verstehen. Es gibt wirklich nur ein paar intellektuelle Werkzeuge, die Sie benötigen:
- Sie müssen (ungefähr) wissen, was
map
ist. Wenn Sie nur die Typensignatur ohne den Namen der Methode angeben würden, wäre es viel schwieriger herauszufinden, was los ist. Da Sie jedoch bereits wissen, was map
zu tun ist, und wissen, wie die Typensignatur lauten soll, können Sie die Signatur schnell scannen und sich auf die Anomalien konzentrieren, z. B. "Warum werden map
zwei Funktionen als Argumente verwendet, nicht eine?"
- Sie müssen in der Lage sein, die Typensignatur tatsächlich zu lesen . Aber selbst wenn Sie Scala noch nie gesehen haben, sollte dies recht einfach sein, da es sich tatsächlich nur um eine Mischung von Typensyntaxen handelt, die Sie bereits aus anderen Sprachen kennen: VB.NET verwendet eckige Klammern für den parametrischen Polymorphismus und verwendet einen Pfeil, um das zu kennzeichnen Rückgabetyp und ein Doppelpunkt zur Trennung von Name und Typ sind eigentlich die Norm.
- Sie müssen ungefähr wissen, worum es bei der generischen Programmierung geht. (Was nicht so schwer herauszufinden ist, da im Grunde alles im Namen geschrieben steht: Es wird buchstäblich nur generisch programmiert).
Keiner dieser drei sollte einem professionellen oder sogar Hobby-Programmierer ernsthafte Kopfschmerzen bereiten. map
war eine Standardfunktion in so ziemlich jeder Sprache, die in den letzten 50 Jahren entwickelt wurde. Die Tatsache, dass verschiedene Sprachen unterschiedliche Syntax haben, sollte für jeden offensichtlich sein, der eine Website mit HTML und CSS entworfen hat und Sie können nicht einmal eine Remote-Programmierung abonnieren verwandte Mailingliste ohne einen nervigen C ++ - Fanboy aus der Kirche St. Stepanov, der die Vorzüge der generischen Programmierung erklärt.
Ja, Scala ist komplex. Ja, Scala verfügt über eines der fortschrittlichsten Systeme, die dem Menschen bekannt sind und mit Sprachen wie Haskell, Miranda, Clean oder Cyclone konkurrieren und diese sogar übertreffen. Aber wenn Komplexität ein Argument gegen den Erfolg einer Programmiersprache wäre, wäre C ++ längst gestorben und wir würden alle Scheme schreiben. Es gibt viele Gründe, warum Scala höchstwahrscheinlich nicht erfolgreich sein wird, aber die Tatsache, dass Programmierer sich nicht die Mühe machen können, ihr Gehirn einzuschalten, bevor sie sich vor die Tastatur setzen, wird wahrscheinlich nicht der Hauptgrund sein.