yonutix
yonutix

Reputation: 2439

Haskell type unfold then fold to another type

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

Answers (2)

Franky
Franky

Reputation: 2376

There are two issues here:

Applicatives

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

Parsers

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 Maybes 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

Sassa NF
Sassa NF

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

Related Questions