1

In Haskell, I can write:

token: Parser a -> Parser a
token p = do space
             v <- p
             space  
             return v

In F#, I have come this far:

let token = compose {
        let! _ = space
        let! v = parser
        let! _ = space
        return v
    }

In other words, I have to introduce this unused let! _ = binding to discard the parse value of "space" parser (monad), that I don't need.

How to avoid these useless bindings in F#? I have tried using do!, but I get an error (because my >>= function does not take type unit but 'a):

let (>>=) (p: Parser<'a>) (f: 'a -> Parser<'b>) : Parser<'b> 

Here is my builder definition:

type ParserComposer() = 
  member x.Bind(p, f) = p >>= f
  member x.Return(y) = ret y
  member x.Zero() = failure

Do I need to define >> function? Add Combine() to builder? Any ideas how to do this right? Code example?

Paul Kapustin
  • 3,297
  • 5
  • 35
  • 45
  • You can define `>>` but with a different name as it's already a standard operator. Example: https://github.com/fsharp/fsharpx/blob/9ad7ff3024b1cc90fd252520272421920c1f4017/src/FSharpx.Core/ComputationExpressions/Monad.fs#L779 – Mauricio Scheffer Nov 20 '13 at 22:01
  • Ok, and when I have defined >> under a different name, what do I do? – Paul Kapustin Nov 20 '13 at 23:35
  • You use it just as you would in Haskell. Or use `let! _ = ...` – Mauricio Scheffer Nov 21 '13 at 00:46
  • But this is exactly what I am trying to avoid, this unnecessary binding. Is there a keyword that will use this >> or Combine, like !let is using >>= ? It seems like there is some syntax " ; " for Combine, but I cannot get it to work. – Paul Kapustin Nov 21 '13 at 00:54
  • Either define `>>.`, use `let! _ = ...` , or `map ignore` it. Those are your options. – Mauricio Scheffer Nov 21 '13 at 00:58
  • You'd have similar issues in Haskell, with the compiler giving you *Warning: A do-notation statement discarded a result of type Foo*. The only solution I can think of is to use parser combinators in Applicative style. – Nikos Baxevanis Jan 18 '17 at 05:49

1 Answers1

5

Assuming the return type of space is Parser<unit> (which would make sense if it does not represent a parser that returns some result) you can write:

let token = compose {
    do! space
    let! v = parser
    do! space
    return v
}

This is just a syntactic sugar for what you wrote - so do! e is translated as let! _ = e which is, in turn, translated to parser.Bind(e, fun _ -> ...). I have a sample for Additive parsers on Try Joinads which also defines Combine and a few more (possibly) useful things, but the do! keyword only needs Bind.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 2
    Ok, in this case space is a Parser, so I can use "do!". But what if it is a parser of something else? In many cases it does return something useful, but I just don't need it. In Haskell I simply don't bind it in this case. What is the best way to handle it in F#? Is Combine something I should use then? – Paul Kapustin Nov 20 '13 at 23:34
  • @badbadboy: Even in that case you can use `do!` as the `Bind` documentation in Computation expression says `Called for let! and do! in computation expressions` – Ankur Nov 21 '13 at 04:49
  • Yes, but then I get a compilation error because do! calls Bind supplying it with (), while it expects different type ('a). @tomas-petricek, any suggestions? – Paul Kapustin Nov 22 '13 at 03:14