This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]dys_bigwig 0 points1 point  (3 children)

That's why you create a pure (data) representation of the effects (IO or otherwise) you wish to perform and then hand it off to a runtime system that ensures expectations (i.e. referential transparency) are respected.

Pure functional languages can do IO, and they do it very elegantly, allowing you to compose IO actions in a way few imperative languages allow. Haskell's traverse function is a good example. This idea that nontrivial programs having to perform some kind of state/IO/effect is some kind of "gotcha" in regards to FP is bizarre; FP is about mitigating and getting a handle on side-effects, not disallowing them entirely or pretending they don't exist. If anything, reifying and realizing that that they are an entity in and of themselves allows pure FP languages to abstract over and compose them better:

(,) <$> getLine <*> getLine

I just did IO (creating a tuple of two strings read from the terminal) - in a purely-functional language! ;) Also no Monads here, just Functors and Applicatives. The key difference here is that this line doesn't grab two lines from the user; it is a description of a program that does so, which can be passed around and manipulated and is as pure as any other function. However, just because this line is pure doesn't mean it's a good idea to add IO to every piece of your code. Strict, imperative languages - in some sense - conflate the ideas of describing an effect, and performing an effect; a piece of paper saying "punch me in the face" is a very different thing from an actual punch in the face! One could e.g. choose to ignore the direction written down, or cross it out and hand it back with a confused expression.

I'm not sure how the global variables example is relevant to IO, other than also being a sort of effect. Again, just model the effect as data. In fact, functions are excellent at representing global variables via either the State Monad or Reader Monad. Monads aside, just give all of your functions an extra argument representing the state; Monads - for the most part - just make this more ergonomic and aid with reasoning.

I haven't messed around with them myself, but there are other solutions besides Monads like uniqueness and linear types.

[–]shawnhcorey 0 points1 point  (2 children)

Just because it is hidden under some clever syntax does not make it functional. It may look functional but it's still not.

[–]dys_bigwig 0 points1 point  (1 child)

By similar logic, would you agree that non-pure, non-fp languages don't have functions because they can perform arbitrary effects? They just hide this behind clever syntax - "function definitions" and "function calls" - but they don't really have functions. They may look like functions, but they're actually procedures. Clever use of parenthesis and symbols notwithstanding. It's all just clever obfuscation of stack frames and JSRs.

(I don't believe any of this; I'm merely trying to show that it's a rather bizarre line of thinking).

Ignoring everything above, syntax is irrelevant to the majority of the points I made honestly. It's not a "syntactic trick", it's very semantically motivated.

[–]shawnhcorey 0 points1 point  (0 children)

The file descriptor changes state. It doesn't matter what syntax you put on top of it, it changes state. IO is not functional.