Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



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


Which 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)


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


If 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 >>=).


The 'do' is superfluous.

It would be much better as

    main = let greet name = putStrLn ("Hello, " ++ name)
                in getLine >>= greet


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


Thanks, that's much better than what I wrote. Still I think that's a big jump for most programmers - it certainly was for me.


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


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


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


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


> getLine >>= (return . (++) "Hello, ") >>= putStrLn

And my coworkers yesterday were complaining that ClojureScript looked like Brainfuck...

(http://en.wikipedia.org/wiki/Brainfuck)


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


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


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


I don't like it because the flow of data keeps changing direction. I would prefer one of

    getLine >>= ( ("Hello, " ++) >>> putStrLn )

    putStrLn . ("Hello, " ++) =<< getLine




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: