views:

198

answers:

2

So, by a hilarious series of events, I downloaded the FParsec source and tried to build it. Unfortunately, it's not compatible with the new 1.9.9.9. I fixed the easy problems, but there are a couple of discriminated unions that still don't work.

Specifically, Don Syme's post explains that discriminated unions containing items of type obj or -> don't automatically get equality or comparison constraints, since objects don't support comparison and functions don't support equality either. (It's not clear whether the automatically generated equality/comparison was buggy before, but the code won't even compile now that they're no longer generated.)

Here are some examples of the problematic DUs:

type PrecedenceParserOp<'a,'u'> =
     | PrefixOp of string * Parser<unit,'u> * int * bool * ('a -> 'a)
     | others ...

type ErrorMessage =
     | ...
     | OtherError of obj
     | ...

Here are the offending uses:

member t.RemoveOperator (op: PrecedenceParserOp<'a, 'u>) =
    // some code ...
    if top.OriginalOp <> op then false // requires equality constraint
    // etc etc ...

or, for the comparison constraint

let rec printMessages (pos: Pos) (msgs: ErrorMessage list) ind =
    // other code ...
    for msg in Set.ofList msgs do // iterate over ordered unique messages
        // etc etc ...

As far I can tell, Don's solution of tagging each instance with a unique int is the Right Way to implement a custom equality/comparison constraint (or a maybe a unique int tuple so that individual branches of the DU can be ordered). But this is inconvenient for the user of the DU. Now, construction of the DU requires calling a function to get the next stamp.

Is there some way to hide the tag-getting and present the same constructors to users of the library? That is, to change the implementation without changing the interface? This is especially important because it appears (from what I understand of the code) that PrecedenceParserOp is a public type.

+1  A: 

One thing you could do is add [<CustomEquality>] and [<CustomComparison>] attributes and define your own .Equals override and IComparable implementation. Of course, this would require you to handle the obj and _ -> _ components yourself in an appropriate way, which may or may not be possible. If you can control what's being passed into the OtherError constructor, you ought to be able to make this work for the ErrorMessage type by downcasting the obj to a type which is itself structurally comparable. However, the PrecendenceParserOp case is a bit trickier - you might be able to get by with using reference equality on the function components as long as you don't need comparison as well.

kvb
Do functions support reference equality? I can't tell that they do, but that *would* solve the problem. As for `OtherError`, good idea, I'll check the uses of `OtherError` to see if I can downcast.
Nathan Sanders
Bad news: One of [Brian's posts](http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!1621.entry) links to the [precise part of the spec](http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html#_Toc245030880) that says that functions do not support reference equality. Well, it's "under-defined", meaning I guess that the method is there but returns nonsense.
Nathan Sanders
@Nathan - it's not that it returns nonsense so much as that the compiler's current behavior should not be assumed to be forward compatible. Although it would be a bit of a hack to rely on it, it's pretty easy to check that at the moment functions stored in a DU can be successfully tested for identity using `obj.ReferenceEquals`. I think that the rule is likely in place to emphasize that there are no guarantees as to how something like `(fun x -> x + 1) = (fun x -> x + 1)` will be evaluated. Having said all that, if you can find a cleaner solution by all means use it.
kvb
+2  A: 

What source did you download for FParsec? I grabbed the latest from the FParsec BitBucket repository, and I didn't have to make any changes at all to the FParsec source to get it to compile in VS 2010 RC.

Edit: I take that back. I did get build errors from the InterpLexYacc and InterpFParsec sample projects, but the core FParsec and FParsecCS projects build just fine.

Joel Mueller
I downloaded the latest official release from 2009-02-26, so I'll give the latest source a try--I forgot about the 'outdated' warning on the page by the time I ran into problems.
Nathan Sanders
Those build errors in the sample projects are just from missing a reference to FSharp.PowerPack.Compatibility, too. It's easy to fix.
Nathan Sanders