简体   繁体   中英

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!

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.

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: fx
  • 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 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM