membrane

Ben Sless 2023-02-08T18:40:05.789969Z

I just had a weird idea - membrane's effect handling model can be used to write any sort of event driven program, where the state is just that of an event. why shouldn't I be able to run backend programs with it? I can write pure code and specify handlers when I start it up. Is this most of the way solution to dynamic algebraic effects in Clojure?

Ben Sless 2023-02-11T10:11:25.839259Z

I guess the main difference is if you write "linear" code or provide a callback, but one can be turned into the other using a rewriting macro. The important part for me is in not specifying how the effect is run, but passing it to a handler, where the waiting code will know how to handle a synchronous or asynchronous result

Ben Sless 2023-02-11T10:12:44.315899Z

This also looks suspiciously like a free monad

dgr 2023-02-10T17:31:04.438179Z

No, but I looked into that. The required speed was low. I ended up using core.async, though.

elliot 2023-02-10T22:53:32.657119Z

I suspect some of the complexity comes from needing to pass control flow back and forth between the effect handling system and the regular interpreter, and basically making the caller manage the difference between (eval (f ...)) vs. (eval-effect-async [:f ...]) vs. (eval-effect-await [:f ...]). To some degree if we’re making the caller manage this we may as well use standardish core.async or a similar async-like API. I’m not sure but the main options I've seen are "make people write explicitly sync vs. async code" or "make everyone write code that's actually running in an async runtime (e.g. erlang, Haskell), that gives them the uniform interface.". Perhaps there's more options in the design space though.

phronmophobic 2023-02-08T18:40:55.099709Z

I wish it were!

phronmophobic 2023-02-08T18:42:04.992819Z

I was thinking along those lines after reading and learning a bit about algebraic effects https://clojurians.slack.com/archives/C03RZGPG3/p1672777264296859?thread_ts=1672700787.847939&cid=C03RZGPG3

🔥 1
phronmophobic 2023-02-08T18:56:38.604589Z

At best, membrane's effect handling is an accidental algebraic effect system. It's on my todo list to learn more about algebraic effects. My bet is that there's lots of good ideas to pilfer.

Ben Sless 2023-02-08T19:28:04.675259Z

I though some time ago of a model similar to membrane's intents, where side effects are represented inn data form, you call a universal handler which passes it off to the proper executor and side effect, but how does the result return? You have to specify who's called when you complete with the previous state and side effect result You either do it as a continuation or a membrane like on handler

phronmophobic 2023-02-08T19:30:34.303299Z

I think fulcro does a good job of expressing that these intents/effects actually are mini-programs and the effect handler is a mini-interpreter. There's issues where you start bing able to represent intents that have programming constructs like conditional-branching.

Ben Sless 2023-02-08T19:38:55.216649Z

That can be solved in the continuation, no? You specify what function gets called next, it does the branching, then produces another intent after a fork, no? My inspiration was actually how servers work, specifically ngnix 🙂

phronmophobic 2023-02-08T19:40:38.411239Z

There's still some open questions that I don't have a good answer for: • can effects rebind other effects before calling them • should we just be using something like vars and namespaces? • I like the idea of saying: "run this UI with this refied set of effects." There's a longer list, but that's what I remember from the top of my head.

1
phronmophobic 2023-02-08T19:41:19.625129Z

> You specify what function gets called next, it does the branching Membrane's effects does a minimal version of this where every effect has an implicit dispatch! argument.

phronmophobic 2023-02-08T19:42:03.031459Z

Is branching handled by the "effects system" or does it somehow reuse the execution and eval provided by clojure?

phronmophobic 2023-02-08T19:44:29.454319Z

One interesting idea views can be almost completely datafied, except for the event/effect handlers. If it were possible to have those specified with instructions similar to llvm, then you compile whole user interfaces to any target language.

🔥 1
Ben Sless 2023-02-08T19:44:50.667279Z

Let's say every sequence of intents has to specify a continuation that takes all the results and the previous state That continuation is just a function, and it could do the logic, branch, then call the handler with the right intent

Ben Sless 2023-02-08T19:47:06.938749Z

sort of like

[[:get-from-db arg1 arg2] next-f]
Then f roughtly
(if (failed? result) (emit [[:get-from-db ,,]]) (emit [[:do-more]])

Ben Sless 2023-02-08T19:47:27.518359Z

even stuff like retries can be seen that way

Ben Sless 2023-02-08T19:48:15.614849Z

And in theory this can have incredible performance, it never blocks

phronmophobic 2023-02-08T19:49:27.824509Z

other use cases to consider are "composite events" like double-click or click-and-release-inside

phronmophobic 2023-02-08T19:49:49.720959Z

which I think can be incorporated

phronmophobic 2023-02-08T19:52:18.736659Z

what's the difference between emit and eval? I'm not saying there isn't* a difference, but I think it's useful to articulate

Ben Sless 2023-02-08T19:53:06.567089Z

emit means "goto the ur handler"

Ben Sless 2023-02-08T19:53:46.845639Z

That lets you specify both how to run the intent, and for extra credit, which thread pool to run it on

Ben Sless 2023-02-08T19:54:03.748689Z

letting you maximize thread pool utilization. you only need three pools, in theory

phronmophobic 2023-02-08T19:59:17.865959Z

right, but doesn't (eval (do-more :my :args))` also mean run the do-more handler?

phronmophobic 2023-02-08T20:04:36.607799Z

there's also some features here that overlap with dependency injection libs like mount, integrant, clip, system, etc

Ben Sless 2023-02-08T20:23:01.033639Z

Yes, I thought this up when I was sick to the teeth from mount

😆 1
Ben Sless 2023-02-08T20:42:13.155409Z

Instead of injecting the dependencies, just say what you want to happen and pass it to an executor

👍 1
dgr 2023-02-08T22:47:54.414729Z

@ben.sless, I used a similar model when writing an automated trading application. The brokerage library (Java) would inject events. Those would be represented in pure data and would be passed to a handler. Each handler would return a set of intents as data (e.g., place a trade, or start getting a quote stream for a specific ticker symbol), and an effect system would interpret those intents as effects (e.g. make calls to the brokerage library). The “active handler” could also change over time, giving a state machine model. One of the intents you could return was “switch to this new active handler” (state change). The nice thing about it is that it gives you a very testable core of logic.

👍 1
Ben Sless 2023-02-09T06:16:16.034839Z

Was it using LMAX Disruptor?