Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concat parse results

Tags:

haskell

parsec

I can create a parser which can handle two or three numbers, separated by comma, like this:

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 the numbers are important, the rest can be discarded. Is there a way to concatenate the intermediate parse results (n1,n2,n3) to handle it outside of input? For example the parser-combinator of Scala 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 in order to pattern 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 to parse. Does parsec have built-in functions to allow a similar behavior? If not how to built such a behavior by myself?

like image 956
kiritsuku Avatar asked Mar 11 '12 19:03

kiritsuku


People also ask

What's the difference between concat and concatenate?

The CONCAT function combines the text from multiple ranges and/or strings, but it doesn't provide delimiter or IgnoreEmpty arguments. CONCAT replaces the CONCATENATE function. However, the CONCATENATE function will stay available for compatibility with earlier versions of Excel.

What is concat function in SQL?

CONCAT takes a variable number of string arguments and concatenates (or joins) them into a single string. It requires a minimum of two input values; otherwise, CONCAT will raise an error. CONCAT implicitly converts all arguments to string types before concatenation.

What does concat do in JavaScript?

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.


1 Answers

You can do it by using applicative functors. The pattern is generally:

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 could have type Parser Int. The functors a1, a2, a3 will be applied sequentially, and their results will be collected and mapped over the function f.

In your case, you'd want to do:

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

(,,) is the constructor of a 3-tuple, so it's a function taking 3 arguments and returning a 3-tuple. Passing 3 parsers with the <$>..<*>.. pattern lifts the application of the 3-tuple constructor into the functor being used here, which is Parser in this case, so the whole expression returns the result of the mapped function wrapped in the functor, being Parser (Int, Int, Maybe Int).

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

PS. You could define number using applicative functors, too (The monad interface is more "heavy-weight", and I personally try to avoid it):

number :: Parser Int
number = read <$> many1 digit <?> "number"
like image 81
dflemstr Avatar answered Oct 21 '22 06:10

dflemstr