7 ms·
Haskell IO for Imperative Programmers
- WilliamLP 17y ago> safeRead s = catch (readFile s) $ \_ -> return "" > Contrasting this with Java is left as an exercise for the reader. String safeRead(String fName) { try { return readFile(fName); } catch (Exception e) { return ""; } } Just the same thing, except replace ASCII garbage with words? (It assumes I have a readFile function, but that's not such a terrible thing to have around...)
- shrughes 17y agoYeah, basically.
- jrockway 17y agoJava's standard library has no way to read a file with one function.
- WilliamLP 17y agoI am allowed to write my own function in Java. Having a basic file read in a static function library is more than reasonable. Should it be in the standard library? Yes, but it not being there isn't really a cost.
- zaphar 17y agoIt is a cost because you now have to carry around your static function with you wherever you code. Either you 1. have it in a utils library that adds to the external dependencies of all your java projects. or 2. you copy and paste it everywhere. Either way it's a cost however small that java has and other languages have. Additionally your example doesn't really demonstrate the same feature the haskell example did, which was a stream that haskell through laziness handles transparently for you. Your readfile static function breaks the stream because it forces you to read the entire file into memory before returning the contents. That was sort of the point and your counter example in java demonstrates it beautifully. An example that did what the haskell example did would require significantly more code.
- shrughes 17y agoI really despise tutorials that extol the benefits of laziness by demonstrating interact and getContents, the most evil functions in the Haskell library.
- fp 17y agoCould you elaborate, please? Or point me to a discussion of the issues with said functions? Thanks!
- loup-vaillant 17y agoIf I recall correctly, `getContent` (and therefore `interact`) uses `unsafeInterleaveIO`, the most unsafe function of the whole Haskell library, while not satisfying all the condition it should satisfy. More practically, `getContent` is evil because it locks the file it is reading for an unbounded amount of time: as long as it's content (the giant string representing the file), isn't either out of scope (and eventually garbage collected) or fully evaluated. That can be a problem if you intent to open loads of files (you could hit the OS limit) or if you want to modify the file after reading part of it. Lazy IO is therefore quite embarrassing. Several people are working at hopefully safer alternatives right now.
- shrughes 17y agoThey work by having input/output happen outside the IO monad. That breaks the abstraction -- which is impossible. You can't implement interact or getContents out of simpler functions, unless you use unsafeSomethingOrOtherIO.
- camccann 17y agoDo you have any references for that? I looked through some Haskell documentation and didn't see anything warning about those functions being unsafe (although there does seem to be an unsafeHGetContents in System.IO.Unsafe). Is there's something I'm missing that says "hey, this function uses unsafePerformIO behind the scenes" or whatnot? Being not very experienced with Haskell I'm a little worried about library functions being unsafe without me realizing it.
- yason 17y agoI don't want to come out as a language troll -- I have written enough and much liked Haskell -- but one thing that Clojure, my favorite language, got right is multiparadigm programming. Because I/O is serial and imperative by nature it just so much makes sense to handle I/O in a serial and imperative way and write the rest of the program in functional style. The same can be said of state and assignments: you need persistent state at _some_ point in runtime, and I think it's best to accept the crap and be explicit about it. I greatly appreciate the idea of monads and all the insight that went into them, but it seems to me that they were conceived merely to solve a problem that doesn't need to exist. Monads are a great academic hallelujah and I hope they lead to the discovery of some new, unfamiliar programming paradigm that benefits all programmers. However, I think monads are currently like the various design patterns akin to the Visitor (and its relatives) in Java. People had to invent these patterns basically to emulate a plain old function pointer bundled with userdata pointer. That's an old "trick" from C in the 70's and assembly before that, but only such that is otherwise unavailable in Java. Consequently, if you're all pure and lazy-evaluating and you decide to make that a principal issue then yes, you do need monads to do the simplest thing, such as I/O, simply to fit into the purely lazy environment. IIRC the I/O monad was where it all began -- I would be surprised to note otherwise. Then again, what I like in Clojure is that while the program control isn't automatically lazy-evaluating I still get most of the benefits of laziness from the fact that almost all data flow in Clojure is lazy by default. I can still write these nice infinite Fibonacci series or infinite sequences of random numbers and (take) as much as I need. And I still have (delay) explicitly for any body of code that I need evaluated lazily. And I can still enjoy writing most of my code in beautiful pure functional Lisp. Yet I _can_ still shuffle my data procedurally. And loop around I/O like a grumpy Pascal hacker in a hamster wheel as much as I want _when_ I need to. And still confine that mess into a function or two with big warning labels around, so to not contaminate the rest of my program. I know there are people who build incredible things out of monads, or inventing monadic metatypes I'm unable to grasp, and generally doing stuff never done before. But that is merely a sign of great cleverness of these people; the same can be said about C++ templates that you can whack into such a spaghetti that they actually run parts of your computation in compile time. One could, I suppose, say that it's like a treadmill with wheels that are connected to the running belt but slightly downgeared: you can use it to move on a street by exercising walking with your legs but most people choose to just.. you know, walk directly.
- Luyt 17y agoThe author makes a nice case for lazy evaluation, but I doubt the "goal to help experienced procedural programmers make the transition to Haskell" is achieved because he doesn't explain how to read special Haskell constructs: mapM_ (interactFile (unlines . map processIt . lines)) args ss <- mapM readFile fileNames putStr $ f (concat ss) A c++ programmer might ask: What do these dots means? Wat does the $ character do? Why us mapM sometimes used with and without trailing underscore? It seems like one should learn Haskell first, before being able to understand the examples.
- jberryman 17y agoThe article is billed as a tutorial on haskell IO, so I presume a knowledge of haskell syntax as basic as (.) and ($) is assumed. But I agree that this probably isn't such an effective introduction to IO or haskell.