F# pattern match using type constraints - f#

Is it possible to do an F# type test pattern with a member constraint?
Such as:
let f x =
match x with
| :? (^T when ^T : (static member IsInfinity : ^T -> bool)) as z -> Some z
| _ -> None
or
let g x =
match x with
| (z : ^T when ^T : (static member IsInfinity : ^T -> bool)) -> Some z
| _ -> None
Neither which work.

You cannot do this, as Petr said, statically resolved type parameters are resolved at compile time. They're actually a feature of the F# compiler rather than being a .NET feature, hence why this kind of information isn't available at runtime.
If you wish to check this at runtime, you could use reflection.
let hasIsInfinity (x : 'a) =
typeof<'a>.GetMethod("IsInfinity", [|typeof<'a>|])
|> Option.ofObj
|> Option.exists (fun mi -> mi.ReturnType = typeof<bool> && mi.IsStatic)
This will check for a static method called IsInfinity with type sig: 'a -> bool

Related

How can unify the signature of this member method and the inline function

given this code
type Baz = Baz of int with
static member bar f (Baz(b)) = f b
let inline foo< ^T, ^U when ^T : (static member bar : (^U -> ^T) -> ^T -> ^T)>
(f:(^U -> ^T)) (t:^T) : ^T =
(^T : (static member bar : (^U -> ^T) -> ^T -> ^T) f, t )
let x = foo (fun x -> (Baz 0)) (Baz 1)
I get this error
error FS0043: Method or object constructor 'bar' not found
I assume that signature of my static member can not really be unified to (^U -> ^T) -> ^T -> ^T
How can I solve this?
Looking at the previous question (i.e. switching back to the member function) and your comments, perhaps this would work:
type Baz = Baz of int with
member this.bar (f: 'a -> 'b): 'b = match this with
| Baz i -> f i
let inline foo (f: ^U -> ^T) (t:^T) =
let foo' = (^T : (member bar : (^U -> ^T) -> ^T) (t, f))
foo'
let x = foo (fun x -> (Baz 0)) (Baz 1)
// This returns Baz 0
printfn "%A" x

How does non idiomatic global operator overloading work?

I want to understand the code from this answer
type Mult = Mult with
static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline ($) (Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline (*) v1 v2 = (Mult $ v1) v2
F# can resolve overloaded members. (Because it doesn't support currying of members). So, I supposed, it should work for methods as well
But it doesn't:
type Mult = Mult with
static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline Do (Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline (<.>) v1 v2 = (Mult.Do (Mult,v1)) v2
A unique overload for method 'Do' could not be determined based on
type information prior to this program point. A type annotation may be
needed. Candidates: static member Mult.Do : Mult:Mult * v1: ^a -> ( ^a
-> ^a) when ^a : (static member ( * ) : ^a * ^a -> ^a), static member Mult.Do : Mult:Mult * v1:'a list -> ('b list -> ('a * 'b)
list)
The syntax in which operator $ is defined is confusing. It accepts upper case identifier as first argument of operator and Visual Studio doesn't complain about it
Mult is inferred to be of type mult, but surprisingly this doesn't work:
type Mult = Mult with
static member inline (!!) (mlt:Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline (!!) (mlt:Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline (<!>) v1 v2 = (Mult !! v1) v2
error FS0003: This value is not a function and cannot be applied
Your second example doesn't work because F# doesn't automatically infer static member constraints with methods as it does with operators.
So yes, it's possible but you will have to write the constraints by hand, the compiler will not infer them for you:
type Mult = Mult with
static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline Do (Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline impl m v1 v2 = ((^T or ^a) : (static member Do:^T* ^a->(^b-> ^c)) (m,v1)) v2
let inline (<.>) a b = impl Mult a b
The upper-case identifier you mentioned is matching a Discriminated Union of only one single case, so it will always succeed and the name of the case is the same name of the type. All this is to shorten just a bit the amount of code since that DU is a dummy type. If it's confusing here's an example with a normal class:
type Mult() = class end with
static member inline ($) (_:Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline ($) (_:Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline (*) v1 v2 = (Mult() $ v1) v2
Your third example doesn't work because (!!) is an unary operator, not binary like ($)
More information about this old technique in this old blog.

F# missing type constraint

In the following code, note the type constraint for get_Zero:
type Wrapper<'t> = { Data : 't[] }
let compute<'t
when 't : (static member get_Zero : unit -> 't)
and 't : (static member (~-) : 't -> 't)
and 't : (static member (+) : 't * 't -> 't)>
(wrapper : Wrapper<'t>) =
wrapper.Data
|> Seq.mapi (fun i value -> (i, value))
|> Seq.sumBy (fun (i, value) ->
if i % 2 = 0 then value
else -value)
Even though I already have an explicit type constraint, I'm still getting the following compiler error on the call to Seq.sumBy:
A type parameter is missing a constraint 'when ^t : (static member
get_Zero : -> ^t)'
Anyone know what's going on here? Thanks.
Trying to make downstream static member constraints explicit can be an exercise in frustration, and, fortunately, it's seldom necessary. Just mark the function inline and let them be inferred.
let inline compute (wrapper : Wrapper<_>) =
wrapper.Data
|> Seq.mapi (fun i value -> (i, value))
|> Seq.sumBy (fun (i, value) ->
if i % 2 = 0 then value
else -value)
The correct signature is:
let inline compute<'t
when 't : (static member Zero : 't)
and 't : (static member (~-) : 't -> 't)
and 't : (static member (+) : 't * 't -> 't)>
(You'll notice the signature in the error message isn't even valid syntax: when ^t : (static member get_Zero : -> ^t). This is part of what I mean by frustrating.)

Null Coalescing Operator in F#?

When interacting with C# libraries, I find myself wanting C#'s null coalescing operator both for Nullable structs and reference types.
Is it possible to approximate this in F# with a single overloaded operator that inlines the appropriate if case?
Yes, using some minor hackery found in this SO answer "Overload operator in F#".
At compiled time the correct overload for an usage of either ('a Nullable, 'a) ->'a or ('a when 'a:null, 'a) -> 'a for a single operator can be inlined. Even ('a option, 'a) -> 'a can be thrown in for more flexibility.
To provide closer behavior to c# operator, I've made default parameter 'a Lazy so that it's source isn't called unless the original value is null.
Example:
let value = Something.PossiblyNullReturned()
|?? lazy new SameType()
Implementation:
NullCoalesce.fs [Gist]:
//https://gist.github.com/jbtule/8477768#file-nullcoalesce-fs
type NullCoalesce =
static member Coalesce(a: 'a option, b: 'a Lazy) =
match a with
| Some a -> a
| _ -> b.Value
static member Coalesce(a: 'a Nullable, b: 'a Lazy) =
if a.HasValue then a.Value
else b.Value
static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) =
match a with
| null -> b.Value
| _ -> a
let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b =
// calling the statically inferred member
((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))
let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b
Alternatively I made a library that utilizes this technique as well as computation expression for dealing with Null/Option/Nullables, called FSharp.Interop.NullOptAble
It uses the operator |?-> instead.
modified the accepted answer by jbtule to support DBNull:
//https://gist.github.com/tallpeak/7b8beacc8c273acecb5e
open System
let inline isNull value = obj.ReferenceEquals(value, null)
let inline isDBNull value = obj.ReferenceEquals(value, DBNull.Value)
type NullCoalesce =
static member Coalesce(a: 'a option, b: 'a Lazy) = match a with Some a -> a | _ -> b.Value
static member Coalesce(a: 'a Nullable, b: 'a Lazy) = if a.HasValue then a.Value else b.Value
//static member Coalesce(a: 'a when 'a:null, b: 'a Lazy) = match a with null -> b.Value | _ -> a // overridden, so removed
static member Coalesce(a: DBNull, b: 'b Lazy) = b.Value //added to support DBNull
// The following line overrides the definition for "'a when 'a:null"
static member Coalesce(a: obj, b: 'b Lazy) = if isDBNull a || isNull a then b.Value else a // support box DBNull
let inline nullCoalesceHelper< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Coalesce : ^a * ^b -> ^c)> a b =
((^t or ^a) : (static member Coalesce : ^a * ^b -> ^c) (a, b))
Usage:
let inline (|??) a b = nullCoalesceHelper<NullCoalesce, _, _, _> a b
let o = box null
let x = o |?? lazy (box 2)
let y = (DBNull.Value) |?? lazy (box 3)
let z = box (DBNull.Value) |?? lazy (box 4)
let a = None |?? lazy (box 5)
let b = box None |?? lazy (box 6)
let c = (Nullable<int>() ) |?? lazy (7)
let d = box (Nullable<int>() ) |?? lazy (box 8)
I usually use defaultArg for this purpose as it is built-in to the language.

A shorter type constraint for F#

I'm practicing with F# and I've though of implementing a type-constrained compare function for F#. In C#, it's implemented by this
// in C#
static int cmp<T>(T x, T y) where T: System.IComparable<T>
{
return x.CompareTo(y);
}
However, in F#, the best way I've come up is this.
(* in F# *)
let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a when 'a :> 'a System.IComparable)
= x.CompareTo(y)
I tried the one below but it didn't work
let cmp (x: 'a) (y: 'a) when 'a :> 'a System.IComparable
= x.CompareTo(y)
Is my working F# sample the shortest way or is there another?
Another (cleaner, IMO) way to implement this is by adding an explicit generic type parameter to the function, like this:
let cmp<'T when 'T :> System.IComparable<'T>> (x : 'T) (y : 'T) =
x.CompareTo y
Ok, found it. I was browsing through the examples in MSDN's F# type constraint and at the thrid from the last example, I found this
let inline add(value1 : ^T when ^T : (static member (+) : ^T * ^T -> ^T), value2: ^T) =
value1 + value2
I notice that constraint for ^T in value1 is used in value2, so I change my cmp function to this
let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a) = x.CompareTo(y)
I ran through the fsi and got the same type signature
> let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a) = x.CompareTo(y);;
val cmp : 'a -> 'a -> int when 'a :> System.IComparable<'a>
> let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a when 'a :> 'a System.IComparable) = x.CompareTo(y);;
val cmp : 'a -> 'a -> int when 'a :> System.IComparable<'a>

Resources