I am a novice haskell programmer and I am trying to write some Haskell cgi which will read from a MySQL DB and output JSON. I am able to generate the right JSON but am unable to get the data types correctly to be able to output JSON correctly. I also think that I am primarily thinking imperative still. Here is my code. Note that getTopBrands provides json output.
My problem is that I am unable to figure out how to return "[Char]" from getTopBrands and not "IO [Char]". It looks to me I am still thinking imperative. Any pointers, suggestions to fix this would be greatly appreciated. Please let me know if I need to provide the rest of the code.
RODB.hs:
{-# LANGUAGE RecordWildCards, OverloadedStrings, PackageImports #-}
module Main where
import RODB
import ROOutput
import System.Environment
import Database.HDBC
import Network.Socket(withSocketsDo)
import Network.CGI
import Text.XHtml
import qualified "bytestring" Data.ByteString.Lazy.Char8 as LBS
import Data.Aeson
page :: Html
page = body << h1 << str
main = runCGI $ handleErrors cgiMain
cgiMain :: CGI CGIResult
cgiMain =
do out <- getTopBrands 10 1
setHeader "Content-type" "application/json"
output $ renderHtml page out
getTopBrands :: Integer -> Integer -> IO [Char]
getTopBrands limit sorted =
do let temp = 0
dbh <- connect "127.0.0.1" "ReachOutPublicData" "root" "admin" "/tmp/mysql.sock"
if sorted == 1
then do brandlist <- getBrands dbh limit True
json <- convPublicBrandEntrytoJSON brandlist
return $ LBS.unpack json
else do brandlist <- getBrands dbh limit False
json <- convPublicBrandEntrytoJSON brandlist
return $ LBS.unpack json
As Niklas B said, getTopBrands
being in IO
is right, since it depends on I/O. I guess your problem is that you get a type error from that when you try to use it directly,
cgiMain :: CGI CGIResult
cgiMain =
do out <- getTopBrands 10 1
setHeader "Content-type" "application/json"
output $ renderHtml page out
since all statements in a do-block must belong to the same monad, and the rest of the block is in CGI
. But, CGI
is a MonadIO
, thus you can simply liftIO
it into CGI
,
cgiMain :: CGI CGIResult
cgiMain =
do out <- liftIO $ getTopBrands 10 1
setHeader "Content-type" "application/json"
output $ renderHtml page out
The next point Niklas raised is also right, the second Integer
argument of getTopBrands
should really be a Bool
. However, even with its current type, the code duplication is entirely unnecessary, the difference between the two branches is just the Bool
argument to getBrands
, so
getTopBrands :: Integer -> Integer -> IO [Char]
getTopBrands limit sorted =
do let temp = 0
dbh <- connect "127.0.0.1" "ReachOutPublicData" "root" "admin" "/tmp/mysql.sock"
brandlist <- getBrands dbh limit (sorted == 1)
json <- convPublicBrandEntrytoJSON brandlist
return $ LBS.unpack json
just pass it the condition on which you branched.
Niklas' third point
I also don't see why
convPublicBrandEntrytoJSON
would need to live inIO
, but since you didn't provide its definition I cannot suggest an improvement here.
also looks very valid, a conversion would usually be a pure function. If the only reason why it is in IO
is the ability to write
json <- convPublicBrandEntrytoJSON brandlist
you should be aware that you can bind results of pure functions in a do-block
let json = convPublicBrandEntrytoJSON brandlist
using let
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With