joetjen.net
EN DE
Programmiersprache

Beispiele

Etwas größere Stücke, jedes gegen v0.63.0 ausgeführt, mit der Ausgabe, die es erzeugt hat.

In Entwicklung · v0.63

Worthäufigkeiten

Eine Pipeline über die Standardbibliothek. Jeder Schritt nimmt Text oder Collection zuerst, deshalb fädelt -> ihn durch.

(defn top-words "The `n` most frequent words in `text`."
  [text n] :spec [string integer -> seq]
  (-> text
      string/lower-case
      (string/split " ")
      (seq/remove string/blank?)
      seq/frequencies
      map/to-pairs
      (seq/sort-by (fn [#(_word count)] (- count)))
      (seq/take n)))

(top-words "the cat and the hat and the bat" 2)
; => (#("the" 3) #("and" 2))

Validierung, die jeden Fehler meldet

Map-Destrukturierung in der Parameterliste, with für den Normalfall und result/collect, um jeden Fehler zu sammeln, statt beim ersten aufzuhören. Preise sind Dezimalzahlen, die Summe ist also exakt.

(defn- positive [field value]
  (if (> value 0)
    #(:ok value)
    #(:error (str field " must be positive"))))

(defn check-order [{:qty qty :price price}]
  (with [#(:ok checked) (result/collect [(positive "qty" qty) (positive "price" price)])]
    #(:ok {:qty (seq/first checked) :price (seq/nth checked 1) :total (* qty price)})))

(check-order {:qty 3 :price 1.25M})
; => #(:ok {:total 3.75M, :qty 3, :price 1.25M})
(check-order {:qty 0 :price -1})
; => #(:error ("qty must be positive" "price must be positive"))

Ein überwachter Actor

Let it crash, in Praxis: das Beispiel actors aus dem Repository, ohne seine Docstrings und Kommentare. Ein Zähler in einem eigenen Prozess, ein Supervisor, der ihn neu startet, und ein Programm, das ihn scheitern lässt und nichts davon behandelt.

src/actors/counter.prax
(ns actors.counter "A counter that lives in its own process." (:behaviour praxis.actor))

(defn init [from] :spec [integer -> tuple]
  #(:ok from))

(defn handle-call [_message _from state] :spec [any any integer -> tuple]
  #(:reply state state))

(defn handle-cast [message state] :spec [keyword integer -> tuple]
  (cond
    (= message :bump) #(:noreply (+ state 1))
    true (throw :asked-to-fail)))
src/actors/tree.prax
(ns ^{:platforms [:beam]} actors.tree "The supervisor over the counter." (:behaviour praxis.supervisor))
(require [praxis.supervisor :as sup])
(require [actors.counting :as _counting])

(defn start-link [] :spec [-> tuple]
  (sup/start-link actors.tree :ignored))

(defn init [_argument] :spec [any -> tuple]
  (sup/tree (sup/flags :one-for-one)
            [(sup/child actors.counting 0 {:id actors.counter})]))

Die Seite der Aufrufer, actors.counting, ist ein eigener Namespace, damit derselbe Zähler auch unter Node läuft: Ein Namespace, der wartet, wird dort zu Generatoren kompiliert, und ein Callback darf das nicht. Das Programm startet den Baum, erhöht den Zähler dreimal, schickt :boom und fragt erneut. prx run:

counted to 3, in #<pid 0.171.0>
asking it to fail...

21:55:24.212 [error] actors.counter stopped: #(:bad_return_value #(:asked-to-fail {}))
It was casting :boom
back at 0, in #<pid 0.172.0>
the program never handled the failure at all

Eine neue Pid unter demselben Namen ist der Supervisor bei der Arbeit, und der Zähler steht wieder bei 0, weil ein neu gestartetes Kind wieder bei seinem Argument beginnt: Dem Zustand eines gescheiterten Prozesses ist nicht zu trauen.

Eine Quelle, zwei Maschinen

Das portable Beispiel im Repository ist ein einziges Programm, zweimal ausgeführt — prx run auf der BEAM, und prx compile --target js --suite node gefolgt von Node — mit einem diff der beiden Ausgaben. Seine vierzehn Zeilen sind die Stellen, an denen zwei Maschinen am leichtesten auseinanderlaufen: Brüche, Dezimalzahlen, Ganzzahlen jenseits von 2⁵³, eine Ordnung über alle Arten von Werten, Bitstrings, und wo eine Zeichenkette aufhört, ein Binary zu sein. Der JavaScript-Build schreibt ein ES-Modul pro Namespace, eine Source-Map und aus den Specs erzeugte TypeScript-Deklarationen. Über die gesamte Konformitäts-Suite stimmen BEAM und Node in 1031 von 1031 Fällen überein.