4 ms·
I think your suggestion is a relative improvement over most tutorials. But it's still unmanageably hard. "Here's how to read something and then print that: >>=
by millstone 12y ago
I think your suggestion is a relative improvement over most tutorials. But it's still unmanageably hard.
"Here's how to read something and then print that: >>=". Sure, like `getLine >>= putStrLn`. Technically speaking, that's correct. But this doesn't generalize at all.
Maybe I want to greet the user by name. How do I read the name, prepend "Hello, " to it, and then print that? This is a totally natural thing to want to do, and is very straightforward in Java. But our Haskell newbie hits a wall - none of your cases cover it!
Here's how I might write that today, without using do notation: `getLine >>= (return . (++) "Hello, ") >>= putStrLn`. Look at that - it's totally nuts, and I would not attempt to explain that to a Haskell neophyte.
Using do-notation, you can make things nicer:
do
name <- getLine
putStrLn $ "Hello, " ++ name
but this introduces a bunch of new syntax to learn: the difference between let-in and let and <-, how do-notation works, etc.
I don't have a better approach than what you suggest. I suspect that this stuff simply can't be made easy.
- JoshTriplett 12y ago> Here's how I might write that today, without using do notation: `getLine >>= (return . (++) "Hello, ") >>= putStrLn`. Don't write point-free code here. Here's a fairly simple version: getLine >>= \name -> putStrLn ("Hello, " ++ name) The only novel thing there is the >>= operator itself. You don't even have to introduce return there; you can introduce return later.
- millstone 12y agoThanks, that's much better than what I wrote. Still I think that's a big jump for most programmers - it certainly was for me.
- rapala 12y agoWhich can then be made to look exactly like the getLine >>= putStrLn case by doing greet name = putStrLn ("Hello, " ++ name) main = getLine >>= greet (Edited to include the definition of main)
- ggchappell 12y agoThis is a nice approach. Thanks to all of you in this thread. I think I'll try something like this when I introduce Haskell I/O in my Programming Languages class in a month or two.
- millstone 12y agoIf this is meant to be under main, you need a "let": main = do let greet name = putStrLn ("Hello, " ++ name) getLine >>= greet For whatever reason, I didn't learn about the magic let syntax in do-notation until relatively late. Anyways this is very elegant, but not something I could have generated early on, especially because it mixes two different sequencing syntaxes (do-notation and >>=).
- wz1000 12y agoThe 'do' is superfluous. It would be much better as main = let greet name = putStrLn ("Hello, " ++ name) in getLine >>= greet
- exDM69 12y agoYou can use "fmap" to make this nicer (but that introduces yet another concept). If you use hlint on the program above, you it will tell you to use fmap instead. fmap ("Hello, " ++) getLine >>= putStrLn I learned this because hlint suggested it to me. Using hlint is a very good idea, even for beginners. For a simple "greet" program, this isn't perhaps any neater or cleaner but fmap is a powerful tool with lots of practical uses.
- davedx 12y ago> getLine >>= (return . (++) "Hello, ") >>= putStrLn And my coworkers yesterday were complaining that ClojureScript looked like Brainfuck... (http://en.wikipedia.org/wiki/Brainfuck http://en.wikipedia.org/wiki/Brainfuck)
- exDM69 12y agoThat's a very contrived example you obviously didn't spend any time trying to understand. No-one in their right mind would write code like that, except to demonstrate something. And if you did write code like that, Haskell's hlint tool would make suggestions to turn it neat and readable using fmap.
- pestaa 12y agoWhy wouldn't I want to write that? Clearly you need a little practice before instantly recognizing a partially applied function where the next parameter will be appended to your other string, but that one-liner has actually a very nice computing flow.
- exDM69 12y agoThat's a bit hard to read, isn't it? (return . f) is a common idiom (or antipattern?) which is better solved by using fmap. hlint will tell you this. For example: fmap ("Hello, " ++) getLine >>= putStrLn This isn't necessarily better either, I'd write this using do-notation. I understood every bit of that code, I just think it's ugly and hard to read and there are better ways to get the same done.
- dllthomas 12y agoI don't like it because the flow of data keeps changing direction. I would prefer one of getLine >>= ( ("Hello, " ++) >>> putStrLn ) putStrLn . ("Hello, " ++) =<< getLine
- exDM69 12y agoThere's one thing that should be mentioned here: you can easily compose these things. You can do: echo = forever $ getLine >>= putStrLn This also works with do notation, if you'd want to make the greet example looping. greetLoop = forever $ do name <- getLine putStrLn ("Hello, " ++ name) This is a very powerful concept. In mainstream imperative programming languages, you can't assign "statements" to variables or return them from functions or pass them as parameters.
- jeorgun 12y agoMaybe I'm misinterpreting you, but isn't that effectively what a closure is in an imperative language? def forever loop do yield end end forever do name = gets puts "Hello, name" end or even template <typename F> void forever(const F& fn) { for (;;) fn(); } int main() { forever([] { std::string str; getline(std::cin, str); std::cout << "Hello, " << str << '\n'; }); } looks a lot like the above code to me.
- exDM69 12y agoWell, in this simple example it is trivial to do the same by wrapping statements in a closure, but it's a bit more nuanced than that. But you still can't assign a for-loop as a value to a variable, for example (to exaggerate a little). This becomes more apparent when using monads for non-IO applications like parsing. It's difficult (but not impossible, of course) to implement things like backtracking or non-determinism by writing lots of closures in an imperative programming language, they don't compose as nicely. There are parser combinator libraries (similar to Parsec) done in other languages of course, but some of them even implement monads in the language they work in. I recall seeing "class Monad" written in Python, I think it was in the pyparsec library.
- dan00 12y ago> getLine >>= (return . (++) "Hello, ") >>= putStrLn A pointfree solution - if you want one - would be more in the form of: getLine >>= putStrLn . ("Hello, " ++) It's really mostly about getting used reading pointfree style, but there's certainly an overuse of pointfree style in some Haskell code, then it gets pointless.