Reputation: 2439
I have this parser definition:
newtype Parser a = P { getParser :: String -> Maybe (a, String) }
I also have this function:
functionMatching :: Parser (Char, String, Char)
functionMatching = (,,) <$> spot (`elem` "\\") <*> getWord <*> spot (`elem` ".")
How can I make the below function returning the middle element from (Char, String Char)
functionCons:: Parser (Char, String, Char) -> (String, String)
functionCons = undefined
Example:
getParser functionMatching "\\x.y"
Just (('\\',"x",'.'),"y")
I want to extract x and y.
Thanks!
Upvotes: 1
Views: 239
Reputation: 2376
There are two issues here:
I think you are not using applicatives to the extent haskell allows. functionMatching
returns the delimiting characters, only to have them discarded later. To discard Parser matches, use *>
and <*
:
functionMatching''' :: Parser String
functionMatching''' = spot (`elem` "\\") *> getWord <* spot (`elem` ".")
functionCons''' :: String -> (String, String)
functionCons''' = fromJust . getParser functionMatching'''
How <*>
, *>
and <*
work is easily memorized, they only return what >
and <
point to:
f <*> x: f x
x *> y: y
x <* y: x
There is a way more idiomatic way to use parsers. Don't extract the unparsed String
of Maybe (a,String)
, parse it instead! That is one more call to getWord
.
functionMatching' :: Parser (String, String)
functionMatching' = (,) <$> (spot (=='\\') *> getWord <* spot (=='.')) <*> getWord
functionCons' :: String -> (String, String)
functionCons' = fromJust . evalParser functionMatching'
-- evalParser discards the unparsed String
evalParser :: Parser a -> String -> Maybe a
evalParser p s = fst <$> getParser p s
evalParser
is a useful function analogous to runState, evalState, execState you will want to export.
The idea of parsers is to not leave the parser prematurely. The code above will encounter an error (fromJust
does this) if there is no match. haskell uses Maybe
s for that, and a Maybe
already is built into the parser. I do not know what you want to do with that (String,String)
, but you probably want to pass it to a function f :: String -> String -> b
. Then a
functionMatching'' :: Parser b
functionMatching'' = f <$> (spot (=='\\') *> getWord <* spot (=='.')) <*> getWord
will handle mismatches better.
Upvotes: 2
Reputation: 5406
It will be something like:
functionCons (P p) = P $ fmap (\((_,x,_), s) -> (x,s)) . p
But I suggest that you declare Parser a
a functor:
newtype Parser a = P { getParser :: String -> Maybe (a, String) }
deriving (Functor)
functionCons :: Parser (a,b,c) -> Parser b
functionCons = fmap (\(_,x,_) -> x)
This of course isn't quite Parser (a,b,c) -> (b, String)
, but you need to see if you really need to unJust
the result. For example:
functionCons :: Parser (a,b,c) -> String -> (b,String)
functionCons = unJust . fmap (\(_,x,_) -> x) where
unJust (P p) s = case p s of
Just x -> x
-- if Nothing happens, it will throw a unmatched pattern
As has been correctly noted in the comments, the type is not the same as requested. It is not possible to get the exact signature Parser (a,b,c) -> (b, String)
without functionCons
providing the String
to be parsed.
Upvotes: 4