Suppose I have a task I want to do to each line of a file, accumulate a result and output it, .. I'd like to write something similar to
main = do res <- foldX process_line initial_value getLine print res
I feel this ought to be straightforward -- the structure is obviously some sort of fold, but I shouldn't have to use a list -- so I must be missing something obvious. What is it?
foldr, foldM, etc. derive a recursive computation from the recursive structure of an input list, so you have to feed one in. If you want to bypass the list, you could use IO-observations (getLine, isEOF) instead of list observations (head/tail, null): import IO foldX f c = catch (do l <- getLine r <- foldX f c return $ f l r) (\e->if isEOFError e then return c else ioError e) main = foldX (:) [] >>= print Whether that is a real fold, or what the real fold/unfold would look like, I leave to others;-) The same goes for optimization. Hth, Claus
foldr, foldM, etc. derive a recursive computation from the recursive structure of an input list, so you have to feed one in. If you want to bypass the list, you could use IO-observations (getLine, isEOF) instead of list observations (head/tail, null):
Yes you can define it, I should have been a bit more direct. It seems to me that there's something odd about the way the IO monad interacts with bulk operations on files. In particular, it seems odd that getContents should ever be the easiest way of tackling something, rather than some natural operation on Monads. Doing something to each line of a file is such a common kind of computation that it ought to be easy! It also seems wrong that end of file should be an exception -- after all, for most files other than a terminal or "special file", having an end is the norm. As a result, the definition you gave strikes me as awkward (no fault of yours!). It suggests to me that a Monad isn't quite enough. One of the great things about fold is that you don't have to code the test for the end: it's encapsulated in the higher-order function. Shouldn't there be the same for IO?
Whether that is a real fold, or what the real fold/unfold would look like, I leave to others;-)
I suppose that was part of my real question, aimed at the others who've got the mental processing power to answer such things. Cheers, Jón -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk 31 Chalmers Road jf@cl.cam.ac.uk Cambridge CB1 3SZ +44 1223 570179 (after 14:00 only, please!)
foldr, foldM, etc. derive a recursive computation from the recursive structure of an input list, so you have to feed one in. If you want to bypass the list, you could use IO-observations (getLine, isEOF) instead of list observations (head/tail, null):
Yes you can define it,
And you can, as well. That's how common idioms come into being; there's no special magic about the folds already in existence. Just because people have done fold already for lists, trees, functors,.. doesn't mean that you won't have to define your own useful abstractions every now and then. They evolve with input from theory and practice, some forms survive, and become common.
It seems to me that there's something odd about the way the IO monad interacts with bulk operations on files.
That may be more related to IO than to the monadic approach to it (try to think it over with a world-passing approach). And if you really just want ot process text, there's interact, which worked well enough for this purpose before IO became more general and more controlable.
In particular, it seems odd that getContents should ever be the easiest way of tackling something,
Who says that? It's the laziest (as in: I don't want to invest any time in this) way, and as it exists, it is often used for quick demos, examples and explanations, e.g., here on the list.That doesn't mean you have (or want) to use it for all programs (e.g., lazy i/o has interesting interactions with concurrency:), and once you start using monadic style more, you'll develop useful abstractions for common idioms, just as it happened for list programming. Some of those are already in the standard libraries, but you'll still develop your own preferences. Haskell allows you to express those, in libraries, usually without language extensions or help from implementers. It only takes a little effort to get away from things that look convenient, but may not be in the long run.
rather than some natural operation on Monads.
Monads are about the type constructor, >>=, and return. Things like getLine have been thrown into this framework for practical use, but the theory won't tell you much about them (you could take some concrete monads, and look at how folds distribute over them, then try to find a general pattern; the Meijer/Hutton "Bananas in Space" paper might be helpful).
Doing something to each line of a file is such a common kind of computation that it ought to be easy!
Who says it isn't?-) There are even several styles you could adopt, including use of interact, or use of your own abstractions on top of io-monad operations..
It also seems wrong that end of file should be an exception -- after all, for most files other than a terminal or "special file", having an end is the norm. As a result, the definition you gave strikes me as awkward (no fault of yours!). It suggests to me that a Monad isn't quite enough.
Again, nothing to do with monads - just my coding decision for that example.. If you prefer, you can redefine it using IO.isEOF (I mentioned that optimization was left to others:-).
One of the great things about fold is that you don't have to code the test for the end: it's encapsulated in the higher-order function. Shouldn't there be the same for IO?
Isn't it? getContents = foldX (++) "" -- we drop the newlines here.. getLines = foldX (:) [] No eof-testing in sight. Note that the list fold *definition* needs to test for the empty list as well (unless you restrict yourself to infinite lists) - why else would you pass in an initial value? Just to make the comparison explicit (not tested): foldr c n l = if null l then n else let h = head l ft = foldr c n (tail l) in c h ft mif c t e = c >>= \b->if b then t else e foldX c n = do mif isEOF (return n) (do l <- getLine fls <- foldX c n return (c l fls) ) So we've mostly got a fold lifted into the io-monad, with getLine delivering the "head" (and implicitly truncating further input to the "tail"). What more do you want?-) Hth, Claus
Yes you can define it,
And you can, as well.
Man sollte sich nicht darauf verlassen, daß ein Englander "man" verwendet, wenn es angebraucht wäre¹.
That's how common idioms come into being; there's no special magic about the folds already in existence.
Well, my point is that there is -- indeed some ways of defining the type for lists use a fold as the starting point. I mentioned the lack here because I think there is some deeper structure that someone cleverer than me may be able to see.
It seems to me that there's something odd about the way the IO monad interacts with bulk operations on files.
That may be more related to IO than to the monadic approach to it
Yes. Hence the change of subject line.
In particular, it seems odd that getContents should ever be the easiest way of tackling something,
Who says that?
I did! I think getContents is a klugey hangover from stream based I/O, and the existence of "semi-closed handles" supports that, so I'm hoping that people might become interested in looking at the question.
One of the great things about fold is that you don't have to code the test for the end: it's encapsulated in the higher-order function. Shouldn't there be the same for IO?
Isn't it?
getContents = foldX (++) "" -- we drop the newlines here.. getLines = foldX (:) []
No eof-testing in sight.
Sure, once we've defined foldX, but then we need to know whether it really is a fold &c. I should remember to make my messages to the list less indirect.
So we've mostly got a fold lifted into the io-monad, with getLine delivering the "head" (and implicitly truncating further input to the "tail"). What more do you want?-)
Theory! That's what I want! Cheers, Jón 1. I had to get help with this; perhaps it would have been better to leave my numerous mistakes in it! -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk 31 Chalmers Road jf@cl.cam.ac.uk Cambridge CB1 3SZ +44 1223 570179 (after 14:00 only, please!)
participants (3)
-
C.Reinke -
Claus Reinke -
Jon Fairbairn