2

I'm doing a fun project in F#, which is a DSL for Camel.Net.

At some point, I want to check conditions. But the conditions entered by the programmer should evaluate to an object tree. So I want the experssion "a = b" evaluate to "SomeType.Equals(a,b)"

Is that even possible in F#?

I have this:

type Macro = 
|   Header of string
|   XPath  of string
|   Const  of string
|   Func   of (Message -> string)
with
    static member (=) (l:Macro, r:Macro) = Equals(l,r)
    static member (=) (l:Macro, r:string) = Equals(l,Const(r))
and Comparison =
|   Equals of Macro * Macro

Now everything in "Macro" will work as "Macro.Func" - with "Func"; a function is executed with "Message" as input param and will output the string. So the Equals(a,b) will evaluate to a string comparison during runtime.

But this code has a problem. Operator (=) does compile (it has a warning), but it can't be used as I would like.

This does not compile in the fsi:

let c1 = Header("property") = "somevalue"

I did read another question about this topic, and a bit more.

It does not answer my question.

[<NoEquality; NoComparison>] - completely shuts off the (=) operator.

[<CustomEquality; CustomComparison>] - wants you to implement an (=) operator which returns bool.

Is it even possible in F# what I want? And assuming that I can find a way, does match x with still work?

Community
  • 1
  • 1
Frank Joppe
  • 151
  • 7
  • Why cannot you heed the warning `The name '(=)' should not be used as a member name`? As you provide alternative constructor semantics for a union type, a `static member Create` would be entirely appropriate. As would any other operator - except this one, which is expressly for equality. – kaefer Apr 15 '16 at 20:14
  • I can respect/avoid the warning, however I prefer to use "(=)", as this is for a DSL. Using (=) delivers a much better user-experience than anything else - in this case. By the way, my work around before this question was using the (==) operator, but I prefer it to be more Fsharpish. – Frank Joppe Apr 16 '16 at 18:14

2 Answers2

2

Sure, I did this reimplement to the operator in terms of System.IEquatable<T> for performance reasons:

#nowarn "86" // F# doesn't like it when you do this

[<AutoOpen>]
module FastEquals =
    let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y    
    let inline (=) x y = eq x y
    let inline (<>) x y = not (eq x y)

Just an example, you'll need to adapt for your own purposes.

Asik
  • 21,506
  • 6
  • 72
  • 131
  • Thanks. That does work, however only partially. As you can see in the code sample, I've overloaded the (=) operator twice, with different types for parameter "r". I don't know how to get the inline function to accept both types, and module funcs cannot be overloaded.. – Frank Joppe Apr 15 '16 at 18:59
  • Hm, what about calling your overloaded static equality members from the "eq" function? I haven't managed to do overloading, if you solve it I would be curious. See http://stackoverflow.com/questions/501069/functions-with-generic-parameter-types for more ideas. – Asik Apr 15 '16 at 19:38
  • Found it. See below. – Frank Joppe Apr 15 '16 at 20:40
2

Thanks to Asik's answer above, in combination with a reread of this post:

This works in the fsi:

type Message = class end

type Macro = 
|   Header of string
|   XPath  of string
|   Const  of string
|   Func   of (Message -> string)

type Comparison =
|   Equals of Macro * Macro

type Operators = Operation with
    static member CompareEquals (Operation, l:Macro, r:Macro) = Equals(l,r)
    static member CompareEquals (Operation, l:Macro, r:string) = Equals(l,Const(r))

#nowarn "0086" "0064"
let inline (=) (l:'N) (r:'M) = ((^T or ^N or ^M) : (static member CompareEquals : ^T * ^N * ^M -> _) (Operation, l, r))

let c1 = Header("property1") = Header("property2")
let c2 = Header("property") = "somevalue"

Note that it does not work when the static "CompareEquals" methods are located in the "Macro" type.

If you look at the signature of op_Equals:

val inline ( = ) :
  l: ^N -> r: ^M -> 'a
  when (Operators or  ^N or  ^M) : (static member CompareEquals : Operators * ^N * ^M -> 'a)

That is a really weird syntax. I don't understand the part after "when". It works, that counts.

Community
  • 1
  • 1
Frank Joppe
  • 151
  • 7