Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Oct 06 14:14

    dependabot[bot] on npm_and_yarn

    (compare)

  • Oct 06 14:14
    dependabot[bot] closed #47
  • Oct 06 14:14
    dependabot[bot] commented #47
  • Oct 06 14:14
    dependabot[bot] labeled #62
  • Oct 06 14:14
    dependabot[bot] opened #62
  • Oct 06 14:14

    dependabot[bot] on npm_and_yarn

    Bump url-parse from 1.4.4 to 1.… (compare)

  • Sep 29 17:45
    dependabot[bot] labeled #61
  • Sep 29 17:45
    dependabot[bot] opened #61
  • Sep 29 17:45

    dependabot[bot] on npm_and_yarn

    Bump i from 0.3.5 to 0.3.7 in /… (compare)

  • Sep 03 00:51

    dependabot[bot] on npm_and_yarn

    (compare)

  • Sep 03 00:51
    dependabot[bot] closed #36
  • Sep 03 00:51
    dependabot[bot] commented #36
  • Sep 03 00:51
    dependabot[bot] labeled #60
  • Sep 03 00:51
    dependabot[bot] opened #60
  • Sep 03 00:51

    dependabot[bot] on npm_and_yarn

    Bump immer from 2.1.5 to 9.0.6 … (compare)

  • Aug 12 04:31
    dependabot[bot] labeled #59
  • Aug 12 04:31
    dependabot[bot] opened #59
  • Aug 12 04:31

    dependabot[bot] on npm_and_yarn

    Bump path-parse from 1.0.6 to 1… (compare)

  • Aug 11 00:49
    dependabot[bot] labeled #58
  • Aug 11 00:49
    dependabot[bot] opened #58
Pete Barnett
@petebarnett
(this is all probably equivalent to NAPs just returning callbacks which produce proposals at the time they're called)
Daniel Neveux
@dagatsoin
I ran into this problem to. I solved this by adding a new responsibility to the StateRepresentation function : define what actions could.be triggered at a specific ControlState.
It is barely equivalent of what exists on the Safe demo that JJ wrote a few years ago.
Daniel Neveux
@dagatsoin
Regarding the intent queue, you can have conflict when two actions in the queue are allowed in the same ControlState but those control states can be separated in time (e.g.: the cart can be empty, the filled, then empty...). In this case I solved the issue by tagging intent whith exact same mechanism than, one more time, the SAM Safe exemple.
Another situation is also on a project I am working on, a lag compensation engine for multiplayer game. In this case I have to deals with multiple users with different ping. And this is solved by using a Lemport Clock which fits perfectly with SAM, as the exemple above, due to the "timed" nature of the algorithm.
Daniel Neveux
@dagatsoin
(writing from my phone, sorry for typos)
Pete Barnett
@petebarnett
So in the end I implemented “multiple NAPs” as callbacks- each “module” taking part in the flow has a chance to return a callback during the nap phase instead of presenting. Callbacks are added to the stack and the loop completes when that stack is empty. Each callback may receive a different state represention but it is up to the implementation whether it wants to act.
The first callback in the stack would be executed immediately anyway. Only where more than 1 is registered would each potentially receive a new state. This again only happens when a listener cares about events rather than state
To clarify, these callbacks are not registered upfront - they are returned in each cycle, where something wants to act. They are essentially single event consumers, or promises
Pete Barnett
@petebarnett
If something is dependent on a particular control state. It can determine when the callback is invoked that the control state is or isnt present- and do nothing
So yeah, i think i realised what you were sayinhg about conflict with intent queue- which is why this is still an extension of the nap, which is simply deferred- these receive the current state when they are called and present proposals (still inside nap)
Daniel Neveux
@dagatsoin
Is there a name for a pattern that can be used and composed at all level? Business level, view level, infrastructure level, etc.
Jean-Jacques Dubray
@jdubray
@JohnGurin you are correct, thats why nextAction later became the next action predicate such that you could control the rendering of the view (or response to a client) based on the next action being triggered. Some next actions allow for rendering others don't. Its not because one action gets triggered that it would prevent rendering, it's on a case by case basis. (Sorry for the delay)
Jean-Jacques Dubray
@jdubray
@petebarnett @dagatsoin SAM is not prescriptive on what happens in nap, the are a lot if actions that can be executed in parallel (if an action's role is to present a proposal)
That's why its not a good idea to implement actions that manipulate the state like MVC, or general event handlers.
You have to distinguish of course between the case of two SAM instances interacting with eath other (not a next-action in SAM sense, its a re-action).
Jean-Jacques Dubray
@jdubray
A true next action, is rather a trans-action. An action has happened (debit account), a next action must now take place. For payments, you need both the credit action and a timer, to recredit the account on timeout (no confirmation).
The is really no logical barrier on how many next action you can initiate. By definition an action has to operate on a stale state, that's why creating proposal rather than orchestration state changes is the paradigm shift that SAM introduces.
Pete Barnett
@petebarnett
Thanks @jdubray - that is enlightening
Jean-Jacques Dubray
@jdubray
The changes are subtle when you shift from orchestrating state changes to submitting a proposal, but the code is much easier to reason about when multiple actors (actions) can be involved, because very quickly the orchestration code needs to become stateful, it's hard to perceive that transition and let alone implement it, orchestration code is inherently hard to write (say WS-BPEL if you ever took a look at it or AWS step functions). People will argue: "...but you can use function generators in javascript", sure but the state is trapped in the function generator, not really accessible to any part of the application (and then what happens in the case where the process crashes and the function generator state is lost). SAM works in all cases, re-action, trans-action, simple action, ...
Jean-Jacques Dubray
@jdubray
SAM is also inherently easy to persist (see the SAM-SAFE implementation)
david kaye
@dfkaye_twitter
@jdubray For clarification, by "function generators in javascript" do you mean generator functions that create iterable/iterator objects?
That's a problem that Redux was never able to solve, unfortunately.
Jean-Jacques Dubray
@jdubray
No, I had not. Generator functions could be used to implement SAM (as you can see, it's easy to "wait for an action"), I am not sure about the other parts of the pattern like render and nap. A generator function inherently yields to the caller and wait for another call (which work well for even handlers and request/response types of interactions). I am not sure about a "reactive loop implementation" where you pass messages along the control thread.
Jean-Jacques Dubray
@jdubray
After that the author gets into the mode "with my new hammer, everything looks like a nail", that piece of code is horrendous https://jsfiddle.net/awto/y3x6L9wu/
Slađan Ristić
@sladiri
Generators seem to be another way to implement state machines in plain javascript. Another example https://codepen.io/rgdelato/pen/vGNRqP. I wanted to try that to represent the control state, in some cases it might be useful, when your logic benefits from being defined as a state machine.
Slađan Ristić
@sladiri
But yes, the generator state is ephermal, but maybe you can still benefit because it is easier to read. Being able to serialise the state is really nice, that is what we do to save state in a SPA when navigating away from a page and want to come back. We got this feature almost for free. (Heh, so maybe generators are not really possible to use easily.)
Slađan Ristić
@sladiri
image.png
messages can only be bits, not commands
just saw this and it reminded me of the "proposal pattern". This seems to me the most foreign principle when explaining SAM, but it has been proposed before, no pun intended
https://www.youtube.com/watch?v=9MqVfzxAp6A
Slađan Ristić
@sladiri
on my millionth thought though, this is just decoupling systems via events :)
Jean-Jacques Dubray
@metapgmr_twitter
I may I have shared this article before (from Achim Schneider): https://codingconnects.com/sam-pattern
1 reply
david kaye
@dfkaye_twitter

Andrew Ray updated his blog design and tweeted about it so I revisited and found this Flux pattern post (dated Nov. 13 2021, but I recall seeing it circa 2014). Have to say, understanding the SAM pattern helped me understand Flux (without Redux) this time around.

https://andrewray.me/blog/the-mental-model-that-helped-me-finally-understand-flux

Also a list from a different tweeter about 3 state management libraries:
  • Zustand is for flux-like state, like Redux.
  • Jotai is for atomic state, like Recoil.
  • Valtio is for proxy state, like MobX.
Jean-Jacques Dubray
@metapgmr_twitter
@sladiri this is coming really the definition of Actions from Dr Lamport that i tried to translate, and i dont claim that any ideas in SAM are mine, I am just a translator and if anyone has a better translation, I'll take it. I just don't believe that scientists the caliber of Newton or Lamport are blogging all day (like me), so i tend to be dismissive when people who thought about something for 10 min come up with better alternatives to TLA+ for instance.
Jean-Jacques Dubray
@metapgmr_twitter
Who needs a new framework? https://remix.run/docs/en/v1/tutorials/blog
it doesn't seem to have state management built in (who needs that anyways?) https://remix.run/docs/en/v1/guides/data-writes#remix-mutation-start-to-finish
Jean-Jacques Dubray
@metapgmr_twitter
That's unrelated to SAM, but this project is near and dear to my heart, we have been busy in my engineering organization building a schema architecture that I designed 12 years ago. GraphQL has been a game changer to realize that design. I expect that this now FOSS project will solve a major pain point in microservices: https://github.com/nav-inc/nav-schema-architecture
Special credit to Michal Scienski, Daniel Zemichael, Jeff Warner, Jovon McCloud and Brent Tubbs. Michal has created a build pipeline that's out of this world. I expect that it will take a life of its own and make a durable impact on low code projects.
For reference, this is the article I wrote 12 years ago: https://www.infoq.com/news/2009/02/message-type-architecture/
GraphQL (as a syntax) and its metamodel deliver an unprecedented DevX.
Jean-Jacques Dubray
@metapgmr_twitter
Here is my post on if-then-else. It's a key aspect of keeping mutations ckearly defined. https://dzone.com/articles/revisiting-switch-and-if-then-else
Gaetano Miranda
@gaemir
Hello all, I am receiving a 404 when loading the dzone article. Is anyone able to load it? Thank you.
Pete Barnett
@petebarnett
@metapgmr_twitter do you have another copy of that article anywhere? i'm really interested in reading it
Jean-Jacques Dubray
@metapgmr_twitter
Sorry, I had made a mistake and the article went back in moderation when I corrected it (for the last couple of days)

In computer science, there is nothing more fundamental and intuitive than control structures. Every student learned about them in the first few weeks of any computer science program. We could not code without them, period. But they are not set in stone: we did get rid of the infamous GOTO in the 80s!

At the pre-ALGOL meeting held in 1959 Heinz Zemanek explicitly threw doubt on the necessity for GOTO statements; at the time no one paid attention to his remark, including Edsger W. Dijkstra, who later became the iconic opponent of GOTO.[3] The 1970s and 1980s saw a decline in the use of GOTO statements in favor of the "structured programming" paradigm, with goto criticized as leading to "unmaintainable spaghetti code"

And this paper is far from being the first call for using less conditional flows in programming. Even Uncle Bob has tackled the topic. In npm alone, countless packages have been published to provide an alternative. People tend to think that the catch with switch and if-then-else is that the more branches in your code, the more opportunities for untested and unexpected behavior. Every branch requires different use cases to be tested thoroughly, and even though, some tools, such as Istanbul, provide some indication as to how many branches have been executed, in addition to the traditional function coverages. But branches are part of the nature of programming, there is nothing we can do about it.

In this short opinion brief, I would like to give a new spin on when to use control structures and when to use some functional alternatives.

Ever since I discovered Dr. Lamport's Temporal Logic of Actions, my programming style has changed to become more explicit about assignments vs mutations of the application state, and more conscious about temporal logic. For me, there are three core patterns to programming: Object-Oriented Programming, Functional Programming, and Temporal Programming. A developer should master all three and use them accordingly, no one pattern wins over the other. We cannot write good programs with one pattern only.

One of the key notions of Temporal Programming is control state: when the light is off, and If the switch is in the off position, I can turn the light on otherwise check the circuit breaker (in bold we have the actions that are expected in a given control state). These are extremely familiar concepts we use every day, yet for some reason, we rarely make the control state explicit in the code we write. My take on it is that situation came about from a lack of alternative constructs, and the ease of use of switch and if-then-else, over the rigor needed to use temporal programming constructs, but that discussion is for another article. I'd like to focus here on something much simpler and actionable today.

First, we must absolutely stop using these control structures for (complex) assignments, it often leads to some pretty ugly code that is hard to read and debug. How many times have you written some code like this? come on, tell me in the comments...

How about a functional alternative?

My code is not new, it is actually heavily inspired by that article from Hajime Yamasaki Vukelic, which probably got inspired by many similar articles. I brushed up his code to make it a bit more developer-friendly and implement some automatic behavior.

We can now rewrite the code above in a functional way and do a proper assignment.

As a side note, my programming style uses named predicates such as exists, rather than writing inline condition functions. It makes the code a lot more readable, maintainable and testable for complex predicates (imagine a bug in a complex condition...).

The matchReduce function composes an array of functions conditionally. That may well be related to monads, but I have no idea since, to this day, I am still looking for a clear definition of what a monad is.

You can find some code examples on how to use match and matchReduce here.

Now we could stop here and say, alright, for all assignments I'll use this functional construct, and if you did that, that

const matched = (x) => ({
  on: () => matched(x),
  otherwise: () => x,
})

const match = (...x) => ({
  on: (pred, fn) => {
    const _pred = typeof pred !== 'function' ? (z) => z === pred : pred
    const _fn = typeof fn !== 'function' ? () => fn : fn
    return _pred(...x) ? matched(_fn(...x)) : match(...x)
  },
  otherwise: (fn) => (fn === undefined ? x[0] : typeof fn !== 'function' ? fn : fn(...x)),
})

const matchReduce = (...x) => ({
  on: (pred, fn) => {
    const _pred = typeof pred !== 'function' ? (z) => z === pred : pred
    const _fn = typeof fn !== 'function' ? () => fn : fn
    return _pred(...x) ? matchReduce(_fn(...x)) : matchReduce(...x)
  },
  end: () => (x.length > 1 ? x : R.head(x)),
})
It allows you to write assignments like this: