Jack Parkinson
Jack Parkinson

Reputation: 711

Map Function Doesn't Work as Intended

I have a couple of Haskell functions for writing to a .tex file (using the HaTeX package) which look like this:

theBody :: Monad m => [ Maybe (String, String)] -> LaTeXT_ m
theBody listOfEntries = do
  maketitle
  center $ tabular Nothing [RightColumn, VerticalLine, LeftColumn] $ do
    textbf "Name" & textbf "Tel"
    let removedJusts = map fromJust listOfEntries
    map tableEntry removedJusts

tableEntry :: Monad m => (String, String) -> LaTeXT_ m
tableEntry (name, number) = do
  lnbk
  hline
  do
    textit (fromString name)
    &
    textit (fromString number)

When I run this code, I get the following error:

Couldn't match expected type ‘LaTeXT m ()’ with actual type ‘[LaTeXT_ m0]’

Arising from the line where I map the tableEntry function to my list of tuples. The list of tuples would look something like this:

[("A Name", "12345"), ("Another Name", "54321"), ("Yet Another Name", "98765")]

If I replace map tableEntry removedJusts with:

tableEntry (removedJusts !! 0)
tableEntry (removedJusts !! 1)
tableEntry (removedJusts !! 2)

It does indeed add these entries to my tex file, so I can't understand why the map function wouldn't work for the list.

Any help appreciated.

Upvotes: 0

Views: 120

Answers (1)

duplode
duplode

Reputation: 34378

tableEntry (removedJusts !! 0) works because it matches the return type of your theBody function (LaTeXT_ m or, expanding the type synonym, LaTeXT m ()). map tableEntry removedJusts, on the other hand, gives a list of LaTeXT_ m, leading to the type error you have seen. What you actually need here is mapM_, which, in addition to mapping, combines all resulting LaTeXT m () computations in a single LaTeXT m ():

GHCi> :t mapM_
mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
theBody :: Monad m => [ Maybe (String, String)] -> LaTeXT_ m
theBody listOfEntries = do
  maketitle
  center $ tabular Nothing [RightColumn, VerticalLine, LeftColumn] $ do
    textbf "Name" & textbf "Tel"
    let removedJusts = map fromJust listOfEntries
    mapM_ tableEntry removedJusts

P.S.: A closely related function that you will eventually find useful is traverse. Unlike mapM_, traverse collects, rather than discards, the results of each computation. (Here I used mapM_ becuase you do want to discard said results, as they are merely ().)

P.P.S.: Using fromJust is generally not a good idea. It will make your theBody crash with a generic error message if there is a Nothing in the input list. Better options include catMaybes (from Data.Maybe), which will silently filter out the Nothing values (e.g. let removedJusts = catMaybes listOfEntries), and maybe and fromMaybe (the latter also from Data.Maybe), which allow you to choose how to handle Nothing values.

Upvotes: 2

Related Questions