4 ms·
> in that case, without adding impure statefulness, how does one differentiate between "a paused frame is being displayed" and "an intermediate frame of a playi
by jbmilgrom 6y ago
> in that case, without adding impure statefulness, how does one differentiate between "a paused frame is being displayed" and "an intermediate frame of a playing video is being displayed"
A "paused" frame could be represented like Fi, Fi+1 ... Fi+N, etc; for every moment i..(i+N), F remains the same and is thus "paused." The thing that's tough to wrap your head around is our language is rooted in object-orientation however. "Pause" is less of a meaningful term when time is discretized. See also https://softwarefordays.com/post/fp-as-a-scientific-revolution/ https://softwarefordays.com/post/fp-as-a-scientific-revoluti...
- jbmilgrom 6y ago> then at the end, the example from elm looks great, but i see lots of impure state being generated (browser, model) or am i missing something? All functional programs (even if written in Haskel) have side effects - the CPU only has a certain amount of registers that can be used, for example, so any nontrivial program will be constantly overwriting them. That elm program however only exposes pure functions in user land, just like with a Haskel program
- andrekandre 6y agothanks for the reply i guess what i was thinking was, if there was a ui, with a play/pause icon, and playing the video should cause the play icon to switch to a pause icon (because video is now "playing" so we want to be able to click it and "pause" the video) how do we represent that in a purely functional way? since at any "timeless" instant, t1 vs t2 is just a frame1 vs frame2 issue at the language level (?), how in a purely functional world, would we determine that video is indeed playing and that icon should be swapped while playing vs not? does user-interactive systems imply that there must inherently be some impurity?
- usmannk 6y agoConsider pressing a "play" button to first start playing the video but then also remove itself from the UI and replace it with a "pause" button that when clicked does the inverse. The UI here is acting as a state store but the functions can then be designed as purely functional, acting only on their inputs.
- andrekandre 6y agoah, that makes more sense i think... yes, i guess that is one way of accomplishing it, thanks ^^
- usmannk 6y agoI don’t recommend this strategy in practice though. It’s brittle and not all application state can even be represented as UI (what about your login cookie?).
- jbmilgrom 6y agoIn this case, the play/pause button becomes part of the "video." So now the video includes a piece of state which has perhaps been toggled to "paused." And at that point F(i) = F(i+1)... = F(i+n), since nothing will have changed for n moments (until the user interacts again). Kinda hard though to fully get this in the abstract. I struggled with the same questions. All I can say is SICP, all of Rich Hickey's videos, redux, elm, resources listed on haskel.com all helped. Also putting it all together in writing here https://softwarefordays.com/post/functional-programming-and-identity-state-and-time/ https://softwarefordays.com/post/functional-programming-and-.... In this^ blog I've included an interactive user interface that can be seen as purely functional, even though it's a stateful ATM program. I'm actually hoping that this blog could help teach someone what took me 2 years to finally realize. I think it could be worthwhile given your questions - if you take a look, please let me know what you think!
- andrekandre 6y agoSICP has been on my list since i saw it on a post about alan kays recommend reading but never purchased a copy yet... thank you for posting a link there to it, ill hopefully get to it soon!
- didibus 6y agoUser interactions would be modeled as a stream of events. And your functions would take that stream of events and return the rendered video player as it should be based on the given events. There's two way to do it. One that requires keeping the full history of events, and another which only requires keeping the next one (or next few) and the result of applying the last ones. I'll start with full history. You'd have something like: defn button-icon(click-events): if is-even(count(click-events)) return paused else return play With this, when the program starts, click-events has nothing in it, so when we call button-icon with it, it returns paused, if the user clicks the button, we call button-icon again and now there is one click event in click-events, so we return play. If user clicks again, there are now two click events and so we return paused, and so on. There is still state in the running program, something is remembering all clicks to the button, but your UI logic is pure. Ok, now this is inefficient and requires lots of memory. Basically every new user action we recompute everything from the program start, and remember all prior actions. That's why there's the second approach. Instead we will do: defn button-icon(current-button click-event): if click-event if (current-button = paused) return play else return paused else return current-button Now the running program won't remember the list of all click events from the program start, instead it'll remember the last result from the last call to button-icon and it'll pass that last result to button-icon the next time the user takes an action. This can be bootstrapped recursively or using fold.
- andrekandre 6y agoi see, those are definitely ways to do it... > There is still state in the running program, something is remembering all clicks to the button, but your UI logic is pure. could this be solved by having the whole app be some kind of recursive function that passes the events as a list that gets appended to over time? then it could remain completely pure?
- didibus 6y agoYes that's exactly how it would be bootstrapped, but that still will leave the IO itself, and that part will be impure. So as you recurse, even if you carry over the events by passing them back, you'll need to have a part where you block and wait on external input from the user, and that is inherently an impure operation. You can keep extracting the IO out, but you can't get rid of it. So Haskell or Elm would basically do the "dirty impure" IO on your behalf, so in your code it wouldn't even seem like you're doing impure IO, but your running program still will, just it'll be done by the Haskell/Elm runtime on your behalf. Or if you're not using those languages, the best you can do is extract it to your top level function.