Here's an excerpt of a domain-specific file-IO function I'm writing:
let
cp :: FilePath -> IO ()
cp "." = putStr "" -- OUCH!
cp ".." = putStr "" -- CRIKEY!
cp fname = custom logic here...
in mapM_ cp filepaths
I understand mapM_
lets us drop/ignore all IO ()
results, so I'd like a cleaner alternative to putStr ""
--- ie. Haskell's "canonical" way to write "a type-correct no-op IO (or monad) expression that does nothing".
From my newbie reading I had undefined
in mind, and while this compiles and causes no issues, it gives an unwanted stdout print main.hs: Prelude.undefined
(I use stack runghc main.hs
as the sole coding environment here --- it's just sufficient for this, but the above code will be looping recursively through directory trees: so would be a good time to (re)learn about a better, or rather "the proper" way).
IO is the way how Haskell differentiates between code that is referentially transparent and code that is not. IO a is the type of an IO action that returns an a . You can think of an IO action as a piece of code with some effect on the real world that waits to get executed.
It's implemented using unsafeInterleaveIO , which does trickery behind the scenes to allow lazy I/O. It's not a good example of how IO is supposed to work.
I/O actions are ordinary Haskell values: they may be passed to functions, placed in structures, and used as any other Haskell value. Consider this list of actions: todoList :: [IO ()]
Essentially, a >> b can be read like "do a then do b , and return the result of b ". It's similar to the more common bind operator >>= .
This is an unfortunate aspect of Haskell's learning curve: you would think there should be some sort of library function called
doNothing :: IO ()
for you to use, but the ecosystem expects you to know of the return
function in the Monad
typeclass, one of the many typeclasses that IO
instances. In this case return ()
should produce the intended behavior of creating an IO
action (read: effect, or thunk, or promise) that does nothing.
You might also be interested in listDir
and copyFile
from the path-io
package, which by using stronger types than type FilePath = String
is able to do away with the whole problem of .
and ..
altogether. Note in particular how listDir
returns subdirectories and files separately in a tuple. If that is not in the spirit of Haskell then what is? It does bring in an external dependency however but I am always looking for situations to plug that excellent library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With