MrD at KookerellaLtd
MrD at KookerellaLtd

Reputation: 2795

how to explicitly reference an operator

I'm trying to have a go with Microsoft.ML.Probabilistic, and the examples are in C#.

e.g.

static void Main(string[] args)
{
    var firstHeads = Variable.Bernoulli(0.5);
    var secondHeads = Variable.Bernoulli(0.5);
    var bothHeads = firstHeads & secondHeads;
}

now there's a bit of magic going on in there, the & operator is resolved by the C# compiler as

Variable<bool>.operator &(Variable<bool> a, Variable<bool> b)

(actually I even though I've been using C# for over 15 years, I don't actually know how to explicitly reference this operator, I just let the compiler do the hard work...but that's an aside)

I want to do this in F#, so I write

let main argv =
    let firstCoinHeads = Variable.Bernoulli 0.5
    let secondCoinHeads = Variable.Bernoulli 0.5
    let bothHeads = firstCoinHeads & secondCoinHeads
    0 

but this doesnt compile, because the F# compiler "this expression was expected to be a bool" for the subexpressions "firstCoinHeads" and "secondCoinHeads".

thats sort of irritating....so how do I explicitly make F# use the correct operator?

Upvotes: 2

Views: 84

Answers (2)

Abel
Abel

Reputation: 57159

The given answer by @Fyodor Soikin is not wrong, but the proposed solution is unnecessary. There seems to be a bit of confusion about the operators:

  • The "bitwise and" is indeed compiled as op_BitwiseAnd
  • In C#, bitwise and is &.
  • In F#, bitwise and is &&&. This is the one you want.
  • In F#, & is used in match expressions, meaning "and", and as the unary address-of operator. There is no default mapping for this operator. (if overridden, it becomes op_Amp).
  • Boolean "and" in F# is &&.

You are certainly allowed to redefine &&& in F#. To redefine the address-of operator not so much (you get a warning).

However, if a type overloads an operator, F# will simply select that operator if you use the proper matching syntactic operator. In this case, &&&. Yes, this is indeed confusing.

If I install Microsoft.ML.Probabilistic and Microsoft.ML.Probabilistic.Compilers, I can do the following (the nuget syntax is available with --langversion:preview):

> #r "nuget: Microsoft.ML.Probabilistic";;
> open Microsoft.ML.Probabilistic;;
[Loading C:\Users\Me\AppData\Local\Temp\nuget\10644\Project.fsproj.fsx]
namespace FSI_0138.Project

> #r "nuget: Microsoft.ML.Probabilistic.Compiler";;
> open Microsoft.ML.Probabilistic.Models;;
[Loading C:\Users\Me\AppData\Local\Temp\nuget\10644\Project.fsproj.fsx]
namespace FSI_0143.Project

>let firstCoinHeads = Variable.Bernoulli 0.5
let secondCoinHeads = Variable.Bernoulli 0.5;;
Binding session to 'C:\Users\Abel\.nuget\packages\microsoft.ml.probabilistic\0.3.1912.403\lib\netstandard2.0\Microsoft.ML.Probabilistic.dll'...
val firstCoinHeads : Variable<bool> = vbool0
val secondCoinHeads : Variable<bool> = vbool1

> let bothHeads = firstCoinHeads &&& secondCoinHeads;;
val bothHeads : Variable<bool> = vbool2

As you can see, no need to create "your own" special operator here, just use the actual "bitwise and" operator: &&&.

If you run into this again, there's a list of what operator is matched to what method: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/operator-overloading. Notably:

enter image description here

Upvotes: 1

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

In .NET (and in F#, to a greater extent) operators are compiled to methods named op_Xyz where Xyz describes the operator itself (for .NET overloadable operators) or the characters included in the operator (for the richer F# operators).

Specifically, the operator & is compiled to op_BitwiseAnd, and that's how you can access it:

let bothHeads = Variable.op_BitwiseAnd(firstCoinHeads, secondCoinHeads)

The original problem you're experiencing comes from the fact that in F#, operator & is "special" (for backward compatibility with Ocaml, one assumes). It's treated specially by the compiler, forcing the arguments to be bool and issuing a warning if you try to redefine it. This is a bit of an annoyance, yes, but here we are.

But you could define yourself a different operator as an extension for the Variable type:

type Variable<'t> with
    static member (&.)(a, b) = Variable<bool>.op_BitwiseAnd(a, b)

And then use it like this:

let bothHeads = firstCoinHeads &. secondCoinHeads

Upvotes: 4

Related Questions