[Haskell-cafe] Getting an attribute of an object

Tomasz Zielonka tomasz.zielonka at gmail.com
Thu Feb 10 16:51:02 EST 2005


On Thu, Feb 10, 2005 at 09:17:17PM +0100, Dmitri Pissarenko wrote:
> Hello!

Hello!

> I have a list of instances of the ClassifiedImage class, which is defined as
> follows.
> 
> data ClassifiedImage = ClassifiedImage {imageFileName :: String, subjectID ::
> String}
>         deriving Show

ClassifiedImage is not a class. You use ClassifiedImage both as a type
constructor (before =) and as a data constructor (after =).

> Attribute imageFileName contains a file name of a certain image. I want
> to "transform" the list [ClassifiedImage] into a list [(ClassifiedImage,
> Image)], where Image is content of the file with name imageFileName.

You've used labelled fields. The label imageFileName automatically
introduces function imageFileName into scope, with type:

    imageFileName :: ClassifiedImage -> String

> That is, I want to have a routine, which iterates through the list of
> ClassifiedImages, reads each file (with filename contained in the attribute
> ClassifiedImage.imageFileName) and stores its content in a variable.
> 
> I have already a function, which reads the content of a file.
> 
> My idea is to use map for this task:

> readClassifiedImages :: [ClassifiedImage] -> [(ClassifiedImage, Image)]

You'll probably have to use a subtly different type for this function:

  readClassifiedImages :: [ClassifiedImage] -> IO [(ClassifiedImage, Image)]

> readClassifiedImages classifiedImages = do
>         return map readerFunc classifiedImages

Note that here you use return with 3 arguments. Most probably not what
you want. More about using "map" later.

> readerFunc denotes some function, which takes the attribute imageFileName of a
> ClassifiedImage instance.
> 
> I suppose that this readerFunc looks like shown below.

> readerFunc :: ClassifiedImage -> (ClassifiedImage, Image)
> readerFunc classifiedImage = (classifiedImage, fileContent)
>         where   fileName = classifiedImageFileName classifiedImage
>                 fileContent = readImage fileName
 
If you wan't to load an image from disk, etc, you'll have to use the
IO monad. Thus readerFunc will have an IO type. Also, there is no need
to return the ClassifiedImage from readerFunc, unless readerFunc should
return a different ClassifiedImage.

  readerFunc :: ClassifiedImage -> IO Image
  readerFunc classifiedImage = readFile fileName
    where   fileName = imageFileName classifiedImage

> classifiedImageFileName :: ClassifiedImage -> String

classifiedImageFileName = imageFileName

> Can I use map for readImage function, which is in the IO domain? If not, what
> tutorial can help me?

Assuming you have 

    readerFunc :: ClassifiedImage -> IO Image

you can use map to construct readClassifiedImages, like this:

    readClassifiedImages :: [ClassifiedImage] -> IO [(ClassifiedImage, Image)]
    readClassifiedImages classifiedImages =
        sequence $ map (\ci -> readerFunc ci >>= \i -> return (ci, i)) classifiedImages

but there is already mapM, which can be defined as:

    mapM f l = sequence (map f l)

so you can:

    readClassifiedImages :: [ClassifiedImage] -> IO [(ClassifiedImage, Image)]
    readClassifiedImages classifiedImages =
        mapM (\ci -> readerFunc ci >>= \i -> return (ci, i)) classifiedImages

you can also use some syntactic sugar:

    readClassifiedImages :: [ClassifiedImage] -> IO [(ClassifiedImage, Image)]
    readClassifiedImages classifiedImages =
        sequence $ [ do i <- readerFunc ci
                        return (ci, i)
                   | ci <- classifiedImages ]

or this way (my preferred notation):

    readClassifiedImages :: [ClassifiedImage] -> IO [(ClassifiedImage, Image)]
    readClassifiedImages classifiedImages =
        (`mapM` classifiedImages) $ \ci -> do
            i <- readerFunc ci
            return (ci, i)

HTH

Best regards
Tomasz

-- 
Szukamy programisty C++ i Haskell'a: http://tinyurl.com/5mw4e


More information about the Haskell-Cafe mailing list