How to reuse IHP classes in a IHP script? - haskell

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"

Can you maybe share what errors you get exactly? Your desired product type should be in Generated.Types which intern is loaded by Application.Script.Prelude.
I think you might have two models that both have the field title. In haskell fields are functions and they may not be used twice.

Related

Lookup values inside a Cassava-ingested CSV

I successfully read in a CSV using Cassava (http://hackage.haskell.org/package/cassava) with this:
getData = do
csvData <- BL.readFile "data.csv"
case decodeByName csvData of
Left err -> putStrLn err
Right (_, v) -> V.forM_ v $ \ p ->
putStrLn $ col1 p ++ "," ++ col2 p ++ "," ++ (show $ col3 p) ++ "," ++ (show $ col4 p) ++ "," ++ (show $ col5 p) ++ "," ++ col6 p ++ "," ++ (show $ col7 p) ++ "," ++ (show $ col8 p) ++ "," ++ (show $ col9 p) ++ "," ++ (show $ col10 p)
What I actually need to do is use the values in col3 as keys to find values in col10.
Someone suggested that I use Map from Data.Map (https://hackage.haskell.org/package/containers-0.4.0.0/docs/Data-Map.html) for this, but I'm not sure how to approach this.
Everything I have tried so far has not worked. I assume you enter the Map inside the Right case, along the lines of:
Right (_, v) -> Map (V.forM_ v) ???
But I am stuck on how to proceed. Would appreciate any suggestions. Ideally, I would want to modify getData so that it is getData keyToFetch = ... -- and that keyToFetch would be used in the Map.
Yes, it is probably a good idea to use Data.Map to find values in col10 using values in col3 as keys.
As we have little data about col3, col10 and the exact data type you are using, I will resort to adapting the decodebyName example in the Cassava documentation to the idea of generating a map object. The example is based on a very simple {name, salary} type of record.
The two branches of the case of construct have to return a common type, in our case a Data.Map object instead of an IO () action. Fortunately, the error function is flexible enough that it can pretend to be of the appropriate type.
This would give this sort of code:
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative
import qualified Data.ByteString.Lazy as BL
import Data.Csv
import qualified Data.Vector as V
import qualified Data.Map as M
import Control.Monad (forM_)
data Person = Person
{ name :: !String
, salary :: !Int
} deriving (Show, Ord, Eq) -- need that for Map objects
instance FromNamedRecord Person where
parseNamedRecord r = Person <$> r .: "name" <*> r .: "salary"
-- build a map object:
makeMap :: V.Vector Name -> V.Vector Person -> M.Map String Int
makeMap hdr pvec =
-- with name and salary playing the role of col3 and col10:
let pls = V.toList pvec -- get a list
zls = zip (map name pls) (map salary pls)
in M.fromList zls
showRecord :: String -> Int -> String
showRecord name salary = name ++ " earns " ++ (show salary) ++ " dollars"
main :: IO ()
main = do
csvData <- BL.readFile "salaries.csv"
let ma = case decodeByName csvData of
Left errMsg -> error $ "decodeByName failed: " ++ errMsg
Right (hdr, pvec) -> makeMap hdr pvec
-- print out the Map object:
putStrLn $ "Contents of map object:"
putStrLn $ show ma
putStrLn $ ""
forM_ (M.toList ma) (\(n,s) -> putStrLn $ showRecord n s)
let sal1 = M.lookup "John Doe" ma
putStrLn $ "sal1 = " ++ (show sal1)
--
Execution:
Contents of map object:
fromList [("Jane Doe",60000),("John Doe",50000)]
Jane Doe earns 60000 dollars
John Doe earns 50000 dollars
sal1 = Just 50000
Note that I have to use plain lists extensively, as for some reason there is no direct route from vectors to maps, something discussed already in this SO question.

Dynamic parent element

I have this piece of code and it works perfectly fine. It toggles some styles on the text input field depending on the field value.
numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
numberInput = divClass "form-group" $ do
let errorState = "style" =: "border-color: red"
validState = "style" =: "border-color: green"
rec n <- textInput $ def & textInputConfig_inputType .~ "number"
& textInputConfig_initialValue .~ "0"
& textInputConfig_attributes .~ attrs
let result = fmap (readMay . unpack) $ _textInput_value n
attrs = fmap (maybe errorState (const validState)) result
return result
I ran into some problems making the parent element dynamic. I'd like to toggle the styles on the parent element of text input. I'd like to write something like but failed!
numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
numberInput = do
rec
dynAttrs <- -- reference to textInput
elDynAttr "div" dynAttrs $ do
n <- textInput $ def & textInputConfig_inputType .~ "number"
& textInputConfig_initialValue .~ "0"
...
Thanks for helping out!
Here is a little program where the attributes (align right or left) of the parent element depend on the state of a child element:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
import Reflex.Dom
import qualified Data.Text as T
import Safe
main :: IO ()
main = mainWidget bodyElement
bodyElement :: MonadWidget t m => m ()
bodyElement = el "div" $ do
el "h2" $ text "Dynamic parent element"
numberInput
return ()
numberInput :: (MonadWidget t m) => m (Dynamic t (Maybe Double))
numberInput = do
rec
let errorState = ("align" :: T.Text) =: "right"
validState = "align" =: "left"
let result = fmap (readMay . T.unpack) $ _textInput_value n
attrs = fmap (maybe errorState (const validState)) result
n <- elDynAttr "div" attrs $ do
n1 <- textInput $ def & textInputConfig_inputType .~ "number"
& textInputConfig_initialValue .~ "0"
text "Some additional control"
return n1
return result
As user user2407038 mentions, we have to return the numeric text element from an inner scope to the outer scope. Then we can use recursive do to access the element we define later in the program.
As soon as you type a letter into the numeric field, it will jump to the right.

Better way to get a list of the top level declarations

I have a module called EditorTest in a similarly-named file.
It imports some modules. It also has some declarations in it, as any Haskell module does.
I'd like to programmatically obtain a list of the top level available declarations within the context of that module.
What I have so far is a way to get the top level local declarations of this module only, by using hint. That's fine, and I figure I could recurse the imports (and so on into those modules, etc), collecting a list of them, then use hint to grab all the available declaration names... but if possible, I'm after an easier way to find the available declarations in a given module. I figure Haskell's API should have some way to do that.
Is there such a thing?
Self-answering because it's been a while and I spiked out a proof of concept ages ago, so I'll put it here for others, even if it is fairly messy.
So, assuming we have this file as TestModule.hs:
module TestModule (g, h) where
import Data.List as L
f = head
g = f [f]
h = L.map
Then the following code can be used to show how we can pull out the top level declarations and use them by using Hint's Language.Haskell.Interpreter module and the Language.Haskell.Exts.SrcLoc module from the haskell-src-exts package:
import Data.List
import Control.Monad
import qualified Language.Haskell.Interpreter as I
import Language.Haskell.Interpreter (Interpreter, GhcError(..), InterpreterError(..))
import qualified Language.Haskell.Exts as H
import Language.Haskell.Exts.SrcLoc
main :: IO ()
main = do r <- I.runInterpreter interpreterTest
case r of
Left err -> putStrLn $ errorString err
Right () -> return ()
errorString :: InterpreterError -> String
errorString (WontCompile es) = intercalate "\n" (header : map unbox es)
where
header = "ERROR: Won't compile:"
unbox (GhcError e) = e
errorString e = show e
p :: String -> Interpreter ()
p = I.liftIO . putStrLn
emptyLine :: Interpreter ()
emptyLine = p ""
interpreterTest :: Interpreter ()
interpreterTest =
do
p "Finding out what the module exports are."
p "To do this, we grab the text of the src/TestModule.hs"
moduleContents <- I.liftIO . readFile $ "src/TestModule.hs"
emptyLine
p "Next, we find what the import module names are, so we can load them"
p "To do that, we'll parse the contents of src/TestModule.hs as AST"
let
parseResult = H.parseModule moduleContents
parsedModuleDeclE =
case parseResult of
H.ParseOk parsed -> Right parsed
H.ParseFailed _ errorReason -> Left errorReason
(moduleDeclarations, moduleImports) =
case parsedModuleDeclE of
Left _ ->
([], [])
Right (H.Module _ _ _ _ _ importDecls decls) -> (decls, importDecls)
p $ show moduleImports
emptyLine
p "Now we can pull the module names out of the ImportDecl values:"
let
moduleNames =
do
H.ModuleName s <- fmap H.importModule moduleImports
return s
p $ show moduleNames
emptyLine
p "After this we can obtain a list of the module exports for each of these modules and join them together:"
I.loadModules ["src/TestModule.hs"]
I.setTopLevelModules ["TestModule"]
topLevelImportDecls <- fmap concat $ mapM I.getModuleExports moduleNames
-- p $ show $ do { I.Fun f <- topLevelImportDecls ; return f } -- filter for functions only
p $ show $ fmap I.name topLevelImportDecls
emptyLine
p "Then, we can get the top level declarations of the initial module and add them, too:"
-- p $ show moduleDeclarations -- AST in haskell types
let
localDeclaractions =
do
(H.PatBind _ (H.PVar (H.Ident declName)) _ _) <- moduleDeclarations
return declName
availableDeclarations =
localDeclaractions ++ fmap I.name topLevelImportDecls
p $ show localDeclaractions
emptyLine
p "Finally, we have our big list:"
p $ show availableDeclarations
emptyLine
p "Now, what about we get all of their types, just for fun?"
typesOfAll <- sequence $ fmap (\n -> fmap ((n ++ " :: ") ++) (I.typeOf n)) availableDeclarations
p $ Data.List.intercalate "\n" typesOfAll
return ()

Parsec permutation parsing

I wrote such permutation parsing example:
data Entry = Entry {
first_name :: String
, last_name :: String
, date_of_birth :: Maybe String
, nationality :: Maybe String
, parentage :: Maybe String
} deriving (Show)
nameParser :: Parser (String, String)
nameParser = do
first_name <- many1 upper
endOfLine
last_name <- many1 letter
endOfLine
return $ (first_name, last_name)
attributeParser :: String -> Parser String
attributeParser field = do
string $ field ++ ": "
value <- many1 (noneOf "\n")
endOfLine
return value
entryParser :: Parser Entry
entryParser = do
(f, l) <- nameParser
(d, n, p) <- permute ((,,)
<$?> (Nothing, liftM Just (try $ attributeParser "Date of Birth"))
<|?> (Nothing, liftM Just (try $ attributeParser "Nationality"))
<|?> (Nothing, liftM Just (try $ attributeParser "Parentage"))
)
return $ Entry f l d n p
main = do
mapM_ putStrLn . map (show . parse entryParser "") $ goodTests
goodTests =
"AAKVAAG\nTorvild\nDate of Birth: 1 July\nNationality: Norwegian\nParentage: business executive\n" :
"AAKVAAG\nTorvild\nNationality: Norwegian\nParentage: business executive\n" :
"AAKVAAG\nTorvild\nParentage: business executive\nNationality: Norwegian\n" :
"AAKVAAG\nTorvild\nParentage: business executive\n" :
"AAKVAAG\nTorvild\nNationality: Norwegian\n" : []
It would be good to extend Entry data with new fields in future, but doing that will require to put even more repetitive code in entryParser function. Is there a way to make this function accept list of parsers?
I started with this:
attributeParsers =
map attributeParser ["Date of Birth", "Nationality", "Parentage"]
permuteParams =
map (\p -> (Nothing, liftM Just (try p))) attributeParsers
But could not come of with correct way to fold permuteParams together with <|?> operator (I guess it would require something smarter than (,,) tuple constructor then).
As a first step, you can abstract the stuff you do for every component:
attr txt = (Nothing, liftM Just (try $ attributeParser txt))
With this, you can go to:
entryParser :: Parser Entry
entryParser = do
(f, l) <- nameParser
(d, n, p) <- permute ((,,)
<$?> attr "Date of Birth"
<|?> attr "Nationality"
<|?> attr "Parentage"
)
return $ Entry f l d n p
Then, if you want, you can combine the infix combinators and the attr calls:
f .$ x = f <$?> attr x
f .| x = f <|?> attr x
infixl 2 .$
infixl 2 .|
This gives you:
entryParser :: Parser Entry
entryParser = do
(f, l) <- nameParser
(d, n, p) <- permute ((,,)
.$ "Date of Birth"
.| "Nationality"
.| "Parentage"
)
return $ Entry f l d n p
Then you can further simplify by getting rid of the intermediate triple. All you're doing is to build it and then apply its components to Entry f l, so you can as well apply the result of the permutation parser to Entry f l directly:
entryParser :: Parser Entry
entryParser = do
(f, l) <- nameParser
permute (Entry f l
.$ "Date of Birth"
.| "Nationality"
.| "Parentage"
)
I think this is compact enough. If you really want some kind of fold, you'll either have to introduce an intermediate list and collect the permutation results in a list. This, however, only works as long as all the permutable attributes are of the same type (they currently are), and is not so nice because you'll make assumptions about the number of elements in this list. Or you'll have to use a heterogeneous list / some type class magic, which will lead to more complexity with the types and is, I think, not worth it here.
(<|?>) does not play nicely with folding because the type of the StreamPermParser you pass as its first argument isn't the same than that of the StreamPermParser result. For a simpler yet analogous issue, you would run into similar problems if you were trying to use (,,) with (<$>) and (<*>) in applicative style (e.g. (,,) <$> foo <*> bar <*> baz).
If you want to cut down some of the repetition, my prosaic suggestion would be using a local definition:
entryParser :: Parser Entry
entryParser = do
(f, l) <- nameParser
(d, n, p) <- permute ((,,)
<$?> optField "Date of Birth"
<|?> optField "Nationality"
<|?> optField "Parentage"
)
return $ Entry f l d n p
where
optField fieldName = (Nothing, liftM Just (try $ attributeParser fieldName))

zip AST with bool list

I have an AST representing a haskell program and a bitvector/bool list representing the presence of strictness annotations on Patterns in order.For example, 1000 represents a program with 4 Pats where the first one is a BangPat. Is there any way that I can turn on and off the annotations in the AST according to the list?
-- EDIT: further clarify what I want editBang to do
Based on user5042's answer:
Simple.hs :=
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!x : xs) -> putStrLn "many"
And I want editBang "Simple.hs" [True, True, True, True] to produce
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!(!x : !xs)) -> putStrLn "many"
Given that above are the only 4 places that ! can appear
As a first step, here's how to use transformBi:
import Data.Data
import Control.Monad
import Data.Generics.Uniplate.Data
import Language.Haskell.Exts
import Text.Show.Pretty (ppShow)
changeNames x = transformBi change x
where change (Ident str) = Ident ("foo_" ++ str)
change x = x
test2 = do
content <- readFile "Simple.hs"
case parseModule content of
ParseFailed _ e -> error e
ParseOk a -> do
let a' = changeNames a
putStrLn $ ppShow a'
The changeNames function finds all occurrences of a Ident s and replaces it with Ident ("foo_"++s) in the source tree.
There is a monadic version called transformBiM which allows the replacement function to be monadic which would allow you to consume elements from your list of Bools as you found bang patterns.
Here is a complete working example:
import Control.Monad
import Data.Generics.Uniplate.Data
import Language.Haskell.Exts
import Text.Show.Pretty (ppShow)
import Control.Monad.State.Strict
parseHaskell path = do
content <- readFile path
let mode = ParseMode path Haskell2010 [EnableExtension BangPatterns] False False Nothing
case parseModuleWithMode mode content of
ParseFailed _ e -> error $ path ++ ": " ++ e
ParseOk a -> return a
changeBangs bools x = runState (transformBiM go x) bools
where go pp#(PBangPat p) = do
(b:bs) <- get
put bs
if b
then return p
else return pp
go x = return x
test = do
a <- parseHaskell "Simple.hs"
putStrLn $ unlines . map ("before: " ++) . lines $ ppShow a
let a' = changeBangs [True,False] a
putStrLn $ unlines . map ("after : " ++) . lines $ ppShow a'
You might also look into using rewriteBiM.
The file Simple.hs:
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!x : xs) -> putStrLn "many"

Resources