Live data from Hacker News

How side effects work in FP

chadnauseam.com

71–80 of 107 posts

Re: How side effects work in FP

#71
post #67

Earlier quoted context omitted.

I like to explain functional programs like this. 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 u…

This I can understand. But why would someone want to do this? It feels like mental overhead. So what advantages are there to a system like this?

You do it if you're using a language where there's a rule that there are only functions. For example there's no concept of doing one thing followed by another. The only way you can arrange that is to have two functions and compose them: g(f(x)). So now you know that the code in g is executed after the code in f. Monadic composition is basically about doing that while having code that looks like it would in a regular language that has statements and sequential execution. There are a few other reasons, such as you can create your own control flow as-code, lifting, etc. But the primary driver is: because you can't write useful programs in a pure functional language without this trick.

Re: How side effects work in FP

#72

> 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.

A related advantage is expression-orientated code. In ML languages, code forms a beautiful tree structure where you can see the binding definition of each binding by looking down and right. let x = something really big and complicated whereas in a language built around statements it will be scattered all over: let x = null; x = something(); x = somethingElse(x); x = anotherThing(x); In the first case the definition o…

You are free to structure it differently:

    let x = anotherThing(
      somethingElse(
        something()
      )
    );

Re: How side effects work in FP

#73
post #55
post #48

FP explanations like this annoy me: That is, this code: x = foo() y = bar(x) z = bar(x) Should mean the same thing as: y = bar(foo()) z = bar(foo()) Because it's obvious to anyone that has written any sort of complex program that those are not the same thing. foo() can be an expensive operation like an HTTP call. Or it might depend on a database which can change state underneath it. I assume FP has answers for these…

Should mean the same thing as means has the same result value here. The operational semantics could be different, as you mention: even without side effects, computing x may be very expensive. This isn’t even special to FP, it’s a basic problem when building a compiler, namely whether inlining/common subexpression elimination is beneficial. To drive the point home, even in a language like Haskell these two examples mi…

In theory you're right, but in this specific case they really are the same - the compiler has an optimization pass called "common subexpression elimination" that converts the second to the first in nearly all cases.

Re: How side effects work in FP

#74
post #62
post #54

Earlier quoted context omitted.

> Because it's obvious to anyone that has written any sort of complex program that those are not the same thing. They are the same thing in Haskell (except for when forcing the thunks into eager values happens, due to the weirdness of laziness, but that has nothing to do with purity). > I assume FP has answers for these things but the tutorials never cover them. Except this one does: > The

>They are the same thing in Haskell That just raises a different problem: y = bar(generateUUID()) z = bar(generateUUID()) >Except this one does: It doesn't explain it in the context of real world programming.

Example program to show three ways of doing that:

  import System.Random

  randomInt :: IO Int
  randomInt = randomIO

  isEven1 :: Int -> Bool
  isEven1 n = (n `rem` 2) == 0

  isEven2 :: IO Int -> IO Bool
  isEven2 n = do
      m 
In example1, result1 and result2 may differ, which is signalled by the arrow (instead of "="). result2 and result2' cannot differ, which is signalled by "=", where the right hand side is verbatim the same.

In example2, result1 and result2 may differ, which is signalled by "In example3, result1 and result3 may differ, which is signalled by "<-" (instead of "="). Another argument here is that they may differ, because example3 is the same as example2, except it used randomIntIsEven in place of isEven2 randomInt. But we defined randomIntIsEven to be equal to isEven2 randomInt, so result1 and result2 being able to be different in example2 but not in example3, or vice versa, would be a contradiction.

Re: How side effects work in FP

#75
post #69
post #62

Earlier quoted context omitted.

>They are the same thing in Haskell That just raises a different problem: y = bar(generateUUID()) z = bar(generateUUID()) >Except this one does: It doesn't explain it in the context of real world programming.

> That just raises a different problem: y = bar(generateUUID()) z = bar(generateUUID()) You can't write that in Haskell! Instead you write something like. yuuid

Yeah, I'm not denying that these things are solved in Haskell or other FP languages. People build complex applications in those languages so all things must be possible one way or another. My complaint is that the tutorials never wade into these weeds and show how FP makes real world applications easier to build.

Re: How side effects work in FP

#76

Earlier quoted context omitted.

Actually the most important part of monads in Haskell is the do-notation. Without that, monadic code would look like JS' callback hell (although composition of monads using monad transformers/monad stacks isn't much better). Haskell without laziness is Purescript, btw.

I still think that it would look a lot less bad than JS callback hell, mostly because of syntax. actA >>= \a -> actB a >>= \b -> pure doSomethingWith b is a lot better (to my eyes) than: actA(a => actB(a, b => doSomethingWith(b) ) ) and it gets worse from there (blocks to have statements to declare variables, or to call multiple functions. pre ES6 those would've also been functions rather than lambdas. Admittedly mon…

Yes, that's true, the infix bind and general Haskell syntax helps with the readability of the chained function calls.

Re: How side effects work in FP

#77
post #18

"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.

As someone who is just getting into FP I’d say Haskell was the most famous language I knew of before a month ago. Lisp might count but it isn’t widely known to be a functional language most people just think it’s a weird language.

Re: How side effects work in FP

#78

Earlier quoted context omitted.

Actually the most important part of monads in Haskell is the do-notation. Without that, monadic code would look like JS' callback hell (although composition of monads using monad transformers/monad stacks isn't much better). Haskell without laziness is Purescript, btw.

A lot of people I've talked to seem to think Purescript is a better Haskell.

Disclaimer: personal opinion Having used both extensively over the years... I'll have to disagree. Don't get me wrong, purescript is awesome in its own way, but it falls short of haskell in a few major ways. - Lacks ergonomics (for example it has better extensible records, but they are clunky to manipulate due to the lack of features at the type level)

- as of now it's tied mostly to the JS ecosystem and runtime(s): that means no TCO, which _really_ hurts when using monads that require on a lot of recursion. And in fact purescript sometimes has to take a performance penalty and use specific trampolined stack-safe monads to avoid stack overflows

- Haskell layout rules may be complex, but purescript's break in weird and unintuitive ways (by layout rules I mean the indentation-sensitive syntax)

- Haskell is catching up on _a lot_ of the feature that made/make purescript great (mostly talking about syntactic things right now, like RecordDotSyntax, QualifiedDo, BlockArguments...)

- as it turns out, when programming "Haskell-style", laziness is really damn effective. So many times I've had to forcefully (and painfully) introduce laziness in purescript to make things behave well...

As much as I like purescript, I think if you want that kind of strict programming, maybe OCaML or a derivative thereof (or maybe even ReasonML/Rescript?) would work better? I feel like whenever I use purescript it looks so similar to Haskell that I try doing things the haskell way and it just doesn't work, and the "purescript" way will often look weird to a trained haskeller.

Re: How side effects work in FP

#79

Earlier quoted context omitted.

Actually the most important part of monads in Haskell is the do-notation. Without that, monadic code would look like JS' callback hell (although composition of monads using monad transformers/monad stacks isn't much better). Haskell without laziness is Purescript, btw.

A lot of people I've talked to seem to think Purescript is a better Haskell.

Yes, it does do some things better than Haskell. And row types and row polymorphism are a really nice way of handling records and some type level programming.

Re: How side effects work in FP

#80

"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...

I'm a professional OCaml developer, so I definitely know and agree with you :p I still think Haskell is "more FP" than OCaml because it allows equational reasoning in more cases. I don't think it's a problem to describe the platonic ideal of functional programming, since even in OCaml the pattern I mentioned is useful (for example a very common OCaml pattern is using the let-monadic syntax to simulate do-notation when using LWT)
Post reply on HN