8 ms·
Monads Schmonads: Functional Input Without Tears (PYFL)
- loa_in_ 5y agoHaskell uses monads to encompass IO actions because IO is so much more than the stdout and stdin. The stderr, file operations, accessing raw memory, network connections and all sorts of global states are the domain of the IO monad.
- Blikkentrekker 5y agoFrom how I understand this approach, these are effectful functions that have the size effect of printing on a prompt when first ran. While it is true that they yield the same result back to the program on each invocation, so would OCaml invocation `Printf.printf "Hello, World!"`, it namely always returns `()`, but it is quite an effectful function, of course.
- sfvisser 5y agoNot entirely sure the author understands what problem monads solve and how it relates to his examples. If you just needs a fixed set of inputs without dependencies, conditionals, branching etc you indeed don’t need monads.
- aranchelk 5y ago> Haskell uses the seriously complex machinery of monads to do I/O, supposedly without side effects (I don’t accept this). The consensus of many highly intelligent people knowledgeable on the subject seems to be monads allow you to maintain a pure language while performing IO. This shouldn’t make it an incontestable fact, but probably should make disagreement worth more than a throwaway line — that is if you want to avoid looking like a crackpot.
- goto11 5y ago> monads allow you to maintain a pure language while performing IO I/O is by definition not pure, but the use of monads allow a strict separation between the pure and impure parts of the code. I guess you can say that monads allow you to have a guaranteed pure sublanguage.
- aranchelk 5y agoAgreed, and AFAIK if you’re implementing IO, it’s either in the runtime or you’re doing FFI, so bizarrely (again AFAIK) all of those statements are accurate. Edit: sorry I glossed over the last bit regarding a sub-language. The point is the whole language is considered (at least by many of its proponents) pure: monads + runtime + FFI are the gymnastics that allow one to perform IO in a pure language.
- goto11 5y agoI disagree. This code: main = do putStrLn "Hello, what's your name?" name <- getLine putStrLn $ "Hello " ++ name is not pure in any meaningful sense. At least not any more pure than the similar code in any other language.
- papamonad 5y agoThat code is 100% pure, it is a value of type IO (), it can be passed to pure functions, kept in lists. It is a pure description of some impure actions. This is run by the runtime. But within haskell is 100% pure
- goto11 5y agoThen so is this code: function main() { let name = prompt('Hello, what is your name?'); alert('Hello, ' + name); } This function can also be passed around, kept in lists etc. without executing it. So you can also say it is a specification of impure actions. It only has effect when executed by the runtime. So within JavaScript I guess it is 100% pure?
- papamonad 5y agoYou could almost say that, if there was any notion of purity in javascript. You don't get referential transparency within javascript so talking about purity in that case is really moot. The main point is: Within haskell we have purity, if you have Int -> Int it is referentially transparent, it can't do anything but take inputs and produce outputs. If we have an expression like let x = y + z y = 10 + z z = 3 these are just expressions that are reduced to a type and a value. IO () is a type, a higher kinded type that satisfies properties the same way a list does. This type has meaning and the meaning is some "Side effectful action producing something". we can manipulate, map and combine this type and it just represents some action to be performed, which is pure as it just represents the actions. The value is the actions to perform. A haskell program is an expression that reduces to the type of an action IO (). So while yes both the IO monad and the js can represent the same effects, it isn't equivalent when we are looking at something that was never pure to begin with and drawing the similarities of what they represent.
- Blikkentrekker 5y agoThat's not a consensus at all. Many are of the position that the IO monad is purely a type theoretical hack that allows one to control the order of effectful functions in Haskell that under the surface creates a vacuous data dependency that ensures the optimizer must sequence them in a particular order. There is no consensus as to what the IO monad “is”; only what it's specific semantics with regard to execution order is. But it serves it's purpose in that allows one to control the order of certain effects in another wise non strict language while semantically not necessarily relying on controlling the order of execution of function calls. On the type level, the effect of the function is separated from the calling of the function and it's effect need not necessarily take place conceptually when the function be called; under the hood, it simply ensures that the optimizer cannot move out of order the effectful kernel of the function by way of this vacuous data dependency.
- anderskaseorg 5y agoIt’s easy to demonstrate that the IO monad is pure by reimplementing it in 100% pure code. {-# LANGUAGE GADTs #-} data IO' a where Pure :: a -> IO' a Bind :: IO' a -> (a -> IO' b) -> IO' b PutStrLn :: String -> IO' () GetLine :: IO' String -- more IO operations… instance Functor IO' where fmap f ma = Bind ma (Pure . f) instance Applicative IO' where pure = Pure mf <*> ma = Bind mf (\f -> Bind ma (Pure . f)) instance Monad IO' where (>>=) = Bind The actual implementation in GHC is much more efficient, of course. GHC does all sorts of magic to hide the fact that it’s generating straight-line code instead of a tree-like data structure full of continuation functions. But it’s semantically equivalent.
- Twisol 5y agoYou're right, but I think there's a distinction here that is easy to miss. An IO action describes what to do, but something still has to do that action. A Haskell program on its own doesn't do anything; that's exactly why it's pure. The Haskell runtime interprets an IO value, unraveling it into the string of commands that comprise it. The runtime executes the commands produced by the Haskell program. Haskell uses monads exactly because it lets you describe/express complex sequences of actions (for which later actions depend on earlier actions) without requiring that the machinery for actually performing those actions be part of the program too.
- bade 5y agoI've proposed an approach that is simple, equation oriented and trivial to implement. I think a substantive discussion off its merits is more useful than arguing about consensuses.The Haskell alternative is to write what are obviously commands in what looks like C and I don't see this as pure. Sometimes the emperor has no clothes.
- aranchelk 5y agoThat’s a fair point regarding comments on the actual design vs its presentation in prose. My feedback regarding the design goals: As a working functional programmer, I find monads to be extremely helpful in many scenarios, I use them as much for validation, business logic, and parsers, as I do for IO. I also like how easily monads can be used to handle async with the same syntax as synchronous IO. Though I haven’t used Haxl yet, I find the idea of using applicative functors for free/automatic parallelism really interesting; I gather that a contributing factor for this being somewhat straightforward is because applicative is a superclass of monad. I also don’t believe monads are actually that complicated, just a scary name and many poor explanations floating around. All of this is to say I don’t find the design goals of eliminating monads very compelling.
- comex 5y agoHere is a question. Can you have an input whose prompt depends on the results of prior input calls? If you can’t, then this only solves a rather limited subset of I/O, where the program is essentially a pure function whose input is the set of answers to a fixed set of prompts. It’s sufficient for some use cases, but most real programs need more interactivity than that. If you can depend on prior inputs, on the other hand, then input() is not pure. Simply evaluating it has the side effect of displaying some text to the user. (Arguably this would be true even if the prompt couldn’t depend on prior input, as long as it was the act of evaluating input() that caused the prompt to appear. An alternative would be to treat input() as a sort of macro, where the interpreter statically analyzes the program to determine every prompt for every input() call that could possibly be made, asks the user up front for responses to all of those, then, when input() is actually evaluated, just looks up the already-entered response. That would make input() pure. And maybe you could argue that even if input() does display a prompt only at the time it’s evaluated, it’s sort of morally equivalent to what I previously mentioned, just saving time by not asking unnecessary questions. But that goes out the window if questions can depend on prior answers.)
- bitwize 5y agoMonads don't make side effects in I/O go away, but they do help you manage them. Crucially, the IO monad describes a computation which must then be run. Computations are describable in side-effect-free code. Running them is handled automatically by the Haskell runtime, and can be done without introducing side effects into the language.
- vmchale 5y ago> Haskell uses the seriously complex machinery of monads to do I/O, supposedly without side effects (I don’t accept this). Seems like the author doesn't understand what's going on.
- tromp 5y ago> And you end up writing stuff like main = do { putStrLn "Hello, World!" ; return () } which to me looks like C. There must be a more functional approach. It looks imperative because that's what Haskell's do-notation was designed to. But it's merely syntactic sugar for the functional equivalent main = putStrLn "Hello, World!" >> return () which btw is equivalent to simply main = putStrLn "Hello, World!"
- Choc13 5y agoYes! I've written about this from an F# perspective, which calls them 'computation expressions' instead of 'do notation', here https://dev.to/choc13/grokking-monads-imperatively-394a https://dev.to/choc13/grokking-monads-imperatively-394a If the author really doesn't like the "imperative" syntactic sugar then it's always possible to write equivalent code without it, but it quickly becomes painful for more complex situations. 'do notation' is just a more generalised version of what async/await does. No one wants to go back to writing async code with callbacks / continuations.
- dang 5y ago"Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something." He's a well-known language designer, famous for his work in dataflow, whose work was influential in the field. We should ask him to explain what he meant by that, before jumping to internet cheap shots. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- resoluteteeth 5y agoIf you really want your program's output to be a pure function of its input you can use the "interact" function in Haskell. I would recommend that the author try this and see the limitations, since it might help him start to understand why Haskell uses the IO monad.
- garethrowlands 5y agoIn the Haskell example in the article, the `do`, braces, semicolon and `return ()` are not necessary: main = do { putStrLn "Hello, World!" ; return () } The code should read like this instead (and I think the linter would suggest this): main = putStrLn "Hello, World"
- oisdk 5y ago[The original paper which introduced Monads to Haskell](https://www.microsoft.com/en-us/research/wp-content/uploads/1993/01/imperative.pdf https://www.microsoft.com/en-us/research/wp-content/uploads/...) is a fantastic read, and extremely easy to understand. It explains clearly why monads are a good option, and what problem they're meant to solve in Haskell. In the article itself, the line: > As it happens the computation will need the value of b before that of a. If this is a problem, we can write the definition of root1 as Is actually quite interesting: the problem being described there is almost identical to one of the problems that Haskell had with I/O, which motivated the introduction of monads.
- bradrn 5y agoOne question for the author: monads are not just a tool for IO but form a coherent abstraction over all side-effects. In my Haskell code, I personally have used monads for applications including parsing, fallible operations, IO, random number generation and testing, amongst others. For me, the most useful feature of monads is that they act as a coherent abstraction, allowing me to write code which can work over any of these domains. How does the author propose to replace this?
- bade 5y agoOther uses of monads are fine, I just think you can do better and simpler for IO.
- bradrn 5y agoSure, but to me the best things about monads are precisely those ‘other uses’. And if that means you still need monads, then why not use them for IO as well? That then gives you the advantage of unifying everything under the same abstraction.
- loopz 5y agoMonads are not strictly necessary in any language, and don't have to cover I/O at all. They are instead the same interface to a useful abstraction. They may as such be used together with all helper-functions and types, for many situations where one needs to wrap and unwrap data/types into more complex representations, and back again. They remain pure, since the actual side effects happen lazily outside of expression evaluations (by magic!). They can also assist in decoupling code from side-effects, while keeping code simpler and coherent. Like with Go, just more subtly, it seems Haskell directs the programmer towards better code design, by giving helpful errors from the type system. This is because the types ensures that you avoid mixing what should not be mixed. If there's alot of errors and progress is slow, this is to be expected at startup, as you will be directed to find the better code structure to build your shell on. Once the shell is more complete, it will be easier to incorporate the functional core in that, or vica versa. So in that area also, it is helpful and useful, once one groks the hints of linter and compiler errors (use an IDE for this).
- smsm42 5y agoSomething looks wrong to me though: a = input(‘coefficient of x squared’) is not a function. Well, it's not a function in a mathematical sense - an entity that provides a mapping between two sets (with some bells and whistles but let's leave it aside for now). Since the result of this "function" does not depend in any way on the input parameter and can be anything, that doesn't look like a functional solution of anything, it's just declaring that the issue does not exist. I mean, most non-functional languages don't care their functions aren't functions, and they are completely fine with it. But if you specifically declare you're going for functional approach and mention Haskell and IO monads, maybe you should?
- tome 5y agoYou have invented an impure[1] lazy programming language. I have an article explaining why that probably won't be very useful: http://h2.jaguarpaw.co.uk/posts/impure-lazy-language/ http://h2.jaguarpaw.co.uk/posts/impure-lazy-language/ [1] Impure as per https://news.ycombinator.com/item?id=27259900 https://news.ycombinator.com/item?id=27259900
- tome 5y agoLots of tedious back-and-forth in here about how many pure angels can dance on the head of a side-effecting IO monad, as usual. The desirable properties that we are trying to enforce are that, firstly x = expression y = expression ... is the same as x = expression y = x ... and that, secondly x = expression ... is the same as ... when x does not appear in ... . These conditions are an order of magnitude more important than any considerations about what "purity" really means, what side effects are versus effects, whether there's an impure runtime below a pure language and whether IO is a "sublanguage".
- Purlox 5y agoHow is this more functional than IO in Haskell? Your approach seems to be the same as I/O handling in Haskell minus the convenience of monads and plus caching. Not all that different.
- deleted 5y ago[deleted]