Using F#, I had the following (simplified), which works fine:
type MyInt =
struct
val Value: int
new v = { Value = v }
end
static member inline name() = "my_int" // relevant line
let inline getName (tp: ^a): string = (^a: (static member name : unit -> string) ())
It seems to me that the statically resolved member signature from the explicit member constraint requires a function. I was wondering if it can also be used with a field instead. I tried a few things:
The following will compile, but won't work and will fail with the error
error FS0001: The type 'MyInt' does not support the operator 'get_name'
type MyInt =
//...
static member inline name = "my_int"
let inline getName (tp: ^a): string = (^a: (static member name : string) ())
Removing the () to prevent it from trying to call the gettor is a syntax error. If I change it to actually implement the gettor, it works (but that's essentially the same as the original code).
type MyInt =
// ...
static member inline name with get() = "my_int"
let inline getName (tp: ^a): string = (^a: (static member name : string) ())
Is there a way to get, using explicit member constraints or similar means, the compiler to find the static field? Or is this simply a limitation of the syntax of constraints?
Update:
Surprisingly, it does work with instance fields, or as in this case, struct fields: using (^a: (member Value: 'b) ()), which will call the member Value.
Related
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.
I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string
I'm adding a static builder method to a record type like this:
type ThingConfig = { url: string; token : string; } with
static member FromSettings (getSetting : (string -> string)) : ThingConfig =
{
url = getSetting "apiUrl";
token = getSetting "apiToken";
}
I can call it like this:
let config = ThingConfig.FromSettings mySettingsAccessor
Now the tricky part: I'd like to add a second overloaded builder for use from C# (ignore the duplicated implementation for now):
static member FromSettings (getSetting : System.Func<string,string>) : ThingConfig =
{
url = getSetting.Invoke "apiUrl";
token = getSetting.Invoke "apiToken";
}
This works for C#, but breaks my earlier F# call with
error FS0041: A unique overload for method 'FromSettings' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member ThingConfig.FromSettings : getSetting:(string -> string) -> ThingConfig, static member ThingConfig.FromSettings : getSetting:Func -> ThingConfig
Why can't F# figure out which one to call?
What would that type annotation look like? (Can I annotate the parameter type from the call site?)
Is there a better pattern for this kind of interop? (overloads accepting lambdas from both C# and F#)
Why can't F# figure out which one to call?
Overload resolution in F# is generally more limited than C#. The F# compiler will often, in the interest of safety, reject overloads that C# compiler sees as valid.
However, this specific case is a genuine ambiguity. In the interest of .NET interop, F# compiler has a special provision for lambda expressions: regularly, a lambda expression will be compiled to an F# function, but if the expected type is known to be Func<_,_>, the compiler will convert the lambda to a .NET delegate. This allows us to use .NET APIs built on higher-order functions, such as IEnumerable<_> (aka LINQ), without manually converting every single lambda.
So in your case, the compiler is genuinely confused: did you mean to keep the lambda expression as an F# function and call your F# overload, or did you mean to convert it to Func<_,_> and call the C# overload?
What would the type annotation look like?
To help the compiler out, you can explicitly state the type of the lambda expression to be string -> string, like so:
let cfg = ThingConfig.FromSettings( (fun s -> foo) : string -> string )
A slightly nicer approach would be to define the function outside of the FromSettings call:
let getSetting s = foo
let cfg = ThingConfig.FromSettings( getSetting )
This works fine, because automatic conversion to Func<_,_> only applies to lambda expressions written inline. The compiler will not convert just any function to a .NET delegate. Therefore, declaring getSetting outside of the FromSettings call makes its type unambiguously string -> string, and the overload resolution works.
EDIT: it turns out that the above no longer actually works. The current F# compiler will convert any function to a .NET delegate automatically, so even specifying the type as string -> string doesn't remove the ambiguity. Read on for other options.
Speaking of type annotations - you can choose the other overload in a similar way:
let cfg = ThingConfig.FromSettings( (fun s -> foo) : Func<_,_> )
Or using the Func constructor:
let cfg = ThingConfig.FromSettings( Func<_,_>(fun s -> foo) )
In both cases, the compiler knows that the type of the parameter is Func<_,_>, and so can choose the overload.
Is there a better pattern?
Overloads are generally bad. They, to some extent, obscure what is happening, making for programs that are harder to debug. I've lost count of bugs where C# overload resolution was picking IEnumerable instead of IQueryable, thus pulling the whole database to the .NET side.
What I usually do in these cases, I declare two methods with different names, then use CompiledNameAttribute to give them alternative names when viewed from C#. For example:
type ThingConfig = ...
[<CompiledName "FromSettingsFSharp">]
static member FromSettings (getSetting : (string -> string)) = ...
[<CompiledName "FromSettings">]
static member FromSettingsCSharp (getSetting : Func<string, string>) = ...
This way, the F# code will see two methods, FromSettings and FromSettingsCSharp, while C# code will see the same two methods, but named FromSettingsFSharp and FromSettings respectively. The intellisense experience will be a bit ugly (yet easily understandable!), but the finished code will look exactly the same in both languages.
Easier alternative: idiomatic naming
In F#, it is idiomatic to name functions with first character in the lower case. See the standard library for examples - Seq.empty, String.concat, etc. So what I would actually do in your situation, I would create two methods, one for F# named fromSettings, the other for C# named FromSettings:
type ThingConfig = ...
static member fromSettings (getSetting : string -> string) =
...
static member FromSettings (getSetting : Func<string,string>) =
ThingConfig.fromSettings getSetting.Invoke
(note also that the second method can be implemented in terms of the first one; you don't have to copy&paste the implementation)
Overload resolution is buggy in F#.
I filed already some cases, like this where it is obviously contradicting the spec.
As a workaround you can define the C# overload as an extension method:
module A =
type ThingConfig = { url: string; token : string; } with
static member FromSettings (getSetting : (string -> string)) : ThingConfig =
printfn "F#ish"
{
url = getSetting "apiUrl";
token = getSetting "apiToken";
}
module B =
open A
type ThingConfig with
static member FromSettings (getSetting : System.Func<string,string>) : ThingConfig =
printfn "C#ish"
{
url = getSetting.Invoke "apiUrl";
token = getSetting.Invoke "apiToken";
}
open A
open B
let mySettingsAccessor = fun (x:string) -> x
let mySettingsAccessorAsFunc = System.Func<_,_> (fun (x:string) -> x)
let configA = ThingConfig.FromSettings mySettingsAccessor // prints F#ish
let configB = ThingConfig.FromSettings mySettingsAccessorAsFunc // prints C#ish
How do I override the method Zero in the following code in such a way that I can return Euro(0) for the definiton in the type Euro
[<AbstractClass>]
type Currency () =
abstract member Zero<'T when 'T :> Currency > : unit -> 'T
type Euro (value: int) =
inherit Currency()
member this.Value = value
override this.Zero() = Euro(0) :> _
Have you tried lifting the generic constraint to the class level?
[<AbstractClass>]
type Currency<'T when 'T :> Currency<'T>>() =
abstract member Zero : unit -> 'T
type Euro (value: int) =
inherit Currency<Euro>()
member this.Value = value
override this.Zero() = Euro(0)
Though self-referencing generics always seems weird to me, this is how it'd be done in, for example, C#.
There is also the 'roll your own typeclass' technique in F#. Basically your abstract type's (instance and static) members become the fields of a 'typeclass' record, and values of that record are typeclass instances. You can have a 'euro' instance, a 'dollar' instance, and so on:
module Currency =
type t<[<Measure>] 'a> =
{ zero : decimal<'a>; from : decimal -> decimal<'a> }
/// Helper function to easily create typeclass instances for any
/// currency.
let make<[<Measure>] 'a> (curr_unit : decimal<'a>) : t<'a> =
{ zero = curr_unit - curr_unit; from = ((*) curr_unit) }
[<Measure>] type euro
let euro : t<euro> = make 1m<euro>
[<Measure>] type dollar
let dollar : t<dollar> = make 1m<dollar>
The unique thing about F# is that the type parameter that is passed to each typeclass instance can actually be a measure type, which is appropriate for currencies.
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