[1] https://en.wikipedia.org/wiki/Eternalism_(philosophy_of_time...
How side effects work in FP
21–30 of 107 posts
Re: How side effects work in FP
#22"How to do side effects in s/FP/Haskell" Erlang is a functional programming language and doesn't need to jump through hoops to do side effects. So is OCaml. So is...
Somehow there is this school of thought that FP === Mirada/Haskell, go figure.
Haskell is referentially transparent, but you can't say the same for many other FP languages like erlang, or ocaml.
Re: How side effects work in FP
#23"How to do side effects in s/FP/Haskell" Erlang is a functional programming language and doesn't need to jump through hoops to do side effects. So is OCaml. So is...
You could finagle your way around that, but the result would be something like unsafePerformIO in current Haskell, where it's hard work to ensure that it's used correctly.
Re: How side effects work in FP
#24"How to do side effects in s/FP/Haskell" Erlang is a functional programming language and doesn't need to jump through hoops to do side effects. So is OCaml. So is...
There is a reason for this, and it's not that Haskell is jumping through hoops unnecessarily. All those languages are strictly evaluated, which means that side effects are clearly localized in time and space, we know when and where they happen if we run the program. Haskell is lazy, and laziness doesn't go well together with ad-hoc side effects. Haskell evaluates expressions when the result is needed, and an expressi…
No where was this more apparent in my career then when I was on a team of Scala-using FP programmers who were building part of a real hardware provisioning system (and the org structure tipped in their favor) - the number of arguments about the sheer amount of things which can and definitely would go bad across hundreds of servers when you tried to do things - versus their desire to ignore these problems as infrequent so they wouldn't have to try and code the handling in a functional way (FP is terrible at logging, so even getting that done adequately was an argument).
Basically the paradigm does tend towards collapsing once the problem grows too complicated for someone to keep in their head because it actively fights the sort of procedural reasoning humans do pretty much natively in favor of dealing with abstract math which very few people are any good at.
Re: How side effects work in FP
#25Well, that's the first time side effects in Haskell made sense to me. Well done. Not that I've ever seriously tried to learn Haskell, but in the past every time I've lazily come across an article about it it's always seemed like a bizarre confusing world, even though I know how functional programming (in the sense of purity) works. Now there's just one thing missing. We all know what this style of programming is. It'…
While you may oversimplify a little, I think the core idea of your comment is pretty useful for learning. Unfortunately the first monads one encounters when learning Haskell are typically Maybe and IO. There are good practical reasons for this, but an early analogy to Async would probably make the idea a lot clearer. Self-plug: I even wrote a short post about this quite recently, after making a similar comment on HN…
Re: How side effects work in FP
#26"How to do side effects in s/FP/Haskell" Erlang is a functional programming language and doesn't need to jump through hoops to do side effects. So is OCaml. So is...
Re: How side effects work in FP
#27Earlier quoted context omitted.
I like the analogy with musical instruments. Very practical. I think you're right in saying that async is not representative of all monads, but it does help with questions that many beginners have, like "How do I get the value out of the maybe/IO/your-monad-here?"
I don't think it generalises that well, unfortunately. How to get the value out of Maybe is "just pattern match it". There's nothing process-like about it at all -- it's just a plain container/wrapper. The async analogy works for some monads like ST and IO and whathaveyou but it's not generally useful, due to the wide variety of types that are monads.
In contrast, there are easily understood reasons to use the monad interface of a promise/async. That's why I think it's a good monad for understanding the structure of monadic binding.
The way I see it is kinda like this, and I hope it makes some sense to others:
You can get the value out of a list... but there might be many or none. You can get the value out of a maybe... if it's there. You can even get the value out of the async function... but you'll just have to hold up what you're doing to wait for it.
This kind of (roughly) non-determinism is how I understand the concept of "effect", which is what Haskell frequently uses monads to model. A function returning a `M a` becomes "deterministic" (it always returns an `M a`) and benefits from the sort of equational reasoning that we like.
To be clear, the use of monads to represent effects is a software engineering decision and not inherent to monads.
It's definitely helped my understanding and I suspect it would help others too.
Re: How side effects work in FP
#28> Equational Reasoning Probably the best reason to use FP imo. It just cuts out so much overhead when trying to read other people’s code.
Re: How side effects work in FP
#29So this is basically Eternalism[1]? Instead of writing a program that reads inputs, experiences a sequence of state mutations and takes actions, you generate the graph of every possible state and path between states annotated with inputs on the edges and outputs on the vertices, and let the runtime pick the actual path through it. And this works because you're generating it lazily, but that's just an implementation d…
Functional programs don't _do_ anything, they are a list of commands some external interpreter (what you call the runtime, but doesn't have to be a runtime) will execute or compile.
Functional programs only contain descriptions of the various commands, but it is an external interpreter executing the commands.
Functional programs return "recipes".
"recipes" are picked up by some executor that will turn them in dishes (the side effect).
If all of this seems generic allow me some typescript.
```
// we can declare a a function that takes no argument and return a value IO.
type IO = () => A
declare const program: () => void; // thus can be rewritten as
declare const program: IO // this is the PURE function I can reason about
declare const execute: IO => void // this is the interpreter that will execute the commands and have side effects
```
Now, let's declare a side effectful functions:
```
const log: (s: string) => IO
// desugarized: (s: string) => () => void `` `
Notice how the signature is similar to the standard console.log: (s: string) => void
except that it's lazy. Laziness is one of the most convenient ways to express "commands" rather than side effects in a language like JavaScript, but there are also alternatives.
Now we can have a pure program that logs to the console some string.
`log("foo")` does not "execute" any console.log, it still needs to be executed:
```
const program: IO = log("foo");
// program() will actually print "foo" in stdout
```
Note: we could've encoded IO in different ways, e.g. with a struct/interface rather than a function and had the interpreter actually reason about the side effect on its own.
Now, the only missing part is: how do I compose such IO functions together? Well, there are various ways but the most common ones are applicative functors and monads. I am not going to delve deeper in this comment on those topics because it would take long but I hope I have transmitted my point:
in functional programs you return programs (I like to think about them as recipes, recipes don't DO anything), those programs may compose effectful commands, the actual execution of the commands is shoved inside an external interpreter.
This is quite obvious in some languages like Haskell, it's less obvious in others.
Re: How side effects work in FP
#30E.g. you may have a dry-run function that doesn't actually change the state but only describes what it would do. Or you could have a function that generates a verbose log of all steps executed. Etc.
That doesn't necessarily work for IO, as that gets special treatment by the runtime, but you can do it with your own types.