Tuesday, August 7, 2007

I should read the fine print.

Especially when it's not so fine...

Next week I leave France for two weeks in Tanzania. Nadine and I will climb the Kilimanjaro, then visit.

The parcs entrances is not included in the price... Nadine just noticed about it.

Result, it'll be an additional cost of about $1000 each. Yeepeee

Fame ! Well, almost.

I was reading haskell weekly news and I saw a familiar post in the "haskell news from the blogosphere" list.

It's a post from my blog. I have no idea how it ended up there.

It's kind of cool. I guess I'll have to try blogging more often.

UPDATE : I realized that all my haskell-related posts are in there ! Oh the jitters !

Thursday, August 2, 2007

Monads are hard to teach.

At first, I thought monads were too complicated for me.

Then after weeks, I slowly grew a kind of fuzzy understanding of monads, at least enough to feel comfortable using a few of them.

I can't rewrite the State monad without re-reading a tutorial, but I know that I would understand it better this time.

But sometimes on haskell-cafe you read a discussion about monads, and as always, the discussion goes into category, set or whatever math theory I don't know about.

After that kind of discussion, comes the feeling that I really only have scratched the surface of what monads are, and that it's really some deep ( and complicated ) subject.

Yet, high level haskellers often tell you that it's not !

Beginners like me, with a strong background in imperative programming languages, and without much mathematical knowledge, need a simple way of learning monads.

Perhaps, taking a few parts of haskell ( Parsec, State, List, Maybe, STM ) and without insiting on the fact it's monads, explain their usefulness, what's the advantage to having programmed them that way. And at that point, don't confuse the beginner with the monadic laws.
The most important, have lots of real-world example cases.

From then on, time to show the beginner that there's a common pattern in those parts, and tell more about monads, introduce the monadic laws.

Talk about monad transformers, but not in too much details yet.
Again, have lots of real-world examples.

Friday, May 25, 2007

Sandy eyes

Yesterday we were invited and ate a raclette. It's a bit hot to eat a raclette but I love it. I ate as if I was hungry.

Back at home around 1:30 am. Wake up at 6.

Ouch. I feel sleepy.

Parsec

I've been learning parsec for a few days and my brain cells are aching.
It's amazing though what can be done with it.

I started to write a parser for a weird programming language we use at work. I would have been too lazy to do it in C, besides I never wrote a parser and have no knowledge about them.

Yet with Haskell and parsec, step by step, I add more things that parse.

It's hell when something doesn't parse the way I thought I tell it to. Being used to debug program by setpping through the code or tracing doesn't help once you switch to Haskell. This is the part that make the brain hurt.

Sunday, May 20, 2007

The theme for this blog sucks.

But I don't have enough time to try do design something.
Me = Sad.

I'll at least try to modify it so its wider.

Sometime.

Hopefully.

Thursday, May 17, 2007

First attempt at using GTK with haskell.

Having managed to connect to a mysql database, the next step I wanted to try is display the result in a graphical window.

I would have loved to find QT bindings for haskell, but it seems that doesn't exist.
So, the choices for a GUI are limited to wxHaskell and Gtk2hs. There are other GUI libraries but they're higher level and I didn't want to begin with something maybe too hard. I choose Gtk2hs.

I downloaded, compiled and installed Gtk2hs without trouble and my first program is an exact replica of the glade tutorial.

After that, I modified the window to add a 4th part to the vbox, made a few modifications and added a treeview :











Now I wanted to be able to add my own data to the list.

I had a look at ListDemo.hs from Gtk2hs' examples, but it creates its own treeview, while I get mine from the XML file glade creates.

I tried reading the docs to find the relevant functions but it's not that well explained. Fortunately, ListTest.hs uses a xml file from glade.

From the example, this is the part that manipulate the treeview :

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
import Graphics.UI.Gtk.ModelView as New

data Phone = Phone { name :: String, number :: Int, marked :: Bool }

main = do

...

view <- xmlGetWidget xml castToTreeView "view"

...

-- create a new list store
store <- storeImpl
New.treeViewSetModel view store
setupView view store

...

setupView view model = do
New.treeViewSetHeadersVisible view True

-- add a couple columns
renderer1 <- New.cellRendererTextNew
col1 <- New.treeViewColumnNew
New.treeViewColumnPackStart col1 renderer1 True
New.cellLayoutSetAttributes col1 renderer1 model $ \row -> [ New.cellText := name row ]
New.treeViewColumnSetTitle col1 "String column"
New.treeViewAppendColumn view col1

...

storeImpl =
New.listStoreNew
[Phone { name = "Foo", number = 12345, marked = False }
,Phone { name = "Bar", number = 67890, marked = True }
,Phone { name = "Baz", number = 39496, marked = False }]



The thing I want, though, is feed it the result of a sql query. The result is a list of rows, each row itself being a list of strings. The first row has the column titles.

The storeImpl funtion needs to change to (dbresult being the result from the database)
storeImpl = New.listStoreNew ( tail dbresult )

Now to display this I have to change the setupView function; it has to take an arbitrary list of columns. If you look at the ListTest example, you see that now many things change between the different column creation : the title, and how to get the column's content from a row.

This is how I did it :

setupView view model cols= do
New.treeViewSetHeadersVisible view True

mapM ( \(num,title) -> newcol view model title $ \row -> [ New.cellText := row !! num ] ) $ zip [0..] cols
where
newcol view model title content = do
renderer <- New.cellRendererTextNew
col <- New.treeViewColumnNew
New.treeViewColumnPackStart col renderer True
New.cellLayoutSetAttributes col renderer model $ content
New.treeViewColumnSetTitle col title
New.treeViewAppendColumn view col

The newcol function creates a new column based on the title and how to get the content from the liststore, to create all the columns I map newcol through the list of numbered columns.

And it works !

Here's the full source of the example, some stuff from the glade tutorial is still left over and there are still some French comments, but it works here :)

import Prelude
import IO hiding (bracket)
import Control.Exception

import Database.HSQL as Hsql
import Database.HSQL.MySQL as Hsql

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
import Graphics.UI.Gtk.ModelView as New

data Phone = Phone { name :: String, number :: Int, marked :: Bool }

main = do
initGUI
Just xml <- xmlNew "hellohaskell.glade"
--
-- Réagir à la fermeture de la fenetre
--
window <- xmlGetWidget xml castToWindow "window1"
onDestroy window mainQuit
--
-- Réagir au clic sur bouton fermer
--
clbutton <- xmlGetWidget xml castToButton "button2"
onClicked clbutton $ do
putStrLn "Close Button Clicked"
onClicked clbutton $ do
widgetDestroy window
--
-- Réagir au clic sur bouton ok
--
prompt <- xmlGetWidget xml castToLabel "label1"
txtfield <- xmlGetWidget xml castToEntry "entry1"
okbutton <- xmlGetWidget xml castToButton "button1"
onClicked okbutton $ do
name <- get txtfield entryText
set prompt [ labelText := "Hello " ++ name ]

onEntryActivate txtfield $ do
name <- get txtfield entryText
set prompt [ labelText := "Hello " ++ name ]
--
-- Mettre des lignes dans le listview
--
recupbase <- testdb
view <- xmlGetWidget xml castToTreeView "treeview1"
store <- New.listStoreNew ( tail recupbase )
New.treeViewSetModel view store
setupView view store (head recupbase)

--
-- Lancer le tout
--
mainGUI


setupView view model cols= do
New.treeViewSetHeadersVisible view True

mapM ( \(num,title) -> newcol view model title $ \row -> [ New.cellText := row !! num ] ) $ zip [0..] cols

-- add a couple columns
where
newcol view model title content = do
renderer <- New.cellRendererTextNew
col <- New.treeViewColumnNew
New.treeViewColumnPackStart col renderer True
New.cellLayoutSetAttributes col renderer model $ content
New.treeViewColumnSetTitle col title
New.treeViewAppendColumn view col

-- The part where I handle the SQL query

db_serv = "127.0.0.1"
db_shema = "test"
db_user = "root"
db_pass = ""

sql = "SELECT * FROM adr ORDER BY num"

testdb = withdb db_serv db_shema db_user db_pass (dbquery sql recupRows)

withdb serv shema user pass func =
bracket (Hsql.connect serv shema user pass >>= ( \cnx -> putStrLn "Connected" >> return cnx))
(\cnx -> Hsql.disconnect cnx >> putStrLn "Disconnected")
(\cnx -> func cnx)

dbquery sql func cnx=
bracket (Hsql.query cnx sql >>= ( \stmt -> putStrLn ( "Ran query " ++ sql ) >> return stmt))
(\stmt -> Hsql.closeStatement stmt >> putStrLn "Closed query")
(\stmt -> func stmt)

recupRows :: Statement -> IO [[String]]
recupRows stmt = do
let champs = map (\(a,b,c) -> a) $ getFieldsTypes stmt
lignes <- collectRows (\s -> mapM (getFieldValue s) champs ) stmt
return ( champs : lignes )