[Haskell-beginners] SQLite3 Row <-> Data models -- Is this a typical Haskell pattern ?

Antoine Latter aslatter at gmail.com
Sun Mar 20 02:43:54 CET 2011


On Sat, Mar 19, 2011 at 8:14 PM, Stef T <stef at ummon.com> wrote:
> On Mar 19, 2011, at 5:43 PM, Antoine Latter wrote:
>
>> On Sat, Mar 19, 2011 at 7:21 PM, Stef T <stef at ummon.com> wrote:
>>> Hey Everyone,
>>>    Greetings, I am a total newb to haskell, so, I have a strange question (perhaps). Consider I have a data model which looks like this;
>>>
>>> data Brand = Brand { id :: Int,
>>>                                       name :: String,
>>>                                       created_at :: String,
>>>                                       updated_at :: String
>>>                                    }
>>>
>>>    Now, I have a sqlite3 function such as
>>>
>>> checkout :: Int -> IO (Either String [[Row Value]])
>>> checkout a = do
>>>       handle <- openConnection "/Users/stef/haskell/db/development.sqlite3"
>>>       execStatement handle $ "SELECT * from brands where id = " ++ show a
>>>
>>>    From this, I get a Row/Tuple. So far, so good, but, the question _I_ have is, how do you go about mapping a returned row to the data model ? Is this even a design pattern that is used in FP languages ? I admit, I have spent numerous years in the MVC world, so, perhaps this is simply "not done". It would seem to be a much nicer thing to then do ;
>>>
>>
>> What problem are you running into trying to do this?
>>
>> You would need to write functions to convert each column into the
>> format you want it for the Brand type, and then pass output of the
>> functions Brand data constructor. Depending on the format of the data
>> and the library you're using these functions might turn out to be
>> pretty simple.
>>
>
> I guess I am having a fundamental disconnect in 'how' to do it nicely (or DRY-ly).
>
> the format of the row is ;
>
> Right [[[("id",Int 4239),("name",Text "Zoppini"),("created_at",Text "2011-02-02 20:51:44.706633+0000"),("updated_at",Text "2011-02-02 20:51:44.706633+0000")]]]
>
> I had attempted to do something along the lines of ;
>
> getBrand :: Int -> Brand
> getBrand a = do
>        b <- checkout a
>        Brand { id = b id, name = b name }
>
> but, that explodes (not least of which is the id being a reserved keyword from prelude)
>

First - 'id' is reserved, it's an ordinary function. But it can be
confusing to give things the same name as things in the Prelude.

Here you're running into a few problems - is there a tutorial you're
working with? There are some good ones over here:

http://www.haskell.org/haskellwiki/Tutorials

First is the type of your function:

> getBrand :: Int -> Brand

Since you're calling functions which do IO (to get data from the
database) in your function, your function needs to note that in its
type signature:

> getBrand :: Int -> IO Brand

On to the body of the function:

> getBrand a = do
>        b <- checkout a
>        Brand { id = b id, name = b name }

In the second line of the body, you have "b id" and "b name", this is
treating "b" has if it were a function, and calling it first with the
value "id" and then with the value "name" which is almost certainly
not what you want.

I don't know much about the library you're using, but you can start
with the types of the functions to figure out what to do with them.

Here, the value 'b' os of type "Either String [[Row Value]]", so you
can use a case statement to deconstruct the out-side 'Either' type:

> case b of
>   Left errorStr -> [do something with the error message]
>   Right rows -> [do something with the list of rows]

In this bit of code, the value 'rows' is of type '[[Row Value]]', so
then you have to make choices about what to do if the list is empty,
or if the list has more than row. Once you have a row you then need to
lookup its type and figure out how to take it apart and make sense of
the values.

So you've got more code to write :-)

I don't have much experience mixing SQL and Haskell, so there might be
tools to automate some of this.

Antoine

>
>> It's considered good in Haskell to get data out of "weak" types into
>> specific types when you want them, as in from a String or from a 'Row
>> Value' - it makes manipulations of the values easier to read and you
>> get the compiler's help figuring things out.
>>
>
> makes sense. It also helps -me- (and possibly future programmers/maintainers) to understand what the heck is going on.
>
>> Which SQL library are you using?
>
> I am using the haskell sqlite (version 0.5.2)
>
> Regards
> S.
>
>>
>>>    name myBrandModel
>>>
>>>    Thanks for reading this far, feel free to complain about my design :D
>>>    Regards
>>>    S.
>>> _______________________________________________
>>> Beginners mailing list
>>> Beginners at haskell.org
>>> http://www.haskell.org/mailman/listinfo/beginners
>>>
>
>



More information about the Beginners mailing list