Skip to content

Commit

Permalink
Implement Reactor.open (non-isolate connectors).
Browse files Browse the repository at this point in the history
Add some docstrings.
README fixes.
  • Loading branch information
patternspandemic committed Aug 23, 2018
1 parent 332aad4 commit 20a0941
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 6 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ While these abstractions form an integration of traditional actor model and func

* Lack of multiple message entry points. Separate protocols handled within an actor must be encoded in a single message-handling construct, and thus need to be aware of each other. Multiple message entry points are required for message protocol isolation.
* Inability to await specific combinations of messages. A `receive` block cannot suspend until some multitude of message types arrive. This is often skirted in basic actor implementations by storing and testing for message states, or through the use of futures/promises, but obviously increases complexity. A requirement for the expression of multi-party message protocols.
* An actor's `receive` is a static construct and not first class. It cannot be passed to and returned from function, a requirement for message protocol composition.
* An actor's `receive` is a static construct and not first class. It cannot be passed to and returned from functions, a requirement for message protocol composition.

It is surmised here, that Pony's specific implementation of the actor model allows for a Reactors framework to be built atop it, thereby providing an approach to overcome the difficulties of reuse and protocol composition, and paving the way to build a protocol stack of reusable distributed computing components in Pony.

Pony absolves itself from the above limitations in the following way:

* Pony includes multiple message entry points in its implementation by way of actor behaviors. Generic behaviors may then form the basis needed for protocol isolation.
* While Pony cannot await specific combinations of messages sent to behaviors, it is further surmised that, just like a Reactor, a Pony actor can use internal event stream composition to essentially await any combination of messages, and avoid the need for a dedicated multi-receive construct.
* Message receives by way of behaviors can be made first class through partial application, fulfilling the requirement for protocol composition.
* Message receives by way of behaviors can be made first class through partial application or captures, fulfilling the requirement for protocol composition.

The _pony-reactors_ framework is inspired and informed by:

Expand Down
4 changes: 3 additions & 1 deletion examples/basic/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ actor Main is Reactor[None]
fun ref reactor_state(): ReactorState[None] => _reactor_state

fun ref init() =>
/* Non-registered Welcomer */
let welcomer = Welcomer(system, None, env.out)
welcomer << "Ponylang"
welcomer << "Reactors"
/*

/* Registered Welcomer * /
let conn = open[(ChannelReservation | None)]()
channels() << ChannelReserve(conn.channel, "welcomer")
conn.events.on_event({
Expand Down
1 change: 1 addition & 0 deletions reactors/channel.pony
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

class tag ChannelTag
""" A unique identifier for a channel. """


interface val ChannelKind
Expand Down
37 changes: 34 additions & 3 deletions reactors/reactor.pony
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,52 @@ trait tag Reactor[E: Any #share] is ReactorKind
// trait tag Reactor[E: Any val] is ReactorKind
""""""
fun ref reactor_state(): ReactorState[E]
""" Required accessor for the reactor's basic state. """

fun ref main(): Connector[E, E] =>
""" The reactor's main connector. """
reactor_state().main_connector

// fun ref sys_events(): Events[SysEvent] =>
// reactor_state().system_events

fun ref channels(): Channel[ChannelsEvent] val =>
""" Accessor for the channels service channel. """
reactor_state().channels_service

// TODO: Reactor.open - Support opening new connectors.
fun ref open() => None
fun ref open[C: Any #share](
reservation: (ChannelReservation | None) = None)
: Connector[C, E]
=>
""" Open another connector for use by this reactor. """
let channel_tag: ChannelTag = ChannelTag
// Create a partially applied version of the `_muxed_sink` with the
// `channel_tag` uniquely identifying this connector's channel.
let pa_muxed: {(C)} val = recover val this~_muxed_sink[C](channel_tag) end
// Build the connector.
let connector = Connector[C, E](
where
channel' = object val is Channel[C]
let _channel_tag: ChannelTag = channel_tag
let _pa_muxed: {(C)} val = pa_muxed
fun channel_tag(): ChannelTag => _channel_tag
fun shl(ev: C) =>
_pa_muxed(consume ev)
end,
events' = BuildEvents.emitter[C](),
reactor_state' = reactor_state(),
reservation' = reservation
)
// Add the new connector to the reactor's collection,
// indexed by the `channel_tag`.
reactor_state().connectors(channel_tag) = connector
// Return the connector for use.
connector

// TODO: Reactor.open_isolate() - A non-isolate reactor should still be able to open isolate channels of its own. Same with an IsolateReactor, which should be able to open non-isolate connectors.

fun tag shl(event: E) =>
""" Shortcut to use a reactor reference itself as its default channel. """
""" Shortcut to use a reactor reference as its default channel. """
default_sink(consume event)

fun tag default_sink(event: E) =>
Expand Down

0 comments on commit 20a0941

Please sign in to comment.