By Matúš Bako


2018-02-13 10:07:44 8 Comments

I'm a beginner in Haskell, and I'm trying to learn few concenpts so I would appretiate your help. I read few books and guides but I can't understand how can I use IO operations. I'm trying to parse file in this format and create graph object. So far I have this code: (nodes are identified by positive integer)

data Edge = Edge Int Int deriving(Show)

data Graph = Graph {
                nodeCount :: Int,
                edges :: [Edge]
            }   deriving (Show)     

parseInput :: String -> IO ()
parseInput filePath = do
    handle <- openFile filePath ReadMode
    contents <- hGetContents handle
    putStrLn contents
    hClose handle

I understand that haskell is lazy. I want to create function with signature

parseInput :: String -> Maybe Graph

so I either get Nothing on error or Graph. My problem is that I can't simply read the file and create the graph in one function. As far as I understand, I'm supposed to get IO String, create the object and then close the file using handle? Could anyone point me in the right direction? I'm having a hard time with this because it's very different from imperative languages.

Thanks for any suggestions.

1 comments

@Ignat Insarov 2018-02-13 13:25:49

You can think of it as two different worlds:

  • the deterministic, ordinary world where all pure functions live, and
  • the magical IO world where files live, and where you don't really know what could happen.

When someone says IO is a monad, you can understand it thus: you can make anything a magical IO thing, but once the spell is cast, there's no way back to the ordinary.

So, functions of type IO are your spells, and the parseInput is an ordinary, totally predictable mechanical device: for any given input, you may always know what the output will be. Once you have your mechanics in place, you have to conjure some contents with readFile, and then move your ordinary function to the magical IO world as well with fmap:

parseInput :: String -> Maybe Graph
parseInput = ...

magicallyGetContents :: IO String
magicallyGetContents = readFile ...

parseMagicalInput :: IO String -> IO (Maybe Graph)
parseMagicalInput = fmap parseInput

By this point, you can apply your parser:

λ :t parseInput magicallyGetContents
    -- This won't work because of the interworld barrier.

<interactive>... error:
    • Couldn't match type ...
      Expected type: String
        Actual type: IO String
...

λ :t parseMagicalInput magicallyGetContents
    -- This is fine, because both things are on the magical side.
parseMagicalInput  magicallyGetContents :: IO (Maybe Graph)

There are several spells at your disposal that can help you cross to the magical side. The simplest would be:

return :: a -> IO a

(The name suggests the magical world is actually the home of all things in Haskell.)

Notice that once you apply this fairly simple spell, there is no way back. Though you theoretically could hack your way through the RAM and retrieve the bytes, it will not be straightforward. For example, this won't do:

λ print $ (unsafeCoerce (return 2 :: IO Int) :: Int)
1099511628032

So, your way is to assume you have the contents in the pure world, and build your machine that would then work, then cross the interworld barrier with it and apply it to any number of DIMACS graph files there on the other side.

Happy adventures!

p.s. You may also check this answer for a more down-to-earth point of view.

@Matúš Bako 2018-02-14 11:57:04

So, tldr, because I performed IO operation (and result is unknown before performing it), my Graph is stuck with IO prefix (also called monad?) and I can't go back. Thanks for the intuition. I'm trying to code it right now so I'll probably post my code soon.

@Ignat Insarov 2018-02-14 13:41:55

@MatúšBako Not only is it unknown, you may not even get the file at all. For example, if the previously valid file gets removed by someone else just before your program tries to access it. Pure things always do their job, yet IO things you should expect to fail you any moment.

Related Questions

Sponsored Content

15 Answered Questions

[SOLVED] Getting started with Haskell

8 Answered Questions

[SOLVED] Large-scale design in Haskell?

8 Answered Questions

[SOLVED] Haskell or Standard ML for beginners?

4 Answered Questions

[SOLVED] A Haskell function of type: IO String-> String

1 Answered Questions

[SOLVED] Haskell: IO [String] to [String]

  • 2016-01-03 14:00:48
  • Banana
  • 868 View
  • 0 Score
  • 1 Answer
  • Tags:   haskell io

1 Answered Questions

[SOLVED] Build a graph structure in haskell

  • 2015-07-10 17:03:52
  • svr
  • 130 View
  • 4 Score
  • 1 Answer
  • Tags:   haskell

0 Answered Questions

Why is this Haskell IO Code so slow?

  • 2015-02-21 23:33:52
  • alexp
  • 68 View
  • 1 Score
  • 0 Answer
  • Tags:   haskell io

2 Answered Questions

[SOLVED] Do Haskell files close automatically after readFile?

3 Answered Questions

[SOLVED] Split strings into lists with Haskell

2 Answered Questions

[SOLVED] Haskell: Inserting every line from a file into a list

  • 2008-10-18 23:57:29
  • Sergio Morales
  • 9392 View
  • 11 Score
  • 2 Answer
  • Tags:   file haskell input io

Sponsored Content