Concat Analysis Results

I can create a parser that can handle two or three numbers separated by a comma, for example:

number :: Parser Int number = many1 digit >>= return . read <?> "number" numbers = do n1 <- number n2 <- char ':' >> number n3 <- optionMaybe $ char ':' >> number return ... -- return all of n1, n2, n3 

Only numbers are important, the rest can be discarded. Is there a way to combine the results of an intermediate analysis ( n1,n2,n3 ) to process it outside of input ? For example, a Scala combiner parser can do this:

 def numbers: Parser[Int ~ Int ~ Option[Int]] = // only the important numbers are returned number ~ (":" ~> number) ~ opt(":" ~> number) 

I want to do this to match the parser in different places. For example, in Scala, I can do something like this:

 val result = input.parseAs(numbers) { case n1 ~ n2 ~ None => // work with n1,n2 case n1 ~ n2 ~ Some(n3) => // work with n1,n2,n3 } 

where input is a string for parsing. Does the parsek have built-in functions that allow this behavior? If not, how do you build this behavior yourself?

+4
source share
1 answer

You can do this using applicative functors. Usually a template has the form:

 import Control.Applicative f <$> a1 <*> a2 <*> a3 

f is some function that takes 3 arguments in this case, and a1 , a2 and a3 are applicative functors over values ​​that can be passed as arguments to f , for example, if f :: Int -> Int -> Int -> Foo , a1, a2, a3 can be of type Parser Int . The functors a1, a2, a3 will be applied sequentially, and their results will be collected and displayed by the function f .

In your case, you want:

 numbers :: Parser (Int, Int, Maybe Int) numbers = (,,) <$> number <*> (char ':' *> number) <*> optionMaybe (char ':' *> number) 

(,,) is the constructor of a 3-tuple, so it takes 3 arguments and returns a 3-tuple. Passing 3 parsers with the template <$>..<*>.. removes the use of the 3-tuple constructor in the functor used here, which in this case is Parser , so the whole expression returns the result of the displayed function wrapped in a functor, Parser (Int, Int, Maybe Int) .

You can also use liftA3 f a1 a2 a3 instead of f <$> a1 <*> a2 <*> a3 ; two expressions are equivalent.

PS. You can also define number using applicative functors (the monad interface is more "heavy" and I personally try to avoid it):

 number :: Parser Int number = read <$> many1 digit <?> "number" 
+6
source

Source: https://habr.com/ru/post/1400875/


All Articles