Skip to content

Instantly share code, notes, and snippets.

@neauoire
Last active October 10, 2018 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neauoire/12433b31d7d81f4ce2c990bca0295787 to your computer and use it in GitHub Desktop.
Save neauoire/12433b31d7d81f4ce2c990bca0295787 to your computer and use it in GitHub Desktop.

On Riven

What constitutes a Riven-like? The "Berlin" interpretation — @Johnicholas

What design principles were you paying attention to that I am missing?

I've built this as a way to organize and visualize the sites I was working on. It's not designed to have any kind of drag-drop interface, but instead, to have the graph object code on one side, and the rendered resulting graph on the other. Riven is not really designed for interaction design, or real time projects even tho it could. Ideally, nodes should not preserve any of the data, but only be operating on the input it receives.

Do you think that signal is central, crucially important or a wart?

Signal is not very important, it's to focus the output of a node in a specific way, I've added a good usecase of signal in the conditional example.

What were your intentions regarding ports, they seem to have dropped away from relevance?

I've been trying to not expose them as much that's true. I only seldom had to target a port by name, and it always turned out to be from a mistake in the design of the webapp. So I'm gradually cleaning up the port UX.

Would you mind reordering the factors in order of importance? Would you be able to separate them into a group of high-value factors and a group of low-value factors?

  • Basics
    • .create()
    • .connect()
  • Connection
    • .Send()
    • .Receive()
    • .Request()
    • .Answer()
  • Helpers
    • .signal()
    • .mesh()
  • Convenience
    • .bind
    • .bang

Are these points necessary and/or sufficient to capture your intent?

Yes, these primitives is everything that should be in this framework.

make you comfortable with a hypothetical C++ or Java or C# "Riven"?

Sure! Although, I'm currently rebuilding the .mesh behaviour, so I'd wait on that part for now.

Are you attached to the vocabulary you chose connect/send/receive/bind/answer etc, or would you consider that superficial?

I am not attached, you can choose which ever words you prefer, a sort of .send, .onSend, might make more sense for you.

In the documentation for Signal, you gave an example: Ø("template").signal("parser").send("hello"), and described it as: Will send "hello", directly to the parser node. Z

That was a mistake, I noticed it yesterday and fixed it :) Ø("template").signal("parser").receive("hello") is the correct way to use signal. Signal's purpose is also to test if there's a signal between two nodes, and to follow that signal across the graph.

Is that correct? What principle led you to prefer the one-argument signal method over alternatives, such as a variadic .signal("parser", "hello") that might be easier to use?

It used to be like that, but the point now is that you can follow the signal across multiple nodes, like: Ø("root").signal("body").signal("element").signal("children"), its name is meant to be a reference to "following the data".

There's a tension between, on the one hand, the notion of ports (which you mentioned you are exposing less) and your mesh-as-black-box plans, and on the other hand, a flat, shared namespace. Would you consider "a flat, shared namespace" to be an essential factor in defining what a Rivenlike is?

I think both can co-exist, I've been thinking that it would be nice if I could reduce the number of direct id node calls(Ø("database")) in my wiki code, I like that an interaction goes through an MVC-type flow, and does not go backward. Although, it's nice to have, and it makes tester and itterating a lot faster. I would like to make it so the children of mesh type nodes are not publicly accessible too.

There's a tension between your mesh-as-black-box plans and the fact that until recently grouping nodes with mesh doesn't mean anything operationally. Would you consider "the grouping of nodes into hierarchy doesn't mean anything operationally" to be an essential factor in defining what a Rivenlike is?

I'm not sure yet which way this will go. As of today, I use the node graph as a way to keep an oversight of how the data flows across my apps. I don't think there's a "hierarchy" system in Riven, it's just some nodes can be "tagged" as being part of one part of the system or another.

If I understand correctly, the values that flow among the various nodes are currently plain JSON structures, acyclic and/or immutable. Would you say passing plain values (such as protobufs or s-expressions) is an essential factor in making something Rivenlike, or would passing a reference to a service, a function object, or an opaque object with some methods on it be a reasonable thing to do in a Rivenlike?

Well, while I don't pass functions along the graph, it could be done and it would not make it any less Riven. It's just how I see it, data flows through "machines", so I don't send machines down the signal, but it could be done.

Basics

  • Flat, shared namespace. Each node in the graph has an id which names it ().
  • Nodes in the graph can be grouped (and the group named), into a hierarchy, though the grouping doesn't mean anything operationally.
  • Each node in the graph is located on a plane (used for drawing system diagrams).
  • The nodes do not have direct dependencies on one another, and the indirect dependencies are expressed by the edges in the graph.
  • There are only two kinds of edges between nodes, a generic query, and a generic command, and the combination is like a third "bidirectional" kind of interface. (This is something like HTTP's verbs, GET and POST.)
  • The generic query is (usually?) idempotent and side-effect free.
  • The generic command does not mutate the argument that it receives.

This vocabulary:

  • "connect": To arrange for "a" to (in general) call "b"'s generic command, do something like graph.connect("a", "b"); Visually, usually "a" is to the left, and "b" is to the right. This is used in the graph-construction phase.
  • "syphon": To arrange for "a" to (in general) call "b"'s generic query, do something like graph.syphon("a", "b"); Visually, usually "a" is above, and "b" is below. This is used in the graph-construction phase.
  • "bind": To do both kinds of association (connect and syphon), do something like graph.bind("a", "b"); This is used in the graph-construction phase.
  • "send": This is used to connect outside sources of control flow, like "onclick" events, to particular nodes of a graph, and also nodes call it on themselves or (rarely) another node found via "signal". Send is rarely overridden, and by default broadcasts send's argument to all the nodes connected to the node (that is, to the right).
  • "receive": Receive is often overridden, but by default broadcasts receive's argument to all the nodes connected to the node (that is, equivalent to send).
  • "request": Request is rarely overridden, and by default calls "answer" (passing the argument along) on all the nodes syphoned to the node (that is, "downward"), and assembles their answers into an object (using their ids as keys) and returns it. This might be called from an outside source of control flow (in principle), or from inside an overriding method.
  • "answer": Answer is often overridden, but by default calls "request".
  • "signal": Signal is rarely overridden, and by default searches among a node's adjacent nodes for a node with a particular id, and returns it.-
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment