With IHP (the haskell web framework) I created a web application. Now I want to create a IHP Script to load some external data into my database. However I'm getting a lot of import conflicts from the Prelude, but not the types I expected.
#!/usr/bin/env run-script
module Application.Script.DataLoader where
import Application.Script.Prelude hiding (decode, pack, (.:))
import qualified Data.ByteString.Lazy as BL
import Data.Csv
import Data.Text (pack)
import qualified Data.Vector as V
import Control.Monad (mzero)
instance FromNamedRecord Product where
parseNamedRecord r = Product def <$> r .: "title" <*> r .: "price" <*> r .: "category" <*> pure def
run :: Script
run = do
csvData <- BL.readFile "~/tender/data/Boiler-en-kookkraan_Boiler.csv"
case decodeByName csvData of
Left err -> putStrLn $ pack err
Right (_, v) -> V.forM_ v $ \ p ->
putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"
Where my Product
schema looks like this:
CREATE TABLE products (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
price DOUBLE PRECISION NOT NULL,
category TEXT NOT NULL
);
Is there a way to use the types I created as a Data object to e.g. read my csv to?
[Updated output]
Application/Script/DataLoader.hs:12:26: error:
• Couldn't match type ‘MetaBag -> Product' a1’
with ‘Product' (QueryBuilder ProjectProduct)’
Expected type: Parser Product
Actual type: Parser (MetaBag -> Product' a1)
• In the expression:
Product def <$> r .: "title" <*> r .: "price" <*> r .: "category"
<*> pure def
In an equation for ‘parseNamedRecord’:
parseNamedRecord r
= Product def <$> r .: "title" <*> r .: "price" <*> r .: "category"
<*> pure def
In the instance declaration for ‘FromNamedRecord Product’
|
12 | parseNamedRecord r = Product def <$> r .: "title" <*> r .: "price" <*> r .: "category" <*> pure def
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Solved, all credits to the help of @mpscholten]
#!/usr/bin/env run-script
module Application.Script.DataLoader where
import Application.Script.Prelude hiding (decode, pack, (.:))
import qualified Data.ByteString.Lazy as BL
import Data.Csv
import Data.Text (pack)
import qualified Data.Vector as V
import Control.Monad (mzero)
parseProduct :: NamedRecord -> Parser Product
parseProduct r = do
title <- r .: "title"
price <- r .: "price"
category <- r .: "category"
newRecord @Product
|> set #title title
|> set #price price
|> set #category category
|> pure
run :: Script
run = do
csvData <- BL.readFile "data/Boiler-en-kookkraan_Boiler.csv"
case decodeByNameWithP parseProduct defaultDecodeOptions csvData of
Left err -> putStrLn $ pack err
Right (_, v) -> V.forM_ v $ \ p ->
putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"
Inside the FromNamedRecord
instance you are missing two fields: id
and meta
. The id
field is the first field of the record. The meta
field is a hidden field used by IHP to keep track of validation errors. It's always the last field of a record.
The easiest way to solve this is to use newRecord
and write out the code in a more explicit way:
instance FromNamedRecord Product where
parseNamedRecord r = do
title <- r .: "title"
price <- r .: "price"
category <- r .: "category"
newRecord @Product
|> set #title title
|> set #price price
|> set #category category
|> pure
For the error "Ambiguous occurrence ‘title’" try to use the get
function instead of using the normal haskell accessor function:
putStrLn $ (get #title p) ++ ", " ++ show (get #price p) ++ " euro"