[HN Gopher] Your "simulation" might not need state
___________________________________________________________________
Your "simulation" might not need state
Author : onsclom
Score : 202 points
Date : 2023-03-28 06:15 UTC (16 hours ago)
(HTM) web link (www.onsclom.net)
(TXT) w3m dump (www.onsclom.net)
| steve76 wrote:
| [dead]
| Huggernaut wrote:
| I _think_ the color changing implementation could result in the
| dvd logo changing color when the window is resized. That may be
| fine of course but I'm just curious what you would do to avoid
| that, if it were required not to.
| rjmunro wrote:
| Not just the color, if you resize the window all calculations
| are totally thrown off. Try it with his live demo page:
| https://www.onsclom.net/dvd-logo
|
| To avoid that, I'd use percentage positioning and have all
| widths and heights from 0-100. This would mean it would move
| faster horizontally than vertically in a wide window, but
| that's probably OK.
| account42 wrote:
| Making the simulated window square would not be OK because
| that makes the period after which the simulation repeats /
| number of possible states much much smaller. A large part
| popularity of the DVD logo comes from waiting for it to hit a
| corner which in the original requires two modulo to line up
| exactly - whith your modulo that condition is either
| impossible or happens all the time because the logo only
| bounces between two corners.
| faeyanpiraat wrote:
| You are right about the logo changing colours.
|
| You may use a bounding box which has a fixed size to avoid
| this.
|
| You cannot have a dynamically sized bounding box and still
| expect the colour to be the same, since the number of bounces
| since a fixed start time depends on the size of said box.
| tromp wrote:
| This also applies to more complicated things than bouncing around
| a rectangle. For instance, solving a Towers of Hanoi puzzle,
| where the i'th step in a solution is to move a disc from peg
| (i&i-1)%3 to peg ((i|i-1)+1)%3). (Note that for even #discs, peg
| 1 is the destination). Example for 3 discs: i
| i-1 i&i-1 from i|i-1 to 1 0 0 0 1
| 2 2 1 0 0 3 1 3 2 2
| 2 3 1 4 3 0 0 7 2 5
| 4 4 1 5 0 6 5 4 1 7 2
| 7 6 6 0 7 2
| Karellen wrote:
| > The Simulation actually has a bug. If you resize the window to
| be smaller you can trap the DVD logo on an edge. Resizing the
| window in the Animation solution means we recalculate to the
| correct position.
|
| Hmmm....that depends on what you define as the "correct"
| position. With the stateless version, any resizing of the window
| will cause the logo to instantaneously jump to where it would
| have been, and start moving in the direction it would have been,
| if the window had been the new size all along.
|
| Is that the correct behaviour though? Or would you want the logo
| to keep moving from it's current position, in it's current
| direction, if that were possible?
| jerf wrote:
| You provide a good hook for the point I want to make, which is
| that while I agree that many developers underestimate the cost
| of state, sometimes people can overreact and overestimate its
| costs too. Less common, as underestimation is still the common
| case, but it can be done.
|
| I treat it as a cost that needs to be managed and that we want
| to make sure to exploit its advantages for those costs, but it
| is still often still something that is better in the system.
|
| In this case, a hybrid approach can be useful. Writing the
| animation in terms of the initial state and the time is quite
| useful and powerful on its own terms, something worth doing. On
| the other hand, dealing with an event stream of changing
| resolutions, while possible, also nukes all the cleanliness out
| of the solution. So what do we do? When the window resizes,
| reseat the original parameters. The animation as written
| implicitly starts at 0,0 and has an implicit direction in it.
| Bringing those out as parameters isn't a bad idea anyhow,
| because that may not be the ideal. Then, when the window
| resizes, simply look at the current parameters and start a new
| animation with those as the initial state, handling problems
| with window becoming too small or something as some simple
| checks on the initial parameter.
|
| This yields a nice mix of both the advantages of statelessness
| and the advantages of state. It's a continuum rather than a
| binary, and the optimum is not always the extrema. Sometimes it
| is, but not always.
| namuol wrote:
| You could fix this by deriving the function from some initial
| state, and then when resizes occur you just take a snapshot of
| the current animation state, freeze the animation until
| resizing stops, and replace the function with a new one derived
| from your previous initial state. So this does require _some_
| state but only to initialize.
| tleb_ wrote:
| Also, with the stateless version, resizing breaks color changes
| on collisions.
| CyberDildonics wrote:
| This example is not interactive. Of course it doesn't need state,
| it is essentially playback of an animation.
|
| John Carmack once pointed out, procedural generation is
| essentially just compression. Video playback only has time as
| state too.
|
| This is just pointing out that if you have data to play back, all
| you need is a time variable for state. If you want to make that
| data procedurally (or somewhere in between by embedding a curve)
| you can do that too.
|
| This does not have anything to do with programming in general.
| HWR_14 wrote:
| I agree with 99% of this, however I feel like the author missed
| something and was 100% wrong in one case. When talking about bugs
| in the simulation state, the author states:
|
| > If you resize the window to be smaller you can trap the DVD
| logo on an edge. Resizing the window in the Animation solution
| means we recalculate to the correct position.
|
| If you resize the window in a simulation state it's trivial to
| maintain the same general position of the logo. For instance, 80%
| across the screen or whatever. With the authors code, as the
| window is resized, the logo goes flying everywhere in what is
| obviously a bug. Especially if it has been running a long time,
| changing the modulus will result in random seeming jumps.
| Joker_vD wrote:
| Yeah, and then at some point of time the "time" input becomes a
| bit too large for "%" to give accurate results and the whole
| thing freezes and updates only once every ten seconds. Whoops!
| dgb23 wrote:
| In a game loop you can typically pass in the time (delta) since
| the last frame update. Everything else is a function of that
| plus the values since the last update.
| Joker_vD wrote:
| ...did you read the actual article? It's explicitly about how
| instead of stepping the simulation by a time delta you can
| instead use "animation"-style where you take the time point
| and derive state straight from it, without any differential
| calculus: def ball_position(time):
| if time < 0: return (0, 0)
| stop_time = 2 * Y_VELOCITY / GRAVITY if time >
| stop_time: return (X_VELOCITY * stop_time, 0)
| x = X_VELOCITY * time y = (Y_VELOCITY - GRAVITY *
| time / 2) * time return (x, y)
| pshirshov wrote:
| Can you do it for the game of life? Pretty please?
| Joker_vD wrote:
| I am pretty certain there is no general closed-form
| solution for Game of Life because it's Turing-complete;
| although for some initial configurations (gliders etc.)
| you can have a simple (or not so simple) expression, in
| the general case you simply have to simulate it step by
| step, no other way around it.
| mrguyorama wrote:
| I think in practice you eventually reset the "time" variable to
| 0 and just accept that one weird discontinuity, if you care
| about it at all. Plenty of time the "time input becomes too
| large" only happens after tens of hours of nonstop play.
| 6510 wrote:
| <marquee behavior="alternate"><marquee behavior="alternate"
| direction="down"><img></marquee> </marquee>
|
| I'll Show Myself Out.
| 6510 wrote:
| https://jsfiddle.net/eLfd71jx/
| jeanlucas wrote:
| You guys are so close to discover the joy of Elixir/Erlang
| jmole wrote:
| This only works if: 1) your state is a linear function of time 2)
| you know the initial conditions 3) the time integral calculation
| of the state evolution over time is trivial to do (e.g. constant
| velocity means distance travelled is simply time * velocity)
|
| It's not that you "don't need state", it's that your state
| calculation is entirely dependent on time T and initial
| conditions I, _and_ it 's calculable in ~roughly~ the same amount
| of time as an actual state update would take.
| janmarsal wrote:
| Even if it doesn't need a state I'd still like to have one. Makes
| things a lot easier.
| pshirshov wrote:
| Oh yeah.
|
| Now try to do the same little trick for a cellular automaton.
|
| Good luck.
| topaz0 wrote:
| The "might" is load-bearing. The advice is to look for ways to
| eliminate/consolidate state, not that every problem is amenable
| to simplification in this way.
| hyperthesis wrote:
| This is really cool. I immediately saw (t%w, t%h) for wraparound
| (it's neat how the cycles interact). The bouncing, as he says,
| seems trickier. I thought about a 2w x 2h box, but stopped there.
|
| His suggestion of starting with 1D was great, made it much easier
| to think about! I drew a graph of x against t, and worked out
| |t%(2w) - w| (starts rightmost) Can use absolute value, no need
| for ternary op. (In hindsight, graphing y against t would be
| easier to visualize.)
|
| I'm always amazed at how cartesian coordinates are independent
| (despite being so by definition). Descartes deserves more credit
| for this eponymous fundamental insight.
|
| For those thinking "simulation without state" (aka "procedural")
| only works for simple simulations, how about an ocean? _The
| Technical Art of Sea of Thieves_
| https://youtube.com/watch?v=y9BOz2dFZzs SIGGRAPH
| https://history.siggraph.org/learning/the-technical-art-of-s...
|
| It's based on _Simulating Ocean Water_ (Tessendorf 2001).
|
| They do a similar thing with ropes drooping correctly, though
| it's really crazy the numerical approach they developed. Those
| guys are really smart.
| SkeuomorphicBee wrote:
| The author gave just a simple example, the DVD animation, but it
| is much more than this, this technique is the industry standard
| in game art for all the background animations of a game, like the
| wind blowing the leaves and the grass, the water waving and
| foaming. Those effects are often implemented in shaders, and
| state in GPU shaders is something very very expensive, so they
| are implemented as a function of time (often based on a noise
| function to give a more natural feel).
|
| One important thing to note is that is not about eliminating all
| state (that would be absurd after-all a game is all about the
| state of the main character), but crucially to never take the `t
| - 1` state of the thing being animated. In games for example, a
| function animating a blade of grass may take into account many
| parts of the game state, they often take the character position
| as a multiplier to amplify the movement if the character is
| close, imitating collision with the character without actually
| having to calculate collisions.
| vendiddy wrote:
| Comments like this are the reason I love HN. Learned something
| new today!
| Cthulhu_ wrote:
| Hmm, the only serious experience I've had with game development
| was a FPS style game during school; one of the things I
| remember was that updating the position of a player or e.g. a
| bullet was to take the previous location, the velocity, and the
| time elapsed between the previous and current state. This was
| demo code btw, not my own - I wasn't smart enough for that yet.
| Still amn't.
|
| Anyway, now I wonder if this could be done in this style.
| Bullet position = its origin, vector and then you can determine
| its location at any point in time, instead of updating its
| position at every tick.
|
| Doing that one will likely cause issues across a network or if
| framerate (or game tick rate) is unstable / unpredictable.
| TrainedMonkey wrote:
| > Anyway, now I wonder if this could be done in this style.
| Bullet position = its origin, vector and then you can
| determine its location at any point in time, instead of
| updating its position at every tick.
|
| It certainly can! Not an expert, but what you are describing
| can probably be done as a bullet shader.
|
| > Doing that one will likely cause issues across a network or
| if framerate (or game tick rate) is unstable / unpredictable.
|
| I kind of want to know if a seamless experience is possible
| without a server... but for now I assume there is one. At
| that point client frame rate / tick rate does not really
| matter. Each client sends current character position and
| origin + vector of all the bullets to the server. Note that
| current location of the bullets is not important to update
| the world state - only the origination point and direction.
| Based on that it can independently do all the hit
| calculations. Of course this makes cheating possible and any
| kind of latency extremely annoying, but margins of this post
| and my available coffee break time are too thin to offer my
| amateurish take on those problems.
| munificent wrote:
| _> updating the position of a player or e.g. a bullet was to
| take the previous location, the velocity, and the time
| elapsed between the previous and current state._
|
| Yes, the technical term for this is Euler integration:
| https://en.wikipedia.org/wiki/Euler_method
|
| It's good enough for a lot of parts of a game simulation,
| especially if you have a fixed framerate. But it can get
| wonky if you have a lot of stuff moving around this way and
| interacting with each other. Full fledged physics engines in
| games tend to use more sophisticated algorithms to do the
| integration to avoid that.
|
| _> Bullet position = its origin, vector and then you can
| determine its location at any point in time, instead of
| updating its position at every tick._
|
| Unfortunately, no. The linked blog post is a really cool way
| to think about simple animations but you will very often hit
| a wall (metaphorically and literally) where this technique no
| longer works.
|
| The key problem is that the state of an object at time T
| depends on all possible interactions the object may have had
| before T. In the linked article, the only interactions are
| bouncing off walls, which are regular enough in time that you
| can model them analytically.
|
| (But even in this trivial example, it still doesn't really
| work. Notice that if you resize your window, the DVD box
| jumps all over the place. That's because the analytic
| solution can't understand the notion of a window whose size
| _changes_. All it can do is calculate where the box would be
| now _if the window had always been its current size._ I
| digress.)
|
| You can analytically calculate the position of a bullet at
| any time T given its origin and velocity, but _only if the
| bullet doesn 't interact with anything else._ If you have,
| say, a human controller play that is running through the path
| of the bullet and gets hit, your simulation needs to
| understand that the bullet won't keep moving after that.
|
| For a use case as simple as this, you may be able to model it
| statefully by just deleting the bullet entirely once it hits
| something. But, in general, there is always some level of
| state that you'll need in order to simulate anything beyond
| trivial complexity.
| barbariangrunge wrote:
| A lot of state resembles caching, and making sure those
| "caches" are updated properly is hard. The whole codebase
| begins to have to know about everywhere somebody decided to
| cache values, and every coder has to remember they exist and to
| update them all correctly even for minor code changes. Which
| means it causes a ton of bugs.
|
| This, and shaders in general, are great examples of better ways
| to do things.
|
| Often: Dynamic calculation > state
| agumonkey wrote:
| dissolution of state through parametrization
| drittich wrote:
| Please go on - I'm a novice at game dev and this kind of lore
| is gold and like gold, hard to find.
| clucas wrote:
| Here's a little tidbit of wisdom in this vein:
| http://web.archive.org/web/20221230074845/http://the-
| witness...
|
| EDIT to add: Also https://www.youtube.com/@MartinDonald has
| some good stuff as well. And here's a subset of Handmade Hero
| you might find interesting:
| https://www.youtube.com/playlist?list=PLEMXAbCVnmY6Mnkt-
| EZC_...
| baxuz wrote:
| GPU shaders have time?
|
| As for game logic-it does make sense. Tying logic and rendering
| to a system timer / frame counter leads to all kinds of issues.
| skrebbel wrote:
| GPU shaders don't "have" time but you can cheaply send them
| the current frame's time wrt some start time as a uniform
| input variable.
| Drakim wrote:
| It's extremely common to send a time input to GPU shaders,
| although this time input doesn't need to be tied to anything
| like a system timer or frame counter, it can be arbitrary and
| adjusted at will.
|
| Think of it like this: Imagine you have an expensive tween
| animation for particles, and every frame you have to use your
| CPU to calculate where the particle should have moved as part
| of it's animation, and send those updated coordinates to the
| GPU. Imagine you have millions of such objects, so it's quite
| the burden for the CPU, both in terms of calculating the
| tween animation, but also in terms of constantly updating
| every particle's x,y values over and over.
|
| Instead, you could seed each particle on the GPU with
| x1,y1,x2,y2 values, and then just provide a global "time"
| value for them all to share. When you update time = time + 1,
| all particles on the GPU will recalculate their position
| without needing to be helped by the CPU. The trick is that
| they don't save this new position though, instead, they do
| the job all over again from scratch at time = time + 2, which
| is a lot cheaper since we didn't need to "save" our previous
| result, which is hard work on the GPU.
| baxuz wrote:
| Great explanation, thanks!
| pdpi wrote:
| The other reason you want to use a function of time and avoid
| using state for things like animations is that you don't want
| animation speed to be frame rate dependent.
| Pxtl wrote:
| You do that by using delta-t instead of 1 when doing a
| stateful operation. The fact that a stateless function does
| it for you automatically is gravy.
| barbariangrunge wrote:
| Removing frame rate dependence isn't central to this concept,
| it's almost a coincidental side effect. You can easily remove
| frame rate dependence in the first example
| wolfi008 wrote:
| In other words, don't write state, only consume it. (Time is
| also state.)
| sublinear wrote:
| Game state isn't usually frame rate dependent either
| creshal wrote:
| A lot of games have some internal "physics rate" or
| similarly termed tick rate at which the logical state is
| updated. The better you can decouple the graphics pipeline
| from it, the better.
| munificent wrote:
| Render state usually isn't, but internal simulation and
| physics often updates using a fixed time step because it
| avoids many many problems caused by variable frame rates.
|
| https://gafferongames.com/post/fix_your_timestep/
| bluGill wrote:
| Not anymore, in the past it was. Some games in the 8bit
| world ran slower in Europe vs the US because Europe TVs
| used PAL which was 50hz refreshes, while the US had NTSC
| with 60hz refreshes (some games counted on NTSC being
| interlaced and so updated at 30hz which made NTSC games
| slower). IIRC some PC games depended on frame rate as well,
| but I don't recall any specifics.
| earthling8118 wrote:
| The original DayZ mod had an issue with this. If your
| framerate was lower your character would run slower. It's
| a very subtle effect, but when you ran several km
| alongside someone with a weaker computer they'd end up
| further back and you'd have to wait for them to catch up.
| dpkirchner wrote:
| Skyrim infamously allows your frame rate to influence
| game physics, with hilarious results.
|
| https://old.reddit.com/r/skyrim/comments/twwqqt/can_someo
| ne_...
| connicpu wrote:
| This is one of the traps when you go for the naive
| approach to framerate independence: simply using the
| frame delta for stepping your calculations. Results for
| physics engines are very wacky! It's usually better to
| step your physics engine at a fixed rate (e.g. 60hz), and
| make the frequency of steps performed independent of the
| graphics framerate. Perform some slight interpolation on
| the position of rendered objects and nobody can tell the
| difference
| j1elo wrote:
| When things aren't done properly, the whole experience can
| fall down.
|
| I recently played Minnit [1], a very small and cute game with
| a very unique proposal: that you only have 1 minute of
| gameplay before being warped to a starting point.
|
| But time is not checked asynchronously but as a function of
| the frame rate, so my playthrough was basically sped up and
| lasted for 45 seconds each time. Turned out I didn't realize
| until almost the end, when a _very_ difficult movement seemed
| almost impossible to make in time (I did end up achieving
| 100% doable things in the game, but it cost me a good amount
| of attempts!)
|
| Gamedevs, please, do not count frames and assume they'll be
| whatever amount per second you believe they'll be. Because
| they won't.
|
| [1]: https://store.steampowered.com/app/609490/Minit/
| Kiro wrote:
| Didn't that mean you also moved faster?
| j1elo wrote:
| Yes, that's how I was able to complete most parts of the
| game (which in general isn't difficult at all). But at
| least one of the extra optional things does get much more
| difficult if you don't have almost perfect execution, so
| faster movement speeds were actually a handicap as my
| control movements had to me much more precise.
|
| To be clear: it was at that time that I discovered that
| some wrong settings in the Steam Deck had been affecting
| the in-game FPS, so after correcting them and getting to
| the designed runtime speeds, the challenge got from
| impossible to barely achievable :)
| __MatrixMan__ wrote:
| As a newbie in a graphics course I once animated something
| using xlib and openGL. I hadn't bothered install my
| graphics card driver, so everything was nice and slow.
|
| I brought it into to the professor's office (it was our
| final) to demo it, and it was so fast on his machine that
| you couldn't see it. I freaked out, but he had seen that
| kind of problem before and just sprinkled some sleeps
| throughout so it was visible to a human and gave me a B.
| rpigab wrote:
| For those interested in shaders, check out Shadertoy and try to
| find really easy to understand ones or code your own:
|
| https://www.shadertoy.com/
|
| For instance, here is a DVD logo bounce animation in Shadertoy:
|
| https://www.shadertoy.com/view/wdcXRj
| davedx wrote:
| For simple things like an infinitely bouncing 3d logo this is a
| good solution.
|
| For things like particle effects in games or projectiles, the
| line starts to get blurrier. At some point, collision detection
| rears its head, and you end up storing state after all.
|
| Right tools for the job as always!
| account42 wrote:
| Particles are a good example because if possible you do want a
| closed-form solution that you can just run on the GPU each
| frame. But that limits what you can simulate. So either
| approach can be the correct one depending on your precise
| requirements.
| codingdave wrote:
| Sure, this is a good exercise to understand different approaches
| to a problem. But the core question behind the difference is
| whether it makes sense to store values, or recalculate them every
| time you need them. If the author is trying to help people learn,
| talking about that difference and getting into some depth about
| scenarios where each choice would be appropriate would really
| round out the article to be more valuable to people learning to
| code.
| davb wrote:
| The stateful example is much easier to read.
| charcircuit wrote:
| If you change your computer's time while this is running you
| break this. If actually care about knowing where something was
| the previous frame, there is nothing wrong with storing it.
|
| It's ironic because the author is already storing this in the
| css.
| wongarsu wrote:
| Date.now() should obviously be replaced by performance.now(),
| that would solve the clock problem.
|
| But you raise a good point that the author is storing the
| previous state in CSS. I guess the author's counter argument
| would be that they can still avoid storing the current
| direction.
| pavlov wrote:
| This kind of stateless programming is a great fit for visual
| graph UIs. A lot of visual effects and game programming tools
| offer some kind of node-based environment for this reason.
|
| The algorithms can even appear more intuitive when you remove the
| linear text trappings of traditional programming languages,
| because you can see at a glance which nodes are the changing
| input values and which are the dependent operations. (To a point
| -- once the graphs get too big to comfortably fit on the screen,
| the clarity advantage starts to disappear. At that point you'd
| better hope the tool offers some kind of grouping/nesting
| functionality to manage the growing complexity.)
|
| Node UIs can also offer immediate visual feedback on intermediate
| states, which makes it easier to tweak the algorithm compared to
| traditional debugging.
| pharmakom wrote:
| I think that the future of most software engineering is
| stateless.
|
| We will still have state, just it will live only in data stores
| (like Postgres) or infrastructure components (like Kafka). Most
| dev work will be writing declarative code for manipulating and
| providing access to such systems.
|
| I even think this will reach front end development, where the UI
| is an access layer to a local datastore.
| giovannibonetti wrote:
| That is exactly the premise of a pure functional programming
| language like Elm.
|
| https://elm-lang.org/
| HervalFreire wrote:
| That future is already here. All WebApps abstract state to some
| external program.
|
| Bare in mind this is only for web dev.
|
| Many other things outside of web dev cannot abstract state so
| easily. For example: The person who programs the database
| itself must deal with state.
| wongarsu wrote:
| This is how most web frontend and backend development was done
| for a couple decades, with PHP which (conceptually) starts a
| new interpreter instance for each request, and discards all
| state afterwards (and before PHP there was PERL and other cgi-
| bin scripts that actually started a new process each time), and
| HTML pages that lose all state on every page transition. If
| anything, the currently popular state of having very little
| request isolation inside nodejs servers and long-running SPAs
| is the anomaly. And at least on the server side this didn't
| come about from a desire to have more state, it's just an
| accidental side effect.
| dgb23 wrote:
| That's a wasteful approach though. Ideally you want your
| auth, validation/parsing, routing, db connection etc. all
| ready to go when a request comes in. There are parts that
| ought to be stateless and parts that shouldn't.
| stevewodil wrote:
| The Frontend local data store is essentially what a lot of
| state management libraries offer. Look at how you store and
| dispatch changes in something like redux.
|
| I think this type of pattern (not necessarily redux
| specifically) is definitely the future of Frontend state if
| it's not already the current paradigm
| chrisweekly wrote:
| It is already the dominant paradigm.
|
| React: f(State) => UI
|
| Redux: (State, Action) => newState
| flangola7 wrote:
| How does inference fit into this?
| pharmakom wrote:
| inference?
| jcuenod wrote:
| Your code might need state after all: time
| whiddershins wrote:
| Indirectly related:
|
| I did an audio space animated thing using the canvas bouncing
| ball, and then I wanted to add arbitrary obstacle with lines at
| are at 90 or 180 degrees.
|
| The math gets a magnitude more complicated immediately. Changing
| the angle of the ball doesn't do that, but taking the walls off
| of the coordinate system does.
|
| You go from simply adding a positive or negative signed number to
| some other situation where you have to calculate the vectors of
| all the bodies involved.
|
| I've always suspected there's some elegant solution that I'm
| missing.
| josephcsible wrote:
| I can see this being framed in terms of math now too: this
| demonstrates a closed-form solution vs. an iterative one.
| mirekrusin wrote:
| Well, you can lift state (x, y, dx, dy) to input as the
| "solution" does it (has (time) as input) and call it stateless as
| well. Lift window width and height and you won't have a bug with
| jumps on resize as a bonus.
|
| As another bonus you can make it non-linear, ie. having some
| basic non-linear physics.
| masukomi wrote:
| ok, the pendantic nerd in me has to point out that replacing
| stored X, Y state with data from the system is NOT eliminating
| state.
|
| It's just changing who is responsible for managing the state. The
| gradually increasing Time constant is still state, and it's still
| got code storing it, and gradually increasing it. This is
| _exactly_ the same thing as storing and changing X, Y
| coordinates, you've just outsourced the work and created code
| that's dependent upon the side-effect of an external system.
|
| If we want to get really pedantic it's also adding a dependency,
| but if you can't trust the system clock you've got bigger
| problems than having an additional dependency. ;)
| the_af wrote:
| > _This is _exactly_ the same thing as storing and changing X,
| Y coordinates_
|
| It's definitely _not_ exactly the same, since the Simulation is
| framerate dependent while the Animation is not.
| ggorlen wrote:
| Their code uses Date.now() but wouldn't it be better to use the
| high-resolution timestamp[1] passed as the parameter to the
| requestAnimationFrame callback[2]?
|
| [1]: https://developer.mozilla.org/en-
| US/docs/Web/API/DOMHighResT... [2]:
| https://developer.mozilla.org/en-US/docs/Web/API/window/requ...
| ecmascript wrote:
| That may work for very easy tasks like animation but nearly all
| apps I have worked on and continue to work on is not going to
| work as stateless. People expect that they will just continue off
| were they left in an app and for some apps, you can't fit all the
| state as query params.
|
| It's just not possible to calculate most stateful things. It's
| interesting to me since I am currently working on a state manager
| where we have many "apps" on in the same app. The user needs to
| be able to switch between apps and only some state is going to be
| shared.
|
| Thus, I need to store the state somewhere and it's not going to
| be in the url or in some calculated way since it's based of user
| selections.
| bryanrasmussen wrote:
| well I for one am looking forward to the "you don't need state
| for this shopping cart" article.
| account42 wrote:
| Amazon already implemented that feature for the "Free Prime
| Trial" offers.
| CSSer wrote:
| In production this would use transforms and/or will-change to
| avoid recalculating page layout. I'm on the fence about whether I
| should consider this a nitpicky implementation detail for this
| example. On one hand, it's only the logo, so there's no other
| elements to introduce jank to. On the other hand, it's important
| to know your platform when you're deciding what to optimize.
| spawarotti wrote:
| One generalization of this concept I see is: Instead of having a
| sequence of successive states, you only need the initial state
| and a function telling you how to compute the next state from
| previous one.
|
| You can also see a connection to a version control system like
| Git. Instead of keeping snapshots of all the contents of the
| repository after each commit, one can keep only the initial
| repository state and changes in each commit. Then to get to N-th
| state you say "Apply first N commits to the initial state".
|
| In the bouncing DVD logo example the "function to compute next
| state" or "commit contents" is just easy and regular, to the
| point of being expressible via simple math functions.
|
| This is also known as Event Sourcing.
| ekimekim wrote:
| > You can also see a connection to a version control system
| like Git. Instead of keeping snapshots of all the contents of
| the repository after each commit, one can keep only the initial
| repository state and changes in each commit.
|
| Not to invalidate your point, but this is a common
| misconception with git. Yes, many git commands will treat a
| commit as a diff compared to its parent(s), but the actual
| commit object is storing the full state of the tree. This still
| works out to be fairly efficient because if a file is identical
| between two commits, it is only stored once, and both commits
| point at the same stored file.
| account42 wrote:
| This is a common misconception with git. Yes, conceptually
| git will treat each commit as complete tree, but the actual
| pack files are storing deltas because anything else is way
| too inefficient. Loose objects are only the initial format
| stored before there is enough data for pack files to make
| sense.
| ghusbands wrote:
| The concept is what's important, here, since they were
| correcting the idea that git works mainly on diffs, which
| it doesn't. The diffs are merely a storage optimisation.
| spawarotti wrote:
| Thx, nice explanation. I think possibly Mercurial actually
| stores only the diffs, but I am not sure.
| lysecret wrote:
| Yes. Also storing each individual state can be seen as caching
| there is a place for both of course.
|
| Each derived state is a pain and has to be maintained but might
| be essential.
| 29athrowaway wrote:
| https://youtu.be/QOtuX0jL85Y
| titzer wrote:
| With the "animation" version, the logo jumps around when resizing
| the window. Yuck.
|
| I hate these hot takes. Maybe you need state because sometimes
| things have state.
| airbreather wrote:
| You still have state, it is just the problem has been reduced to
| one of the very minumum amount of state you need to represent
| desired functionality. e.g. current time.
|
| Not all problems reduce this far, just because one does it does
| not mean they all do.
|
| There are really only two ways of describing the world around us,
| by state or by function, and not all problems can be either. In
| tis casse the state (time) is applied to a function. This is good
| for some things and not neccesarily so good for others.
|
| If you think that there is some future of computing with
| universal stateless code you are delusional.
| MuffinFlavored wrote:
| > There are really only two ways of describing the world around
| us, by state or by function
|
| Hilariously, my girlfriend always yells at me "I'm not a block
| of code! I'm not 0 or 1! I'm not just on or off! Humans have
| feelings and emotions in the gray!" when I try to boil down "if
| this, then that" logic to real world problems.
| dtagames wrote:
| It's impossible to eliminate state, but the goal of functional
| programming is to have as little as possible. The original
| article does a great job of explaining how to do it and why
| that's an advantage in this case.
| klibertp wrote:
| To add to the sibling commenter - in my experience FP is not
| about having as little state as possible. It's about
| explicitly representing and tracking all state. OOP _hides_
| all state in private properties, FP _displays_ the state as
| function arguments. In the end, you want a healthy mixture of
| both approaches. Sometimes state is better off hidden, and
| then in FP you need closures - might as well use an object
| then, instead of emulating it by creating a closure that
| "responds" to "messages" by manually dispatching arguments in
| a giant switch. I think OCaml, Scala, and Clojure got it
| right, on a language level, but then the problems float up
| into design - ie. where to use a mutable record and where to
| use a functional stream, etc. I don't think I saw a
| compelling set of guidelines how to mix the approaches in the
| most effective way.
| FpUser wrote:
| >"but the goal of functional programming is to have as little
| as possible."
|
| The goal is to produce optimal software not to serve
| particular paradigm.
| mrguyorama wrote:
| Everything I have seen about "pure functional" programming
| has cared more about ideology than practicality.
| dtagames wrote:
| Indeed. If the goals of functional programming help out in
| a particular instance and that style works for you, then
| use it.
| Asooka wrote:
| An important difference is that you are not modifying the
| state, though it is still there. There is one problem - time is
| limited to the range of values the given type can contain and
| since it's often a floating point type, the farther from zero
| you go, the less precision you have. Depending on how exactly
| you use the time variable, the loss of precision can become
| catastrophic long before the ulps get larger than a nanosecond.
| catapart wrote:
| To be even more specific - you can only ever describe something
| by a function, and you have to supply a state in order to get a
| singular result. In physical space, the state we provide to get
| a result of any particular "object" is time. More simply: you
| can't describe any specific table with any meaningful
| properties without specifying the time in which you're
| observing the description. Every "thing" is just a transient
| state of being for at least one, if not millions, of variously
| interactive processes.
|
| Of course, none of that is relevant to how useful it is to be
| able to reduce "state" down to the singular (and mostly
| "invisible") property of "time". It's just that while you are
| technically correct that the article did not 'remove state',
| it's only as technically correct as saying a banana is a
| "berry". In the vast majority of use cases, including teaching
| people the value of something, the specification is a
| difference without a distinction.
| teddyh wrote:
| If something is totally deterministic, it can be computed
| _without state_ for any point in time, by simply running it
| iteratively until time == current_time(). Anything better than
| this, like in the article, is an _optimization_.
| mkl95 wrote:
| > Next time you find yourself rushing to use state, try spending
| some more time at the whiteboard
|
| It's mostly a tradeoff and a dubious one. I would rather have
| some "dumb stateful architecture" that can be debugged trivially.
|
| An example of how this pattern fails to scale is skeletal
| animation in 3D games. You will usually have some function that
| takes some ellapsed time as input (sounds familiar?) and
| multiplies together a bunch of matrixes to transform the joints
| of your model. You absolutely want your code to be a "simulation"
| rather than an "animation" when you need to make some adjustment.
| brundolf wrote:
| This is _almost_ a great demonstration of how refactoring code
| into pure functions can help with robustness and maintainability
|
| To get all the way there you need a couple tweaks:
| function update(time: number, random: number): { left: number,
| top: number } {
|
| First, the random number for each render gets pulled out into a
| function argument, making the contents of the function
| deterministic
|
| Second, the function now _returns_ left /top instead of setting
| them onto an element directly
|
| (we may also now want to rename it from "update" to something
| like "getPosition" to reflect its new role)
|
| With these two changes, this function is now _extremely_ easy to
| test, apply to other use-cases, etc. It 's nothing but
| input/output. This is something we in general should try to do
| with as much business logic as possible.
|
| (One caveat that's specific to animations: returning these values
| as an object instead of setting them directly may, in JavaScript,
| result in doing allocations on every frame which may harm
| performance. But this is only really a concern in real-time
| domains like graphics, where the function may be called 60+ times
| per second. It could also be avoided by splitting this into two
| separate functions, one for `top` and one for `left`, so the
| numbers don't have to be bundled up into an object to be
| returned.)
|
| EDIT: I just noticed it grabs the window and logo dimensions
| directly too. Those would also need to be pulled up into function
| arguments for this to be a pure function
| danhau wrote:
| > It could also be avoided by splitting this into two separate
| functions, one for `top` and one for `left`, so the numbers
| don't have to be bundled up into an object to be returned.
|
| This is probably a bad idea for performance too, as you'll
| potentially be doing lots of calculations twice.
| brundolf wrote:
| In some situations, though in this one I think they're
| actually totally independent
|
| But ofc in a real scenario where this matters you'd only want
| to optimize based on what a profiler says
| TheRealPomax wrote:
| Technical nit: "your _implementation_ might not need state ".
| Code only has one state: itself.
| burnished wrote:
| If there is only one unambiguously correct interpretation then
| it is probably fine to shorten the statement.
|
| Of course I am very curious about the sort of misunderstanding
| that you are trying to prevent, if you have any examples.
| ladberg wrote:
| If anyone wants to play around with an interactive demo of this
| kind of thing, check out https://textshader.com and select the
| "bounce" example!
___________________________________________________________________
(page generated 2023-03-28 23:01 UTC)