F# - string | int inference on union - f#

I'm pretty sure this isn't possible but thought I'd double check. I guess I'm trying to mimic typescript's union types.
I have a type
type StringOrInt =
| String of string
| Int of int
And then a function
let returnSelf (x: StringOrInt) = x
At the moment the function has to be called like
returnSelf (String "hello")
Is it possible to do
returnSelf "hello"
and infer that it is a valid StringOrInt?

At the moment it's not supported out of the box, but fairly simple generic casting method can be implemented.
Given an example function:
let call v = (* v has inferred type StringOrInt *)
match v with
| String s -> printfn "called a string %s" s
| Int i -> printfn "called an int %i" i
We could eventually call it like so:
(* % is a unary prefix operator *)
call %"abc"
call %1
We need to provide some way to tell how to convert ordinary string/int into StringOrInt type. This can be used by exemplar convention call:
type StringOrInt =
| String of string
| Int of int
static member inline From(i: int) = Int i
static member inline From(s: string) = String s
Here our discriminated union provides two static methods which are responsible for casting. Since they correspond to the same naming convention, we can use them together with F# statically resolved generic type parameters - these are generics that are resolved at compile time unlike the .NET generics which exists also at runtime - to create a casting function (or operator):
(* casting operator *)
let inline (~%) (x: ^A) : ^B =
(^B : (static member From: ^A -> ^B) x)
This way you can use % prefix operator over any type that implements a casting mechanism in form of static From method.

Related

How to do explicit overloaded conversion in F# like in C#? [duplicate]

This question already has an answer here:
How to use overloaded explicit conversion operators?
(1 answer)
Closed 4 years ago.
Let's say that I have a class in C# with overloaded implicit and explicit operators:
public static implicit operator CSClass(int a) => ...;
public static explicit operator int(CSClass a) => ...;
I compile this project as class library.
In F# now I can add my operator for implicit conversions and use it:
#r #"C:\path\to.dll"
open Some.Namespace.ToMyClass
let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)
let a : CSClass = !> 5
But how can I do an explicit overloaded conversion in F#? (CSClass to int)
It is my understanding that F# does not usually do explicit conversions. Instead, you would just use a function. For example, if you have a char and want to convert that explicitly into an int, in C# you write:
char theChar = 'A';
int convertedChar = (int)theChar;
In F#, the int operator (function) is used for the same purpose:
let theChar = 'A'
let convertedChar = int theChar;
Therefore the idiomatic way to do the conversion would be something like this:
module Some.Namespace.MyClass
let toInt (x : MyClass) = [...]
You would use it like so:
let convertedMyClass = MyClass.toInt myClass
It can also be piped:
funcReturningMyClass x y
|> MyClass.toInt
|> printfn "%d"

In F#, is it possible to have a tryParse function that infers the target type

Presently we do this...
let parseDate defaultVal text =
match DateTime.TryParse s with
| true, d -> d
| _ -> defaultVal
Is it possible to do this...
let d : DateTime = tryParse DateTime.MinValue "2015.05.01"
Yes. Welcome to the world of member constraints, ref, and byref values.
let inline tryParseWithDefault
defaultVal
text
: ^a when ^a : (static member TryParse : string * ^a byref -> bool)
=
let r = ref defaultVal
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r.contents))
then !r
else defaultVal
defaultVal and text are formal parameters and will be inferred. Here, text is already constrained to be string because it is used as the first parameter in a call to the static method, SomeType.TryParse, as explain later. defaultVal is constrained to be whatever ^a is since it is a possible result value per the if..then..else expression.
^a is a statically resolved type parameter (vs a generic type parameter of the form 'a). In particular, ^a will be resolved at compile time to a specific type. Consequently, the function hosting it must be marked inline, which means that each invocation of the function will become an in-place replacement at that invocation with this body of the function, wherein each static type parameter will become a specific type; in this case, whatever type defaultVal is. There is no base type or interface type constraints restricting the possible type of defaultVal. However, you can provide static and instance member constraints such as is done here. Specifically, the result value (and therefore the type of defaultVal) must apparently have a static member called, TryParse, that accepts a string and a reference to a mutable instance of that type, and returns a boolean value. This constraint is made explicit by the stated return type on the line beginning with : ^a when .... The fact that defaultVal itself is a possible result constrains it to be of the same type as ^a. (The constraint is also implicit elsewhere throughout the function which is unnecessary as explained below).
: ^a when ^a : (static .... describes the result type, ^a, as having a static member called TryParse of type string * ^a byref -> bool. That is to say, the result type must have a static member called TryParse that accepts a string, a reference to an instance of itself (and therefore a mutable instance), and will return a boolean value. This description is how F# matches the .Net definition of TryParse on DateTime, Int32, TimeSpan, etc. types. Note, byref is F# equivalent of C#'s out or ref parameter modifier.
let r = ref defaultVal creates a reference type and copies the provided value, defaultVal, into it. ref is one of the ways F# creates mutable types. The other is with the mutable keyword. The difference is that mutable stores its value on the stack while ref stores its in main memory/heap and holds an address (on the stack) to it. The latest version of F# will seek to automatically upgrade mutable designations to ref depending on the context, allowing you to code only in terms of mutable.
if (^a : (static... is an if statement over the invocation results of the TryParse method on the statically inferred type, ^a. This TryParse is passed, (text, &r.contents), per its (string * ^a byref) signature. Here, &r.contents provides the reference to the mutable content of r (simulating C#'s out or ref parameter) per the expectation of TryParse. Note, we are off the reservation here and certain F# niceties for inter-operating with the .Net framework do not extend out this far; in particular, the automatic rolling up of space separated F# parameters into .net framework function parameters as a tuple is not available. Hence, the parameters are provide to the function as a tuple, (text, &r.contents).
!r is how you read a reference value. r.Value would also work.
The TryParse methods provided by .Net seems to always set a value for the out parameter. Consequently, a default value is not strictly required. However, you need a result value holder, r, and it must have an initial value, even null. I didn't like null. Another option, of course, is to impose another constraint on ^a that demands a default value property of some sort.
The following subsequent solution removes the need for a default parameter by using the Unchecked.defaultof< ^a > to derive a suitable placeholder value from the "inferred result" type (yes, it feels like magic). It also uses the Option type to characterize success and failure obtaining a result value. The result type is therefore, ^a option.
tryParse
text
: ^a option when ^a : (static member TryParse : string * ^a byref -> bool)
=
let r = ref Unchecked.defaultof< ^a >
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r.contents))
then Some (!r)
else None
And, per #kvb suggestions, the following brevity is possible. In this case, type inference is employed to both stipulate the type constraint on ^a as a consequence of it's invocation in the if (^a : ...)) expression and to also establish the type of the mutable buffer r for TryParse's out parameter. I have since come to learn this is how FsControl does some of it's magic
let inline tryParse text : ^a option =
let mutable r = Unchecked.defaultof<_>
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r))
then Some r
else None
let inline tryParseWithDefault defaultVal text : ^a =
match tryParse text with
| Some d -> d
| _ -> defaultVal
Wherein the usage would be...
> let x:DateTime option = tryParse "December 31, 2014";;
val x : DateTime option = Some 2014-12-31 12:00:00 a.m.
> let x:bool option = tryParse "false";;
val x : bool option = Some false
> let x:decimal option = tryParse "84.32";;
val x : decimal option = Some 84.32M
For the case of using type constraints on instance member such as type constraining for Fsharp's dynamic member lookup operator, ?, such that the type of its target must contain a FindName:string -> obj member for use in resolving member lookup requests, the syntax is as follows:
let inline (?) (targetObj:^a) (property:string) : 'b =
(^a : (member FindName:string -> obj) (targetObj, property)) :?> 'b
Note:
The signature of instance methods must explicitly specify their self object, which is normally a hidden first parameter of object methods
This solution also promotes the result to the type of 'b
A sample usage would be the following:
let button : Button = window?myButton
let report : ReportViewer = window?reportViewer1

How to convert between F# and C# tuples?

I am writing an F# interop class to be used from C#. I thought that F# had an implicit conversion from .NET Tuple<> type (similar to IEnumerable treated as seq), so I wrote the following code:
type MyClass() =
member this.MyMethod (t: Tuple<int, int>) =
let meaningOfLife (t : int * int) = 42
meaningOfLife t
This code fails to compile with the following error:
error FS0001: This expression was expected to have type int * int but here has type Tuple
Then how to convert tuples between C# and F# (and back)?
If you're targeting .NET 4.0 and above, you don't need to specify the object Tuple. F# tuples get automatically compiled to Tuple. You can use:
type MyClass() =
member this.MyMethod (t: int * int) =
let meaningOfLife (t : int * int) = 42
meaningOfLife t
And it should work fine from C#.

Why am I not allowed to use reference cell as argument for byref parameter in let functions?

This doesn't work:
let increment(i: int byref) = i <- i + 1
let xxx = ref 0
increment(xxx) // this expression was expected to have type
// byref<int> but here has type int ref
But this works:
let incrementParam(a: int byref) = a <- a + 1
let mutable b = 30
incrementParam(&b)
as well as this:
type Incrementor =
static member Increment(i : int byref) =
i <- i + 1
let fff = ref 10
Incrementor.Increment(fff)
Because the spec says so. See Type Directed Conversions at Member Invocations (emphasis mine), especially the following:
Note: These type-directed conversions are primarily for interoperability with existing member-based .NET libraries and do not apply at invocations of functions defined in modules or bound locally in expressions.
To add some details to the reference that Daniel pointed out, the problem is that the type 'T ref is not the same as the type 'T byref and so the compiler needs to insert some conversion (to take the address).
I think this is only supported for members, because this is the main scenario (calling COM interop methods etc.) and because implicit conversions generally compilcate type inference. The type-directed conversions are an easier case where the compiler already knows the required type ('T byref). I suppose that, if this was allowed on functions, the compiler might infer that the type of argument should actually be 'T ref.
If you want to get the first sample to work, you have to explicitly construct 'T byref by taking the address of the contents field:
let increment(i: int byref) = i <- i + 1
let xxx = ref 0
increment(&xxx.contents)

How to define explicit operator in F#?

How do you implement the equivalent of C#'s explicit operator in F#? Is it supported?
Just implement an op_Explicit static member like
type SomeType() =
static member op_Explicit(source: SomeType) : int =
1
and then you can use a corresponding F# explicit conversion operator like
SomeType() |> int
you can see a bit into how this works by noting the static member constraint on the type signature of int
^a -> int when ^a : (static member op_Explicit : ^a -> int)

Resources