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.
Related
I'm not sure that what I am looking for is possible, but let's try!
I am trying to define an inline function explicit that will convert a Box<'t> into a 't to any level of nesting.
The code works as expected for one level (Box<int> -> int), however Box<Box<int>> is only converted to Box<int>, despite the type-annotation on the L.H.S.
type Box<'t> =
{
Item : 't
}
with
static member inline op_Explicit (x : Box<'t>) : 't =
x.Item
let inline explicit<'t, 'u when 't : (static member op_Explicit : 't -> 'u)> (x : 't) : 'u =
't.op_Explicit x
let x : int = explicit { Item = 123 }
let y : int = explicit { Item = { Item = 123 } }
let z : int = explicit { Item = { Item = { Item = 123 } } }
I think the short answer is no. The signature of explicit clearly unwraps only a single level, and I don't think there's any type-safe way to define the function you want in F#.
It sounds like you actually want the compiler to automatically chain multiple explicit calls together? For what it's worth, C# will silently insert a single implicit (not explicit) conversion for you, but even that language won't chain more than one together implicitly.
According to docs you can implement multiple interfaces with object expressions. But if you see below code :
// Define two interfaces
type IFirst =
abstract F : unit -> unit
abstract G : unit -> unit
type ISecond =
abstract H : unit -> unit
abstract J : unit -> unit
// This object expression implements both interfaces.
let implementer : IFirst =
{ new ISecond with
member this.H() = ()
member this.J() = ()
interface IFirst with
member this.F() = ()
member this.G() = () }
So casting to IFirst causes a compiler error. Why is that so?
F# does not perform implicit conversions.
When type annotating in a let binding, the type must strictly match the expression.
For example,
let value : obj = new System.Collections.Generic.List<int>()
will fail to compile, even though a List is very obviously an object.
When you write:
let implementer : IFirst = expr
The type of expr must absolutely be IFirst. There's no implicit casting like in C#.
An object expression will have its type as the abstract type implemented, so:
{ new ISecond with ... }
will be inferred to have a type of ISecond. Combine it with no-implicit casts rule, and you have a compile error.
Because IFirst and ISecond are unrelated, you could (runtime) downcast to IFirst:
let firstImplementer = implementer :?> IFirst
Another option is to make a combined interface:
type IBoth = inherit IFirst inherit ISecond
and do:
let implementer =
{
new IBoth with ...
That way you can freely (static) upcast to IFirst or ISecond.
let firstImplementer = implementer :> IFirst
let secndImplementer = implementer :> ISecond
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.
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
Consider the following sample code where I have a generic type and 2 static member constructors that create a specialized instance of the said type.
type Cell<'T> = { slot: 'T }
with
static member CreateInt x : IntCell = { slot = x }
static member CreateString x : StringCell = { slot = x}
and IntCell = Cell<int>
and StringCell = Cell<string>
// Warnings on the next 2 lines
let x = Cell.CreateInt 123
let y = Cell.CreateString "testing"
I think I have the necessary type annotations in place and yet F# gives me warnings. E.g:
Warning 2 The instantiation of the generic type 'Cell' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'Cell<_>'.
How can I make the warning go away?
As hinted by #ildjarn, Cell is a generic type and the compiler wants to know the type 'T when calling the static member.
// Two ways to fix the compiler warning
let x = Cell<int>.CreateInt 123
let y = StringCell.CreateString "testing"
A way to avoid specifying 'T is to move the create functions into a module.
type Cell<'T> = { slot: 'T }
type IntCell = Cell<int>
type StringCell = Cell<string>
module Cell =
let createInt x : IntCell = { slot = x }
let createString x : StringCell = { slot = x }
let x = Cell.createInt 123
let y = Cell.createString "testing"
However, since you specify the desired type in the function name anyway, the following syntax may be preferred.
type Cell<'T> = { slot: 'T }
with
static member Create (x : 'T) = { slot = x }
type IntCell = Cell<int>
type StringCell = Cell<string>
let x = IntCell.Create 123
let y = StringCell.Create "testing"
// or simply
let z = Cell<float>.Create 1.0
Thanks to #Vandroiy for pointing out the missing type constraint in my Create method and for his answer that shows how the compiler can infer 'T for the generic type Cell when it can be determined by the static method being called.
The compiler cannot determine the generic parameter 'T of the methods CreateInt and CreateFloat because it is unrelated to the methods' return types. In the question, it is valid to write:
Cell<float>.Create 1.0 // useless type annotation to remove warning
However, you can just as well write
Cell<string>.Create 1.0 // Trollolol
To avoid this, you need to make sure the factory can only produce the type it is called on. When declaring a factory on a generic type, use a type annotation to equate the generic argument of its return type with the generic argument of the type it is called on.
In my opinion, the complicated formulation adds to the confusion. You can achieve the desired effect with
type Cell<'T> =
{ slot: 'T }
static member Create (x : 'T) = { slot = x }
let x = Cell.Create 123
let y = Cell.Create "testing"
Note the type annotation for x that equates the factory's input type with the generic parameter of the Cell<> type!
Edited to address the comment:
As is, the types IntCell and StringCell serve no purpose; they are just a less readable form of Cell<int> and Cell<string>. From a comment to this answer, I understand that these types should be exposed instead of Cell. As far as I know, this is not possible if they are defined as in the question, since type abbreviations have at most the accessibility of the type they abbreviate.
This is a reasonable design choice: if a type is generic, it should accept all valid generic type arguments. If IntCell and StringCell add specialized implementation, the usual way is to compose them of the appropriate instantiation of the Cell type and their specialized features. Then, the Cell type is allowed to have a more restricted accessibility than the specialized types.