I'm a software architect/consultant in Boulder, Colorado. I started blogging at: http://wayne-adams.blogspot.com/, but have since started the new blog, Data Sceintist in Training which will cover the areas I am learning as part of my own "big data" training, including the Hadoop family, frameworks like OpenStack, the R programming language, column-oriented databases and the art of examining data sets for useful patterns. The original blog will be kept alive with Java-specific posts. Wayne is a DZone MVB and is not an employee of DZone and has posted 35 posts at DZone. You can read more from them at their website. View Full User Profile

From OO to FP: Haskell I/O, Part 2

12.09.2011
| 3081 views |
  • submit to reddit

In my Part-1 post on this topic, we actually did all the I/O I'm going to do here. We lazily read in the entire sample data file, a file containing data describing events generated by a process monitor. My next goal was to re-hydrate my Events from the Strings serialized to the file. These Strings were generated by calling the function show on my List of Events.

Time to back up a little. We got show for free, but we had to ask for it. Leaving aside the Event data type for a moment and looking at the simpler Property data type, remember how we defined it:

     data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Show)
At that time we discussed "deriving (Show)" by simply noting it allowed ghci to know how to output Property values. Of course, there's more to it than that. The "more" is Haskell typeclasses. A Haskell typeclass defines a set of functions that can be defined to operate on a data type. The show function is a member of the Show typeclass and has the following type:
     *EventProcessor> :type show
   show :: Show a => a -> String
For a moment, let's see what had happened if we had not specified that Property derives Show:
     -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
  type Key = String
  type Value = String
  data Property = Property {
    key :: Key,
    value:: Value }
If we load this file, then create a Property, then try to show it, we see the following:
     Prelude> :load simpleProperty.hs
   [1 of 1] Compiling Main             ( simpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   :1:1:
   No instance for (Show Property)
     arising from a use of `show'
   Possible fix: add an instance declaration for (Show Property)
   In the expression: show prop1
   In an equation for `it': it = show prop1

That's considerably more helpful than a typical compiler error message. The key is the hint to add an instance declaration. In order for the functions of a typeclass to be applicable to your data type, you need to declare an instance of that typeclass that handles your data type. In our case, we can get Show simply by stating that we derive it. So we go back to our old definition:
    -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
   type Key = String
   type Value = String
   data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Show)
Repeating our earlier test, now we get the following:
    *Main> :load simpleProperty.hs
   [1 of 1] Compiling Main             ( simpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   "Property {key = \"key1\", value = \"value1\"}"

That's definitely an improvement. But, back to the subject -- what I want is to be able to read a String and turn it in to a Property. First, let's try to read a String into an Integer:
     *Main> read "5"
   :1:1:
   Ambiguous type variable `a0' in the constraint:
   Read a0) arising from a use of `read'
   Probable fix: add a type signature that fixes these type variable(s)
   In the expression: read "5"
   In an equation for `it': it = read "5"

Again, a helpful hint from ghci. It basically doesn't know what we are trying to create and suggests adding a type signature. Here's how you would do it:
     *Main> let anInt = (read "5")::Integer
   *Main> :type anInt
   anInt :: Integer
   *Main> show anInt
   "5"

Haskell now knows we're trying to read an Integer, so it recognizes that anInt is an Integer and knows how to show it. All of this should tell you that Integer has defined instances for both Show and Read.

Can we get Read "for free" simply by stating that our data type derives it? It can't hurt to try:
   -- file:  c:/HaskellDev/eventProcessor/simpleProperty.hs
   type Key = String
   type Value = String
   data Property = Property {
     key :: Key,
     value:: Value }
     deriving (Read, Show)
Next, I'll create a Property and output it with show, then see if that format (which, incidentally, is how I created my sample Event file -- using show on a List of Events) is "read"-able:
   *Main> let prop1 = Property "key1" "value1"
   *Main> show prop1
   "Property {key = \"key1\", value = \"value1\"}"
   *Main> let prop2 = (read "Property {key = \"key1\", value = \"value1\"}")::Property
   *Main> show prop2
   "Property {key = \"key1\", value = \"value1\"}"
This is great news -- I didn't have to write a parser, and I'm perfectly happy to use, as my format, the same format that Haskell uses to show a Haskell data structure.

What I would really like to do is to be able to read a full Event, as defined in my earlier posts, including a List of Events. Here's an example Event:
     "Event {timestamp = 1320512200548, className = \"java.lang.String\", lineNumber = 1293, 
message = \"NPE in substring()\", properties = [Property {key = \"userId\", value = \"smith\"},
Property {key = \"sessionId\", value = \"ABCD1234\"}]}"

Let's try the same trick with Event that we used with Property:
  -- file:  c:/HaskellDev/eventProcessor/notSoSimpleProperty.hs

   type Timestamp = Integer
   type ClassName = String
     type LineNumber = Integer
     type Message = String
     type Key = String
     type Value = String
     type Properties = [Property]

     data Property = Property {
       key :: Key,
       value:: Value }
       deriving (Read, Show)

     data Event = Event {
       timestamp :: Timestamp,
       className :: ClassName,
       lineNumber :: LineNumber,
       message :: Message,
       properties :: Properties }
       deriving (Read, Show)
Was it enough simply to declare that Event derives Show? Let's see:
     *EventProcessor> :load notSoSimpleProperty.hs
   [1 of 1] Compiling Main             ( notSoSimpleProperty.hs, interpreted )
   Ok, modules loaded: Main.
   *Main> let event1 = (read "Event {timestamp = 1320512200548, className = \"java.lang.String\", 
lineNumber = 1293, message = \"NPE in substring()\", properties = [Property {key = \"userId\", value = \"smith\"},
Property {key = \"sessionId\", value = \"ABCD1234\"}]}")::Event *Main> show event1 "Event {timestamp = 1320512200548, className = \"java.lang.String\", lineNumber = 1293, message = \"NPE in substring()\",
properties = [Property {key = \"userId\", value = \"smith\"},Property {key = \"sessionId\", value = \"ABCD1234\"}]}" *Main> show (properties event1) "[Property {key = \"userId\", value = \"smith\"},Property {key = \"sessionId\", value = \"ABCD1234\"}]"
This is great. We can take a String representation of an Event, as output by show, and use it directly with read to instantiate an Event variable.

This discussion in no way described how to provide an instance of a typeclass; maybe in another post. Right now I still want to create Events from my sample Event file.

My first cut at this is the following:
   import System.IO
   import EventProcessor

   main :: IO ()
   main = do
    inh <- openFile "sampleEvents" ReadMode
    inContents <- hGetContents inh
    let eventList = processData (lines inContents)
    hClose inh

   processData :: [String] -> [Event]
   processData = map createEvent

   createEvent :: String -> Event
   createEvent event = (read event)::Event


While this appears to be correct, it doesn't do anything that proves I've been able to parse my example-file lines into a List of Events. Unfortunately, at this point I'm a little stuck, as I haven't yet figured out how to correctly extract elements from my Events (I can do so interactively in ghci, but I am plagued by compiler errors if I try to do so in the do block).

I'm going to leave this for a while and go back to some online resources/tutorials, then revisit. The problem for me is that I still don't understand the I/O monad, as it's called. It's becoming clear to me that the concept of a monad -- apparently so integral to Haskell -- isn't easily grasped, as evidenced by the overwhelming number of "Here's my take on monads" tutorials on-line (one author, I see, jokes that everyone who learns about monads appears to post a tutorial on the subject shortly thereafter). So I'm off to the Haskell Wiki to learn about monads, then learn some more about Haskell I/O, and then pick up where I left off.

 

From http://wayne-adams.blogspot.com/2011/12/from-oo-to-fp-haskell-io-part-2.html

Published at DZone with permission of Wayne Adams, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)