Skip to content

Instantly share code, notes, and snippets.

@denkspuren
Last active November 29, 2021 13:00
Show Gist options
  • Save denkspuren/5430333040803ec8b95ab10b85752c5e to your computer and use it in GitHub Desktop.
Save denkspuren/5430333040803ec8b95ab10b85752c5e to your computer and use it in GitHub Desktop.
Fibers für Consize

Fibers für Consize

Ein in Arbeit befindliches Konzeptdokument

Inspiriert durch die Skriptsprache Wren ist die Idee entstanden, Fibers in Consize einzubauen. Fibers (siehe Wikipedia) kann man als eine Implementierung von Coroutinen ansehen, eine Form leichtgewichtiger Threads mit deteministischem Verhalten.

Fibers und ein Fiberstack

Eigentlich sind Fibers ein naheliegendes Konzept für Consize, denn eine Continuation, also ein Rechenzustand aus Data- und Callstack, ist fundamental für Consize. Ich habe dafür in der Vergangenheit schon oft diese Notation dafür gewählt: Ein Stapel [ @S ] und eine Quotierung [ @Q ] im Verbund stellen eine Fiber dar, [| @S | @Q |].

Die verwendete Notation wird ausführlich in der Consize-Documentation (Anhang B, Version 2017) erläutert.

[ @S ] [ @Q ] fiber -> [| @S | @Q |]
[| @S | @Q |] unfiber -> [ @S ] [ @Q ]

Aufruf, Suspendierung und Ergebnisrückgabe

Der erste Designvorschlag zum Aufruf (call/f) einer Fiber legt die aktuelle Continuation als Fiber auf einem neu und eigens dafür eingeführten Fiberstack ab, der sich "unter" dem Datastack befindet. Auf dem Datastack muss sich genau ein Datenelement #D befinden, das als Argument auf dem Stapel der Fiber abgelegt wird.

@F | @DS #D [| @S | @Q |] | call/f @CS
=> @F [| @DS | @CS |] | @S #D | @Q

Dieser Entwurf geht davon aus, dass eine Fiber immer einen Datenwert #D beim Aufruf erwartet und ebenso einen Datenwert bei ihrer Unterbrechung zurückgibt. Wenn kein Datenwerte übergeben oder zurückgegeben wird, so ist mit Dummywerten zu arbeiten, die durch ein drop entfernt werden.

Die Selbstunterbrechung einer Fieber durch yield und die damit einhergehende Suspendierung (Stilllegung) aktiviert wieder die oberste Fiber auf dem Fiberstack. Der Datenwert #D wird an die reaktivierte Fiber übergeben.

@F [| @S | @Q |] | @DS #D | yield @CS
=> @F [| @DS | @CS |] | @S #D | @Q

@F [| @S | @Q |] | @DS #D | yield
=> @F | @S #D | @Q

@F [| @S | @Q |] | @DS #D | 
=> @F | @S #D | @Q

Frage: Wie wird eine Fiber beendet, ohne dass sich an einer Reaktivierung versucht wird? Schon oben konnte ein Fiber mit einer leeren Quotierung aufgerufen werden. Das ist nicht brauchbar. Würde ein fiber-done? etwas nützen?

[| @S |       |] | fiber-done? -> t
[| @S | #C @Q |] | fiber-done? -> f

: fiber-done? unfiber swap drop isEmpty?

Wiederaufnahme einer Fiber

Um eine Fiber aufzurufen, muss sie zunächst mit resume vom Fiberstapel geholt werden.

@F [| @D | @Q |] | @DS | resume @CS
=> @F | @DS [| @S | @Q |] | @CS

Zur Erinnerung: call/cc und continue

Es heißt, dass man mit call/cc Fiber bzw. Coroutinen implementieren können soll. Das scheint mir ein ziemlicher Aufwand zu sein.

@DS [ @Q ] | call/cc @CS =>
[ @DS ] [ @CS ] | @Q

[ @DS ] [ @CS ] | continue =>
@DS | @CS

Kann man call/cc mit Fibern umsetzen?

Kann eine Fiber innerhalb ihrer selbst ein call/cc umsetzen? Und kann das mit Hilfe einer zweiten Fiber realisiert werden ohne ein primitives Wort einzuführen?

[| @D | @C |] [ @Q ] call/cf

: call/cf [ unfiber ] dip call
: continuef [ fiber ] dip

Das ist so noch nicht lauffähig.

Fiber-Primitive in Wren

Wren kennt die folgenden Primitive, siehe wren_core.c: new, abort, current, suspend, yield, call, error, isDone, transfer, transferError, try.

PRIMITIVE(vm->fiberClass->obj.classObj, "new(_)", fiber_new);
PRIMITIVE(vm->fiberClass->obj.classObj, "abort(_)", fiber_abort);
PRIMITIVE(vm->fiberClass->obj.classObj, "current", fiber_current);
PRIMITIVE(vm->fiberClass->obj.classObj, "suspend()", fiber_suspend);
PRIMITIVE(vm->fiberClass->obj.classObj, "yield()", fiber_yield);
PRIMITIVE(vm->fiberClass->obj.classObj, "yield(_)", fiber_yield1);
PRIMITIVE(vm->fiberClass, "call()", fiber_call);
PRIMITIVE(vm->fiberClass, "call(_)", fiber_call1);
PRIMITIVE(vm->fiberClass, "error", fiber_error);
PRIMITIVE(vm->fiberClass, "isDone", fiber_isDone);
PRIMITIVE(vm->fiberClass, "transfer()", fiber_transfer);
PRIMITIVE(vm->fiberClass, "transfer(_)", fiber_transfer1);
PRIMITIVE(vm->fiberClass, "transferError(_)", fiber_transferError);
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
PRIMITIVE(vm->fiberClass, "try(_)", fiber_try1); 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment