How do I get the named fields in haskell correctly? - haskell

I am writing a parser with the help of parsec and I have a problem
data Param = Param {paramName::String, argument :: Maybe String}
deriving (Show)
paramExpr1 :: Parser Param
paramExpr1 = do
paramKeyword
void $ lexeme $ char '-'
paramName <- word
return $ Param paramName Nothing
paramExpr3 :: Parser Param
paramExpr3 = do
pN <- paramExpr1 -- <- PROBLEM HERE
return $ Param pN Nothing
In short, I don't understand how to get the named field, paramExpr1 will return Param and I would like to get paramName, but I don't understand how

You can extract the field by using the field name as a function:
paramExpr3 :: Parser Param
paramExpr3 = do
pN <- paramExpr1
return $ Param (paramName pN) Nothing
Alternatively, instead of creating a new value using the constructor Param, you can use the record update syntax and change the fields you want to change while leaving the others as they are.
paramExpr3 :: Parser Param
paramExpr3 = do
pN <- paramExpr1
return pN{ argument = Nothing }
More alternatives exist (e.g. using lenses), but these are the most basic approaches.

Parser is a functor, so you can map a function over a parser to apply the function to whatever the parser will produce.
paramExpr3 :: Parser Param
paramExpr3 = do
pName <- fmap parserName parseExpr1 -- turn a Parser Param into a Parser String
return $ Param pName Nothing
If you had a function of type Param -> Param that replaced the existing argument field with Nothing
toNothing :: Param -> Param
toNothing (Param n _) = Param n Nothing
you could map that directly over the parsers.
parseExpr3 :: Parser Param
parseExpr3 = fmap toNothing parseExpr1

Related

Why the type signature changed to monad, thus giving extra functionality

Please look at the following example:
import qualified Data.Map as Map
candidateDB :: Map.Map Int String
candidateDB = Map.fromList [(1,"candidate1")
,(2,"candidate2")
,(3,"candidate3")]
assessCandidateMaybe :: Int -> Maybe String
assessCandidateMaybe cId = do
candidate <- Map.lookup cId candidateDB
let passed = candidate ++ "x"
return passed
assessCandidateMaybe' cId = do
candidate <- cId
let passed = candidate ++ "x"
return passed
candidates = ["candidate1","candidate2","candidate3"]
main :: IO ()
main = do
print(assessCandidateMaybe 1)
print(assessCandidateMaybe' (Map.lookup 1 candidateDB))
print(assessCandidateMaybe' (Just "Box"))
print(assessCandidateMaybe' candidates)
In assessCandidateMaybe, I used Map.lookup. When I put the Map.lookup outside the assessCandidateMaybe function, thus creating assessCandidateMaybe', it became a monad.
assessCandidateMaybe' :: Monad m => m String -> m String
Now I can even pass a maybe String or list of Strings to it and it works.
$ runhaskell examples.hs
Just "candidate1x"
Just "candidate1x"
Just "Boxx"
["candidate1x","candidate2x","candidate3x"]
What is going on here?

Making an 'optional' parser using optparse-applicative and constructing value for recursive data type

I have a data type called EntrySearchableInfo written like this
type EntryDate = UTCTime -- From Data.Time
type EntryTag = Tag -- String
type EntryName = Name -- String
type EntryDescription = Description -- String
type EntryId = Int
data EntrySearchableInfo
= SearchableEntryDate EntryDate
| SearchableEntryTag EntryTag
| SearchableEntryName EntryName
| SearchableEntryDescription EntryDescription
| SearchableEntryId EntryId
Basically represents things that make sense in 'search' context.
I want to write a function with this type
entrySearchableInfoParser :: Parser (Either String EntrySearchableInfo)
which (I think) will be a combination of several primitive Parser <Type> functions I have already written
entryDateParser :: Parser (Either String UTCTime)
entryDateParser = parseStringToUTCTime <$> strOption
(long "date" <> short 'd' <> metavar "DATE" <> help entryDateParserHelp)
searchableEntryDateParser :: Parser (Either String EntrySearchableInfo)
searchableEntryDateParser = SearchableEntryDate <$$> entryDateParser -- <$$> is just (fmap . fmap)
searchableEntryTagParser :: Parser (Either String EntrySearchableInfo)
searchableEntryTagParser = ...
...
So I have two questions:
How do I combine those parsers to make entrySearchableInfoParser functions.
EntrySearchableInfo type is a part of a larger Entry type defined like this
data Entry
= Add EntryDate EntryInfo EntryTag EntryNote EntryId
| Replace EntrySearchableInfo Entry
| ...
...
I already have a function with type
entryAdd :: Parser (Either String Entry)
which constructs Entry using Add.
But I'm not sure how to make Entry type using Replace with entrySearchableInfoParser and entryAdd.
So combining those parsers were a lot simpler than I imagined.
I just had to use <|>
entrySearchableInfoParser :: Parser (Either String EntrySearchableInfo)
entrySearchableInfoParser =
searchableEntryDateParser
<|> searchableEntryTagParser
<|> searchableEntryNameParser
<|> searchableEntryDescriptionParser
<|> searchableEntryIdParser
and constructing Entry type using Replace with entrySearchableInfoParser and entryAdd was too.
entryAdd :: Parser (Either String Entry)
entryAdd = ...
entryReplace :: Parser (Either String Entry)
entryReplace = liftA2 Edit <$> entrySearchableInfoParser <*> entryAdd
Now it works perfectly!

Parsing user options into custom data types with OptParse-Applicative

I'm trying to build a CLI food journal app.
And this is the data type I want the user input to be parsed in.
data JournalCommand =
JournalSearch Query DataTypes Ingridents BrandOwnder PageNumber
| JournalReport Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber
| JournalDisplay FromDate ToDate ResultNumber
| JournalStoreSearch Query DataTypes Ingridents BrandOwnder PageNumber ResultNumber StoreFlag
| JournalStoreCustom CustomEntry OnDate StoreFlag
| JournalDelete FromDate ToDate ResultNumber
| JournalEdit CustomEntry ResultNumber
deriving (Show, Eq)
and because there's a lot of overlap I have a total of 8 functions with Parser a type.
Functions like these
-- | Search Query
aQueryParser :: Parser String
aQueryParser = strOption
( long "search"
<> short 's'
<> help "Search for a term in the database"
)
The idea if to ultimately have a function like this
runJournal :: JournalCommand -> MT SomeError IO ()
runJournal = \case
JournalSearch q d i b p
-> runSearch q d i b p
JournalReport q d i b p r
-> runSearchAndReport q d i b p r
...
...
where MT is some monad transformer that can handle error + IO. Not sure yet.
The question is: How do I setup the parseArgs function
parseArgs :: IO JournalCommand
parseArgs = execParser ...
and parser function
parser :: Parser JournalCommand
parser = ...
so that I'd be able to parse user input into JournalCommand and then return the data to relevant functions.
I know I can fmap a data type like this
data JournalDisplay { jdFromDate :: UTCTime
, jdToDate :: UTCTime
, jdResultNumber :: Maybe Int
}
as
JournalDisplay
<$>
fromDateParser
<*>
toDateParser
<*>
optional resultNumberParser
But I'm not sure how to go about doing that with my original data structure.
I think I need to have a list like this [Mod CommandFields JournalCommand] which I may be able to pass into subparser function by concatenating the Mod list. I'm not completely sure.
In optparse-applicative there's the Parser type, but also the ParserInfo type which represents a "completed" parser holding extra information like header, footer, description, etc... and which is ready to be run with execParser.
We go from Parser to ParserInfo by way of the info function which adds the extra information as modifiers.
Now, when writing a parser with subcommands, each subcommand must have its own ParserInfo value (implying that it can have its own local help and description).
We pass each of these ParserInfo values to the command function (along with the name we want the subcommand to have) and then we combine the [Mod CommandFields JournalCommand] list using mconcat and pass the result to subparser. This will give us the top-level Parser. We need to use info again to provide the top-level description and get the final ParserInfo.
An example that uses a simplified version of your type:
data JournalCommand =
JournalSearch String String
| JournalReport String
deriving (Show, Eq)
journalParserInfo :: O.ParserInfo JournalCommand
journalParserInfo =
let searchParserInfo :: O.ParserInfo JournalCommand
searchParserInfo =
O.info
(JournalSearch
<$> strArgument (metavar "ARG1" <> help "This is arg 1")
<*> strArgument (metavar "ARG2" <> help "This is arg 2"))
(O.fullDesc <> O.progDesc "desc 1")
reportParserInfo :: O.ParserInfo JournalCommand
reportParserInfo =
O.info
(JournalReport
<$> strArgument (metavar "ARG3" <> help "This is arg 3"))
(O.fullDesc <> O.progDesc "desc 2")
toplevel :: O.Parser JournalCommand
toplevel = O.subparser (mconcat [
command "search" searchParserInfo,
command "journal" reportParserInfo
])
in O.info toplevel (O.fullDesc <> O.progDesc "toplevel desc")

Yet Another Haskell Rigid Type Variable Error

I've investigated many answers to other rigid type variable error questions; but, alas, none of them, to my knowledge, apply to my case. So I'll ask yet another question.
Here's the relevant code:
module MultipartMIMEParser where
import Control.Applicative ((<$>), (<*>), (<*))
import Text.ParserCombinators.Parsec hiding (Line)
data Header = Header { hName :: String
, hValue :: String
, hAddl :: [(String,String)] } deriving (Eq, Show)
data Content a = Content a | Posts [Post a] deriving (Eq, Show)
data Post a = Post { pHeaders :: [Header]
, pContent :: [Content a] } deriving (Eq, Show)
post :: Parser (Post a)
post = do
hs <- headers
c <- case boundary hs of
"" -> content >>= \s->return [s]
b -> newline >> (string b) >> newline >>
manyTill content (string b)
return $ Post { pHeaders=hs, pContent=c }
boundary hs = case lookup "boundary" $ concatMap hAddl hs of
Just b -> "--" ++ b
Nothing -> ""
-- TODO: lookup "boundary" needs to be case-insensitive.
content :: Parser (Content a)
content = do
xs <- manyTill line blankField
return $ Content $ unlines xs -- N.b. This is the line the error message refers to.
where line = manyTill anyChar newline
headers :: Parser [Header]
headers = manyTill header blankField
blankField = newline
header :: Parser Header
header =
Header <$> fieldName <* string ":"
<*> fieldValue <* optional (try newline)
<*> nameValuePairs
where fieldName = many $ noneOf ":"
fieldValue = spaces >> many (noneOf "\r\n;")
nameValuePairs = option [] $ many nameValuePair
nameValuePair :: Parser (String,String)
nameValuePair = do
try $ do n <- name
v <- value
return $ (n,v)
name :: Parser String
name = string ";" >> spaces >> many (noneOf "=")
value :: Parser String
value = string "=" >> between quote quote (many (noneOf "\r\n;\""))
where quote = string "\""
And the error message:
Couldn't match type `a' with `String'
`a' is a rigid type variable bound by
the type signature for content :: Parser (Content a)
at MultipartMIMEParser.hs:(See comment in code.)
Expected type: Text.Parsec.Prim.ParsecT
String () Data.Functor.Identity.Identity (Content a)
Actual type: Text.Parsec.Prim.ParsecT
String () Data.Functor.Identity.Identity (Content String)
Relevant bindings include
content :: Parser (Content a)
(bound at MultipartMIMEParser.hs:72:1)
In a stmt of a 'do' block: return $ Content $ unlines xs
In the expression:
do { xs <- manyTill line blankField;
return $ Content $ unlines xs }
In an equation for `content':
content
= do { xs <- manyTill line blankField;
return $ Content $ unlines xs }
where
line = manyTill anyChar newline
From what I've seen, the problem is that I'm explicitly returning a String using unlines xs, and that breaks the generic nature of a in the type signature. Am I close to understanding?
I've declared Content to be generic because, presumably, this parser might eventually be used on types other than String. Perhaps I'm abstracting prematurely. I did try removing all my as, but I started getting many more compile errors. I think I'd like to stick with the generic approach, if that's reasonable at this point.
Is it clear from the code what I'm trying to do? If so, any suggestions on how to do it best?
You're telling the compiler that content has type Parser (Content a), but the line causing the error is
return $ Content $ unlines xs
Since unlines returns a String, and the Content constructor has type a -> Content a, here you would have String ~ a, so the value Content $ unlines xs has type Content String. If you change the type signature of content to Parser (Content String) then it should compile.
I've declared Content to be generic because, presumably, this parser might eventually be used on types other than String. Perhaps I'm abstracting prematurely. I did try removing all my as, but I started getting many more compile errors. I think I'd like to stick with the generic approach, if that's reasonable at this point.
It's fine to declare Content to be generic, and in many cases it is the exact right way to solve the problem, the issue is that while your container is generic, whenever you fill your container with something concrete, the type variables also have to be concrete. In particular:
> :t Container (1 :: Int)
Container 1 :: Container Int
> :t Container "test"
Container "test" :: Container String
> :t Container (Container "test")
Container (Container "test") :: Container (Container String)
Notice how all of these have their types inferred without any type variables left. You can use the container to hold whatever you want, you just have to make sure that you're accurately telling the compiler what it is.

Search through a haskell list

So I have this calculator I'm building that accepts user variable inputs like "let a = 2" These variable are stored in a List of Tuples (Variable, value) I need help with getting the data from this list. My code so far
primary :: Parser Float
primary = do symbol "("
e <- expression
symbol ")"
return e
+++ do v <- identifier
let a = (find (==(head v)) vlist)
return a
I get an error because find returns a Maybe and I need it to return a Float or give the user an error message. How do I do this?
I'm not sure where vlist comes from. It should probably be part of the parser's user state. For now, let's assume it's a top-level definition:
vlist :: [(String, Float)]
vlist = undefined -- fill in the blanks...
I assume you're using Parsec. You can simplify your parser to:
primary :: Parser Float
primary = choice [ between (symbol "(") (symbol ")") expression
, do { ident <- identifier
; case lookup ident vlist of
Nothing -> fail $ "No such identifier: " ++ ident
Just v -> return v
}
]
You have several options for how to deal with the error. Here, I have used the parser monad's fail function. This will cause the parser to return a Left parserError. Alternatively, you could substitute error for fail, which will result in an error that can only be handled in the IO monad.
Note: To add the vlist as parser state, you need to define a new parser type with that state:
data MyParserState = MyParserState { vlist :: [(String, Float)] }
type MyParser = CharParser MyParserState
-- these parsers now need to return MyParser type!
symbol :: String -> MyParser String
identifier :: MyParser String
expression :: MyParser Float
primary :: MyParser Float
primary = choice [ between (symbol "(") (symbol ")") expression
, do { st <- getState
; ident <- identifier
; case lookup ident $ vlist st of
Nothing -> fail $ "No such identifier: " ++ ident
Just v -> return v
}
]
Perhaps run it through a case x of block like so:
case find x of (Just y) -> --your code here
Nothing -> error "Wrong"

Resources