Question

I want to simplify expression if(x == 1 || x == 2).
I wish I could write if(x == 1 or 2) but there is no syntax for that.
Other possibility is to use Contains or Any method like: if([1,2].Contains(x)) but this involves unnecessary call.

Can I create some operator which allows me to do this ?

In Nemerle language I can write macro:

macro @|||(left, right)
  match (left)
    | <[ $x == $y ]> => <[ $x == $y || $x == $right ]>
    | _ => Message.Error("Error"); <[ ]>

And then usage:

if (x == 1 ||| 2) { .. }

Can I create operator in such way in F# ?

Was it helpful?

Solution

You could use |> to accomplish this, borrowing from a common use of one of the haskell monoid instances.

let appendResults f g = (fun x -> f(x) || g(x))
let f x = x=1
let g x = x=2
let inline (>>||) x y = (appendResults f g) x y
let x = 1
if(x |> (1 >>|| 2)) then printfn "true" else printfn "false"

For arbitrary numbers of arguments, just mimic the relevant mconcat method from haskell for the same effect, perhaps like this:

let rec concatResults = function
| [] -> (fun x -> false)
| (x:xs) -> appendResults x (concatResults xs)

Honestly though, you may as well just use Contains. If there is any special overhead doing that I doubt it really matters.

OTHER TIPS

I agree with Brian's comment that constructing a macro in order to save three characters is probably not a good idea. This will only make the program harder to read (for those who do not know your custom macros or changed meaning of operators).

Moreover, it is quite likely that you could write the same logic in a more concise way using standard F# constructs like pattern matching. For example:

match x with
| 1 | 2 -> printfn "yes"
| _     -> printfn "no"

The idiomatic solution will depend on the concrete case, which is hard to judge from the example you gave.

I agree with Brian and Tomas; it makes a little practical sense to invent your own macros that might be used just a few times.
However, I do find it very interesting from the point of studying the internals of functional languages.
Consider this:

// Generic
let inline mOp1<'a> op sample x = op sample x, sample
let inline mOp2<'a> op1 op2 (b, sample) x = op1 b (op2 sample x), sample

// Implementation for (=) and (||)
let (==) = mOp1 (=)
let (|=) = mOp2 (||) (=)

// Use
let ret1 = x == 1 |= 2 |> fst

You may find more details, other operators, and performance measurement here: https://stackoverflow.com/a/11552429/974789

This is slightly hackish, but it does work

let x = 1
let inline (|||) a b = [a;b]
let inline (==) a b = b |> List.exists (fun t -> t=a)

if x == (1 ||| 2) then printfn "true" else printfn "false"

It requires a custom operator for both or and equals. It would not be hard to modify this to support arbitrary or chains

Of course if you only need 2 numbers you can do

let x = 1
let inline (|||) a b = (a,b)
let inline (==) a (c,d) = a=c ||a=d

if x == (1 ||| 2) then printfn "true" else printfn "false"

This works by converting a tupple to an array, so do not expect the best performance.

let inline (==) a b = 
    Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(b) 
        |> Array.exists((=) a)

Example:

3 == (1,2)      // false
3 == (1,2,3)    // true
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top