My First Haskell Question

This is a technical admission that I don’t really know what I’m doing in the Haskell programming language, but there are some successes — I’d welcome any feedback, positive or negative, since I’m learning as I go here…

So I installed Haskell Platform for Windows no problem; learning Haskell is not a problem per se — there are tutorials, and the wikibook but I can’t find what I want to know… (I may have to revise that — see update after the break)  I may want to read the “How to…” by Wash. U of St. Louis students, or perhaps Write Yourself a Scheme in 48 Hours.  Also, of note is Real World Haskell, the book online…

But having not done those things, here’s what I tried:

data BUEntry = FileSpec String
    | WCFiles String
    | Folder String
  deriving (Eq, Ord, Show)

A “backup entry” — I’m trying to write a program to ‘backup’ files (simply by copying from one folder to another), for now… — either a “FileSpec”: a filename, including path; “WCFiles”: wildcard files (a folder name and some wildcard spec — in common format); or a “Folder”, indicated by the trailing backslash (this means copy the entire directory).

So far, so good.  Now I want to “convert” a BUEntry to a list of strings — possibly only one.

getFileList :: BUEntry -> [String]
getFileList (FileSpec s) = [s]

So this is the signature of the function to do so, as well as the first, for a “simple” FileSpec, which returns a list of this only…

Working on “getFileList (WCFiles wc) = …” I first have to extract the path….

well, maybe it would be easier to start with “getFileList (Folder f) = …”  I found the function getDirectoryContents in the Directory module.

import Directory

getFileList (Folder f) = let files = getDirectoryContents(f)
                         in files

But of course:

Couldn't match expected type `[String]'
      against inferred type `IO [FilePath]'

So what is the type “IO [FilePath]” ?? Is that a monad thingy?

Five hours later, I’ve concluded that, yes, indeed it is.  From the students “How to” (see above), I read about astronauts, then “All About Monads.”

Update, next day (Tues., 22 June) Around quarter to midnight last night, reading that last link, I think I figured out what I needed to know — at least I got something to work.  Reading this section, I found out that once I’m “in” a monad, I can’t get out; to put it another way, once I have a monad in my return value(s), I have to keep it and propagate it all the way up/out.  (Confirmed here.)

So I changed the type signature of my function wie das:

getFileList :: BUEntry -> IO [String]

getFileList (Folder f) = let files = getDirectoryContents(f)
                         in files

But then, I couldn’t get the original to work “getFileList (FileSpec s)…”  because [s] isn’t of type “IO [String].”  Then I remembered reading about the return (keyword??), and on a whim, tried it — it worked!

Now I’m going to skip ahead a bit — to get wildcards to work required two other tricks, including getting hackageDB package Text.Regex.TDFA — not exactly that, but it is what I chose.

getFileList (WCFiles wc) = let two_sepd = B.spanEnd notSep (U.fromString wc)
                               r = makeRegex ("\\`" ++ conv (U.toString (snd two_sepd)) ++ "\\'") :: Regex
                           in do
                              files <- getDirectoryContents(U.toString (fst two_sepd))
                              return (filter (\s -> matchTest r s) files)

Then other “trick” (I’m not sure the right one) is to separate the filename from the path, and here is my function to convert a filename wildcard expression to a regular expression for searching:

-- convert a string from file wildcard to a regular expression
conv  :: String -> String
conv [] = []
conv (x:xs)
    | x == '.' = "\\." ++ conv xs
    | x == '*' = ".*" ++ conv xs
    | otherwise = x : conv xs

So a brief description of all that is in order — getFileList (WCFiles wc) first separates the string of wc by using Data.ByteString.spanEnd (at the top of the file, now, there are more import statements, including these two:

import qualified Data.ByteString.UTF8 as U
import qualified Data.ByteString as B

for both spanEnd and toString/fromString).

The function notSep is my own, which returns true as long as the character argument is not the file path separator (“\” for Windows).  The function spanEnd returns a two elt. tuple — the first (“fst”) is the entire path up to the final path separator, while second (“snd”) is the wildcard portion of the filespec.  The do statement allows me to operate on the results of getDirectoryContents via the filter function without worrying about the fact that I have an IO monad hanging around…

And that is a very abbreviated description of the rest, after taking too much time to explain my confusion about monads.


2 Responses to “My First Haskell Question”

  1. Ted Tower Says:

    My goodness. This post rocked my world! Not in a good way! I eagerly started reading about the new language you’re looking into and was almost immediately lost — all the while calming myself that I’m really enjoying learning python.

    And I’ve never heard of Monad. I take that back, I seem to recall my parents had a card game called Monad, but I seriously wonder at the connection.

    Keep at it, Greg! I’ll also keep trying to keep up with you! 🙂

  2. I Feel Vindicated « More than a Tweet Says:

    […] More than a Tweet Random Thoughts and Musings « My First Haskell Question […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: