3 ms·
I read the article, and it annoyed me, because the rebuttal is obvious and wasn't addressed. The rebuttal is do-notation. The second program you write after He
by millstone 12y ago
I read the article, and it annoyed me, because the rebuttal is obvious and wasn't addressed.
The rebuttal is do-notation. The second program you write after Hello World is going to use two IO actions instead of one, and so you need a way to sequence them, and every tutorial is going to do that with do-notation. And suddenly all of the other syntax you learned, like how to declare a variable with let-in, or how arrows go -> that way, or how to call a function, is tossed out the window. And you'll spend ten minutes before you realize your indentation is wrong and then ten more fighting with how you declare a variable and then apply a function to it before it dawns on you, this is monads.
You don't have to use do-notation, sure. You can use >>= as an infix operator. It's still totally confusing.
The reality is that monads hit you hard and fast as soon as you want to do any IO. Not the monad laws or functors or whatever the tutorials focus on, but the nitty-gritty syntax. This is where I got stuck and gave up (twice), before I finally got it.
- platz 12y agoYes, you'll need them to build up bigger programs. The point is they aren't fundamental to IO itself.
- rjberry 12y agoThey pretty much are as without sequencing you can only do the most trivial thing (a single IO action). Most interesting programmes are going to need at least two IO actions.
- olavk 12y agoYeah, no way around learning do-notation even for the simplest of programs. And do-notation is kind of a DSL with a totally different feel than the rest of the language. So getting started writing even simple toy programs in Haskell requires you to learn two languages. No denying that Haskell is a language with a very steep learning curve. (Or is it a very shallow learning curve? Never understood that metaphor. But you have to learn a lot to get started.) However it might make sense in a tutorial or course to teach do-notation first as a DSL for IO in order to get started. And only later, when the student have a good understanding of the core language and type system, introduce monads and show how IO is a monad and how do-notation can be used with other monads. The problem with the much maligned monad tutorials is that the audience typically is beginners who get stuck on on the IO monad and do-notation immediately after writing "hello world". But the tutorials try to explain monads as a general concept, which requires a pretty through understanding of things like higher kinded types and so on. An understanding the newbie getting stuck at "hello world" obviously doesn't have yet. Then the tutorials try to be accessible by using metaphors like a "monad is a specesuit", a "monad is burrito" etc. But knowing that a monad is a buritto doesn't help the newbie who gets inscrutable compile errors after adding a second putStrLn to "hello world".
- bmh100 12y agoIn operations management, a "learning curve" is an efficiency curve plotted over time. I have most often seen it as a time-per-task-completed curve. The way it works is this: The time it takes to produce widget 2 ^ t is BASE_TIME * LEARNING_RATE ^ t. Example with 120 minute starting time and 90% learning rate: Widget 1 (2 ^ 0) takes 120 (120 * 0.9 ^ 0) minutes to produce. Widget 2 (2 ^ 1) takes 108 (120 * 0.9 ^ 1) minutes to produce. Widget 4 (2 ^ 2) takes 97.2 (120 * 0.9 ^ 2) minutes to produce. Widget 8 (2 ^ 3) takes 87.48 (120 * 0.9 ^ 3) minutes to produce. Example with 120 minute starting time and 70% learning rate: Widget 1 (2 ^ 0) takes 120 (120 * 0.7 ^ 0) minutes to produce. Widget 2 (2 ^ 1) takes 84 (120 * 0.7 ^ 1) minutes to produce. Widget 4 (2 ^ 2) takes 58.8 (120 * 0.7 ^ 2) minutes to produce. Widget 8 (2 ^ 3) takes 41.16 (120 * 0.7 ^ 3) minutes to produce. If you plotted a curve of minutes per widget over time, you would note that the graph for the 70% learning rate would have a much steeper slope than that of the 90% learning rate. Looking at that you might say, "ah, high learning rates are great!" All else being equal, you would be correct. However, real world tasks with high learning rates often imply high base times and a proportionately large amount of time until proficiency or mastery is reached. That's why "steep learning curves" are "bad things", because it is going to take "a lot of effort" just to "get good" at the skill. To bring the point home, here are some contrived examples of "90%" and "70%" skills: Version control: 90% - copying and pasting files 70% - git Word processing: 90% - Microsoft Word 70% - vim and latex Databases: 90% - Excel documents in a shared folder 70% - PostgreSQL Web design: 90% - your web host's website builder 70% - Ruby on Rails 70% - hand coding in notepad This is just an introduction to the concept as I learned it. Two skills might have the same learning rate, but one might be much more productive than the other. Still this should help you understand the origin of "steep learning curve" and why that's usually considered a "bad thing."
- jle 12y agoyea, but the struggle isn't really quite with monads, but with syntax. properly understanding the syntax for do notation with use in IO does not require an understanding of monads.
- exDM69 12y agoThe issue isn't as great as you portray it to be. You're facing beginner issues and learning a foreign topic, expect to spend some time on it and put in the mental effort and you'll master it in no time. After spending an hour to write "Guess the number" game with and without do notation, you should know enough to be able to write practical programs in Haskell. Yes, an hour for guess the number is a lot, but it's the learning part that takes the time, not just putting a program together. It makes perfect sense to have do notation and desugaring it to use >>=. For "imperative" programming, do notation makes sense. Other times you'll be writing data processing "pipelines" where using >>= is more convenient. To give an example: readQueries inFile >>= readFromDb db >>= writeOutput outFile The above would be very clumsy to write in do notation (but not too difficult, that's what you'd do in an imperative language). But writing "guess the number" without do notation is also clumsy (but a good learning exercise). do notation and monad laws may be a foreign concept when coming from an imperative programming background, but it's also a neat an elegant concept that works very well in practice.
- millstone 12y agoI have mastered it, but I have not drunk so much Kool-Aid that I have forgotten what it took! It sure did not take "no time," and much longer than an hour - longer than any other programming language I have attempted. The biggest stumbling blocks were syntactical. For example, there are three ways to declare a variable (let-in, let, and <-) and if you use the wrong one, you get a very unhelpful error message. The learning was not so much new concepts but instead things like how a "not in scope" error means you used a tab instead of spaces. I completely agree, the underlying concepts are elegant, and powerful and enlightening and quite amazing. But you can understand the concepts perfectly and still not be able to write a line of Haskell. That's where most tutorials fall short.
- exDM69 12y agoPerhaps you interpreted me a bit too literally. Of course it takes time for internalizing new and foreign concepts. The lecture, lesson or exercise might only take an hour but it may take days or weeks to grok the concepts. And this might mean a lot of practice. I certainly remember having a hard time understanding this years ago, and my problem was probably the same as yours - the lack of nice books and tutorials (but things are a bit better now) as well as it all being new and foreign. I don't regret putting in the effort, though. I also do not like the inconsistency in let-in vs. let (do notation), but I guess there's a good reason for that. <- is not strictly "variable assignment" but it seems superficially similar, though (if comparing to imperative languages). But I don't think these issues can be "fixed" without turning Haskell into something completely different (e.g. something more like ML). Or do you have a good suggestion how to simplify this without making it worse?
- djipko 12y agoHe actually does go into that in the "Some side notes" part. He recommends calling it a monad only when you actually use the monadic interface: “I have to directly use the result of one IO action in order to decide which IO action should happen next”: Yes, this is a use case for IO’s monadic interface." Then yes - you need the do-notation. For simple sequencing he recommends to use it's Applicative instance. Your overall point is correct though - choosing to avoid the do notation in Haskell will likely limit your ability to read and understand a lot of code just because monads are so commonplace in Haskell.