
I've written a function that looks similar to this one getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n First, how do I fix the identation of the if then else? Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String. The closer thing that may work that I could find was making a pipe and convertind the file descriptor. Can I simplify that function to take it out of the IO monad? How? I thought about getContents, but that eats all input. Thanks in advance, Bruno

br1:
I've written a function that looks similar to this one
getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n
First, how do I fix the identation of the if then else?
getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String. The closer thing that may work that I could find was making a pipe and convertind the file descriptor. Can I simplify that function to take it out of the IO monad? How? I thought about getContents, but that eats all input.
Refactor! The reason your getList is hard to test, is that you're mixing side effecting monadic code with pure computations. Let's untangle that, and then test the the referentially transparent parts simply with QuickCheck. And remember that since getContents uses lazy IO, it only eats as much input as you ask it to. So let's refactor this, partitioning off the side effecting IO code: getList :: IO [Char] getList = take5 `fmap` getContents -- a thin IO "skin" take5 :: [Char] -> [Char] take5 = take 5 . filter (`elem` ['a'..'e']) -- the actual worker Now we can test the 'guts' of the algorithm, the take5 function, in isolation. Let's use QuickCheck. First we need an Arbitrary instance for the Char type -- this takes care of generating random Chars for us to test with. I'll restrict it to a range of nice chars just for simplicity: import Data.Char import Test.QuickCheck instance Arbitrary Char where arbitrary = choose ('\32', '\128') coarbitrary c = variant (ord c `rem` 4) So now we can write some simple tests. An easy one, a [Char] is equal to itself: *A> quickCheck ((\s -> s == s) :: [Char] -> Bool) OK, passed 100 tests. Reversing twice is the identity: *A> quickCheck ((\s -> (reverse.reverse) s == s) :: [Char] -> Bool) OK, passed 100 tests. Ok, so what properties does take5 have? Well, for one, the length of the string returned by take5 should be 5, no? *A> quickCheck (\s -> length (take5 s) == 5) Falsifiable, after 0 tests: "" Ah, but what if the input file is small :) Thanks quickCheck. Let's modify that then: *A> quickCheck (\s -> length (take5 s) <= 5) OK, passed 100 tests. Ok good. You can probably come up with some more things to check for now. -- Don

dons:
br1:
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String. The closer thing that may work that I could find was making a pipe and convertind the file descriptor. Can I simplify that function to take it out of the IO monad? How? I thought about getContents, but that eats all input.
Oh, another thing to check would be that the correct characters are returned, such as: *A> quickCheck (\s -> all (`elem` ['a'..'e']) (take5 s)) OK, passed 100 tests. So for all strings QuickCheck produced, all Chars in the string returned by take5 where elements of ['a'..'e']. -- Don

dons:
br1:
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String. The closer thing that may work that I could find was making a pipe and convertind the file descriptor. Can I simplify that function to take it out of the IO monad? How? I thought about getContents, but that eats all input.
I've summarised this little introduction to QuickCheck on the haskell.org wiki here, http://haskell.org/haskellwiki/Introduction_to_QuickCheck -- Don

On Thu, 21 Sep 2006 01:52:38 -0300, Donald Bruce Stewart
First, how do I fix the identation of the if then else? getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n
OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String. The closer thing that may work that I could find was making a pipe and convertind the file descriptor. Can I simplify that function to take it out of the IO monad? How? I thought about getContents, but that eats all input.
Refactor! The reason your getList is hard to test, is that you're mixing side effecting monadic code with pure computations. Let's untangle that, and then test the the referentially transparent parts simply with QuickCheck. And remember that since getContents uses lazy IO, it only eats as much input as you ask it to.
So let's refactor this, partitioning off the side effecting IO code:
getList :: IO [Char] getList = take5 `fmap` getContents -- a thin IO "skin"
take5 :: [Char] -> [Char] take5 = take 5 . filter (`elem` ['a'..'e']) -- the actual worker
The problem is that getContents 'eats' stdin, which can't be used again. I'm forced to change all my IO code to the getContents way, which is not possible. About QuickCheck: I'm going to check it out. Thanks for the pointers. Bruno

On Thu, 21 Sep 2006, Bruno Martínez wrote:
getList :: IO [Char] getList = take5 `fmap` getContents -- a thin IO "skin"
take5 :: [Char] -> [Char] take5 = take 5 . filter (`elem` ['a'..'e']) -- the actual worker
The problem is that getContents 'eats' stdin, which can't be used again.
You can process the result of getContents as many times as you want.
I'm forced to change all my IO code to the getContents way, which is not possible.
If your program contains so much IO code you should seriously consider refactoring.

On Thu, 21 Sep 2006 11:20:33 -0300, Henning Thielemann
On Thu, 21 Sep 2006, Bruno Martínez wrote:
getList :: IO [Char] getList = take5 `fmap` getContents -- a thin IO "skin"
take5 :: [Char] -> [Char] take5 = take 5 . filter (`elem` ['a'..'e']) -- the actual worker
The problem is that getContents 'eats' stdin, which can't be used again.
You can process the result of getContents as many times as you want.
But I cannot process input again without getContents. This function's use of getContents forces me to use only getContents everywhere. For example, I can't use hWaitForInput in another IO action.
I'm forced to change all my IO code to the getContents way, which is not possible.
If your program contains so much IO code you should seriously consider refactoring.
I try to minimize the amount of IO I do, but there's a point I don't know how to strip my functions of their remaining IOness. That's why I ask :) Bruno

Bruno Martínez wrote:
On Thu, 21 Sep 2006 01:52:38 -0300, Donald Bruce Stewart
wrote: First, how do I fix the identation of the if then else? getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n
OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
Emacs is evil! It also inserts random tab characters into your code just to save a few space bytes. Tends to completely trash indentation e.g. when pasting code into mails etc. Ben

On 21/09/06, Benjamin Franksen
Emacs is evil!
I'll ignore the throwaway flaimbait there ;)
It also inserts random tab characters into your code just to save a few space bytes. Tends to completely trash indentation e.g. when pasting code into mails etc.
Not once happened to me when writing Haskell code. It does annoy you when writing Lisp, but it takes a single line of config to turn it off. -- -David House, dmhouse@gmail.com

On Thu, 21 Sep 2006 15:12:07 -0300, Benjamin Franksen
OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
Emacs is evil!
I'm open to alternatives. I use Windows, so went out of the way to have emacs. What do you use? I don't care much for colors, but automatic identation is very handy (when it works :D). Bruno

Bruno Martínez
On Thu, 21 Sep 2006 15:12:07 -0300, Benjamin Franksen
wrote: OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
Emacs is evil!
That's a great exaggeration
I'm open to alternatives. I use Windows, so went out of the way to have emacs. What do you use? I don't care much for colors, but automatic identation is very handy (when it works :D).
If there's a problem with haskell emacs mode, it seems very likely that if you ask the maintainer nicely, he'll do something about it. See http://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2006-09-13)

If there's a problem with haskell emacs mode, it seems very likely that if you ask the maintainer nicely, he'll do something about it. See http://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode
I asked Stefan a while ago:
I like your Emacs mode but it behaves a bit oddly when trying to indent if/then/else expressions in do notation. Typing tab only gives me one possible indentation, like so:
do if True then foo else bar
That is, the then and else branches line up under the if which is an error according to Haskell's layout rule. It probably should indent them like "case" with the then lining up with the condition to the if. I'd fix it myself if I knew Lisp but I don't. :/
Yes, it's a (recently) known problem which I haven't fixed yet. It's in the indent.hs test-suite, with a "FIXME" :-(

"Johan Tibell"
If there's a problem with haskell emacs mode, it seems very likely that if you ask the maintainer nicely, he'll do something about it. See http://www.iro.umontreal.ca/~monnier/elisp/#haskell-mode
I asked Stefan a while ago:
I like your Emacs mode but it behaves a bit oddly when trying to indent if/then/else expressions in do notation. Typing tab only gives me one possible indentation, like so:
do if True then foo else bar
That is, the then and else branches line up under the if which is an error according to Haskell's layout rule. It probably should indent them like "case" with the then lining up with the condition to the if. I'd fix it myself if I knew Lisp but I don't. :/
Yes, it's a (recently) known problem which I haven't fixed yet. It's in the indent.hs test-suite, with a "FIXME" :-(
For that one, if it doesn't get mended for long enough, Haskell' might accept the present layout. http://hackage.haskell.org/trac/haskell-prime/wiki/DoAndIfThenElse -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2006-09-13)

For that one, if it doesn't get mended for long enough, Haskell' might accept the present layout. http://hackage.haskell.org/trac/haskell-prime/wiki/DoAndIfThenElse
Hmm... the bug in haskell-mode has been known for almost a year now. So you're saying that I should just wait even more and it'll get fixed by Haskell'? IOW Haskell isn't just lazy but it even rewards laziness? Stefan

Bruno Martínez
writes: On Thu, 21 Sep 2006 15:12:07 -0300, Benjamin Franksen
wrote: OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
Emacs is evil!
David House wrote:
I'll ignore the throwaway flaimbait there ;)
Jón Fairbairn wrote:
That's a great exaggeration
Of course. I am sorry, couldn't resist the urge, if you know what I mean. Oh and forgot the ubiquitous smiley, so here goes: ;-) Cheers, Ben

First, how do I fix the identation of the if then else? getList = find 5 where find 0 = return [] find n = do ch <- getChar if ch `elem` ['a'..'e'] then do tl <- find (n-1) return (ch : tl) else find n
OK. Thanks. I didn't find that one because it's not offered as an identation option in emacs haskell mode.
It's not just a missing option, it's a bug. I'll hopefully get around to fixing it soon.
Emacs is evil!
Of course, that's why we like it so much.
It also inserts random tab characters into your code just to save a few space bytes.
Actually, it has nothing to do with saving space: it only uses TABs because many people *want* to use tabs. Of course, the rest of the users *doesn't want* to use tabs, so we get to be flogged both ways. BTW, when was the last time you tried haskell-mode? Haskell-mode (at least since version 2.0) makes it difficult for the user to insert TABs by mistake.
Tends to completely trash indentation e.g. when pasting code into mails etc.
Huh? You mean your mail reader doesn't display tabs in the same way as 8 spaces? TABs are evil for many reasons, but I've never seen this one. Stefan

Hello Bruno, Thursday, September 21, 2006, 8:28:23 AM, you wrote:
First, how do I fix the identation of the if then else?
http://haskell.org/haskellwiki/IO_inside contains a lot of information about using IO monad
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String.
http://haskell.org/haskellwiki/Library/Streams although you will need to slightly change your program using v* operations instead of h*, in return you will get ability to use String as Stream: h1 <- openFile "test" ReadMode vGetChar h1 h2 <- newStringReader "input" vGetChar h2 -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Thu, 21 Sep 2006 05:30:34 -0300, Bulat Ziganshin
Hello Bruno,
Thursday, September 21, 2006, 8:28:23 AM, you wrote:
First, how do I fix the identation of the if then else?
http://haskell.org/haskellwiki/IO_inside contains a lot of information about using IO monad
That page is very good.
Second, I want to test this function, without hitting the filesystem. In C++ I would use a istringstream. I couldn't find a function that returns a Handle from a String.
I'll give this a try. Thanks, Bruno Conectese mas rapido y ahorre hasta un 50% Tel. 0909.2030 => $0,15 IVA incluido el minuto ______________________________________________________ http://www.internet.com.uy - En Uruguay somos internet
participants (9)
-
Benjamin Franksen
-
Bruno Martínez
-
Bulat Ziganshin
-
David House
-
dons@cse.unsw.edu.au
-
Henning Thielemann
-
Johan Tibell
-
Jón Fairbairn
-
Stefan Monnier