JoshuaO
JoshuaO

Reputation: 33

Supplying a data constructor with elements from a list

I'm using the mysql-haskell library, and the result of a query has the type [MySQLValue]. This itself is a sum type, wrapping various data types from the database into their Haskell equivalent.

I'd like to "iterate" through this list, using a function for each element to transform the MySQLValue into what I want, and then supply each element in turn into a data constructor for my in-code representation of the data.

However, that would obviously require each function to have a different type (MySQLValue -> Int vs MySQLValue -> Text), so I can't just use a list.

What is the elegant way of doing what I need? At the moment I'm doing it very manually like this:

data OpenWord = OpenWord
  { russian :: Maybe T.Text,
    wordType :: Maybe WordType,
    audio_link :: Maybe T.Text,
    usage_note :: Maybe T.Text,
    level :: Maybe T.Text
  }

stripText :: MySQLValue -> Maybe T.Text
wordTypeFromSql :: MySQLValue -> Maybe WordType


openWordFromRow :: [MySQLValue] -> OpenWord
openWordFromRow row = OpenWord ( stripText $ row !! 0) ( wordTypeFromSql $ row !! 1) 
                               ( stripText $ row !! 2)  ( stripText $ row !! 3)  ( stripText $ row !! 4)

I understand I'm in a bit of a non-typesafe land, I'm mostly looking for more convinience rather than more correctness.

Upvotes: 3

Views: 176

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476544

You can use pattern matching to unwrap the list:

openWordFromRow :: [MySQLValue] -> OpenWord
openWordFromRow (xa : xb : xc : xd : xe : _) = OpenWord (stripText xa) (wordTypeFromSql xb) (stripText xc) (stripText xd) (stripText xe)

We can use the lens package to perform the same mapping over all elements of a tuple, and thus guarantee that the result is a tuple with the same number of elements:

import Control.Lens.Each(each)
import Control.Lens.Lens((&))
import Control.Lens.Setter((%~))

openWordFromRow :: [MySQLValue] -> OpenWord
openWordFromRow (xa : xb : xc : xd : xe : _) = OpenWord ya (wordTypeFromSql xb) yc yd ye
    where (ya, yc, yd, ye) = (xa, xc, xd, xe) & each %~ stripText

or as @JonPurdy says:

import Control.Lens.Combinators(over)
import Control.Lens.Each(each)

openWordFromRow :: [MySQLValue] -> OpenWord
openWordFromRow (xa : xb : xc : xd : xe : _) = OpenWord ya (wordTypeFromSql xb) yc yd ye
    where (ya, yc, yd, ye) = over each stripText (xa, xc, xd, xe)

Upvotes: 3

Related Questions