I'm writing some kind of serialization library (for purpose of learning F#). And now I stuck with this:
Suppose we already have serialization functions for some basic types:
type StoreOps =
static member inline store(x:int) = ...
static member inline store(x:int64) = ...
static member inline store(x:float) = ...
static member inline store(x:float32) = ...
static member inline store(x:bool) = ...
static member inline store(x:string) = ...
....
Now I want to implement generic function to store any array of basic types:
let inline store(x:'T[]) =
x |> Array.iter StoreOps.store
, but compiler can't compile it (error message says: A unique overload for method 'store' could not be determined based on type information prior to this program point).
What is a right way to implement such functions in F#? Because I don't want to copy-paste N equal functions for int[], bool[], float[]...
First of all, you probably don't need inline on the definitions which take arguments of a particular type. Secondly, the short answer is probably "there's no good way to do that". However, if you're willing to tolerate horrible hacks, you can do something like:
type StoreOps =
... // everything you've currently got
let inline storeArray< ^t, ^u when (^t or ^u) : (static member store : ^u -> unit)> arr =
arr
|> Array.iter (fun x -> ((^t or ^u) : (static member store : ^u -> unit) x))
type StoreOps with
static member inline store arr = storeArray<StoreOps,_> arr
You can also make the storeArray helper private (using let inline private storeArray... if you don't want it exposed.
One workaround is passing store functions as a parameter in a generic function:
type StoreOps =
static member inline store (x: int) = ...
static member inline store (x: int64) = ...
static member inline store (x: float) = ...
static member inline store (x: float32) = ...
static member inline store (x: bool) = ...
static member inline store (x: string) = ...
static member storeArray xs f =
xs |> Array.iter f
....
// The compiler chooses the right overload based on types of array elements
StoreOps.storeArray [|100; 200|] StoreOps.store
StoreOps.storeArray [|100L; 200L|] StoreOps.store
You can do it this way:
type StoreOps = StoreOps with
static member ($) (StoreOps,x:int) = (* code for storing *) ()
static member ($) (StoreOps,x:int64) = (* code for storing *) ()
static member ($) (StoreOps,x:float) = (* code for storing *) ()
static member ($) (StoreOps,x:float32) = (* code for storing *) ()
static member ($) (StoreOps,x:bool) = (* code for storing *) ()
static member ($) (StoreOps,x:string) = (* code for storing *) ()
let inline store(x:_[]) = Array.iter (fun a -> StoreOps $ a) x
It will generate the constraints for you automatically.
Related
I have some production code that I'd like to simplify (especially in light of new SRTP F# behaviour).
The intention is to statically resolve a method/function based solely on the return type required.
A simplified version of it is this:
type TypeOf<'a> = T
type ZeroFactory = Z with
static member Zero(_: ZeroFactory,_ : TypeOf<int>) : _ = 0
static member Zero(_: ZeroFactory,_ : TypeOf<string>) : _ = ""
let inline inlineZero t =
((^T or ^N) : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
let inline zero () = inlineZero Z
let foo : int = zero ()
let bar : string = zero ()
this code compiles and does what I intend, but has always felt overly contrived.
I CAN write this:
let inline inlineZero2 t =
(^T : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
and to my eyes, that would seem to be good enough, but if i write:
let inline zero2 () = inlineZero2 Z
I get
Error FS0043 A unique overload for method 'Zero' could not be determined based on type information prior to this program point. A type annotation may be needed.
Known return type: 'a
Known type parameters: < ZeroFactory , TypeOf<'a> >
Candidates:
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<int> -> int
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<string> ->
my hunch is that all the static type parameters in the method specification have to be mentioned on the left.
This question is based on the Functional Random Generators in week one of this course: https://www.coursera.org/course/reactive
The course is Scala based, I'm trying to replicate it in FSharp.
Here's my problem:
I have an abstract generator
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
And I have an implementation of that that generates random integers
type IntGenerator() =
inherit Generator<Int32>()
let rand = new System.Random()
override this.Generate = rand.Next(Int32.MinValue, Int32.MaxValue)
Now, I want to add a Map method to my base class so that new types of generators can be created using code like this
let integers = new IntGenerator()
let booleans = integers.Map (fun x -> x > 0)
So, here's how I modified the base class
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
member this.Map (f:'a -> 'b) = { new Generator<'b>() with member this.Generate = f base.Generate }
Unfortunately that call to base.Generate seems to be constraining the type 'b to the same as 'a
I don't get why. I'm sure I'm tripping up on something simple.
The problem here is that you have to be careful with the instance names of the member methods:
I think this should work out:
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: unit -> 'a
static member Map (f:'a -> 'b) =
fun (g : Generator<'a>) ->
{ new Generator<'b>() with
member this.Generate () = g.Generate () |> f
}
or if you insist on the member-method:
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: unit -> 'a
member this.Map (f:'a -> 'b) : Generator<'b> =
{ new Generator<'b>() with
member __.Generate () = this.Generate () |> f
}
note the differences with the this and base and __ ;)
BTW this will even work without the unit -> (as you wrote it):
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
member this.Map (f:'a -> 'b) : Generator<'b> =
{ new Generator<'b>() with
member __.Generate = this.Generate |> f
}
but I would not recommend this as you get a value-looking method in disguise: Generate
more idiomatic way
type Generator<'a> = unit -> 'a
module Generator =
let map (f:'a -> 'b) (g : Generator<'a>) =
fun () -> g () |> f
let intGenerator : Generator<int> =
let rand = new System.Random()
fun () -> rand.Next(System.Int32.MinValue, System.Int32.MaxValue)
fun fact
As you can see here (if you look closely) you will see that this map is really just the Functor-map for unit -> * ;)
disclaimer but of course as the generators are sadly impure none of the functor-laws will really hold (if you don't fix your system time)
type MyOpenFileDialog(dg: OpenFileDialog) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.OpenFile
type MySaveFileDialog(dg: SaveFileDialog) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.OpenFile
The following indicated 'T (^T) would escape it's scope:
type MyFileDialog<'T
when
'T : (member OpenFile:unit->Stream) and
'T : (member ShowDialog:unit->Nullable<bool>)
>(dg: 'T) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.op
Note, the constraint expression MyFileDialog<'T...>(dg: 'T)= should all be on one line. Is split for clarity (should be allowed in language I think :) )
Static member constraints let you do quite a lot of things, but they are somewhat cumbersome (they work well for generic numerical computations, but not so well as a general-purpose abstraction). So, I would be careful about using them.
Anyway, if you want to do this, then you can - to some point. As John mentioned in the comments, code that uses static member constraints need to be inline. But you can make that a static member, which captures the operations that your type uses:
type MyFileDialog
private(openFile:unit -> Stream, showDialog : unit -> DialogResult) =
member x.OpenFile() = openFile()
member x.ShowDialog() = showDialog()
static member inline Create(dg : ^T) =
MyFileDialog
( (fun () -> (^T : (member OpenFile:unit -> Stream) dg)),
(fun () -> (^T : (member ShowDialog:unit -> DialogResult) dg)) )
let d1 = MyFileDialog.Create(new OpenFileDialog())
let d2 = MyFileDialog.Create(new SaveFileDialog())
Say I have DUs
type Zero = Zero
type Succ<'a> = Succ of 'a
I can use Unchecked.defaultof<Zero Succ Succ> when I'm passing the value to an inline function that only uses the type of that value. But how would I go about getting a proper value of a given type? One that isn't null.
Since the only valid value that has a type Zero Succ Succ is Succ(Succ(Zero)) I feel like I should be able to get it.
I don't care for intermediate type safety so I'm ok with typecasts.
I've tried this:
let rec makeProperSucc (a : Succ<'a>) = Succ(makeProperNum Unchecked.defaultof<'a>)
and makeProperNum (obj : obj) : obj =
if obj :? Zero then Zero :> obj
else makeProperSucc (obj :?> Succ<obj>) :> obj
let instance<'a>() = makeProperNum Unchecked.defaultof<'a> :?> 'a
Which doesn't work because defaultof gives me a null and all the type information is lost because I send it to a function expecting obj.
What about defining a static member with static constraints in the DU?
Here's a quick draft with a unary operator:
type Zero = Zero with
static member (!!) Zero = Zero
type Succ<'a> = Succ of 'a with
static member inline (!!) (Succ a) = Succ (!!a)
// Test
!!(Unchecked.defaultof<Succ<Succ<Succ<Zero>>>>)
// val it : Succ<Succ<Succ<Zero>>> = Succ (Succ (Succ Zero))
I used an operator to keep it simple, but you can also write the static constraints by hand or use the inline helper as you did before in your previous question.
UPDATE
If you want to use the inline helper module, used in FsControl 1.x, you can do it this way:
type Instance = Instance with
static member instance(Instance, _:unit ) = fun () -> ()
static member instance(Instance, _:Zero ) = fun () -> Zero
static member inline instance(Instance, _:Succ<'b>) = fun () ->
Succ (Inline.instance Instance ())
let inline instance() = Inline.instance Instance ()
let test:Succ<Succ<Zero>> = instance()
If you want to use the notation instance<'t>() you will have to disable the warning.
Been working with a lot of TimeSpans recently, and have a need to get sums & averages.
However, TimeSpan defines neither operator get_Zero nor DivideByInt, so Seq.sum and Seq.average can't be used directly with this type. The following fails to compile:
open System
type System.TimeSpan
with
static member Zero with get() = TimeSpan()
static member (/) (n:DateTime, d:int) = DateTime( n.Ticks / (int64) d )
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
Error: The type 'TimeSpan' does not support any operators named 'get_Zero'
Error: The type 'TimeSpan' does not support any operators named 'DivideByInt'
Warning: Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.
Is there some F# magic that can define these operators on an existing type?
I know the following will work (and should be more efficient to boot), but I'm still curious about the above so I can add it to my toolbox for use with other types.
let sum = TimeSpan( ts |> Seq.sumBy (fun t -> t.Ticks) )
let avg = TimeSpan( let len = ts |> Seq.length in sum.Ticks / int64 len )
As far as I know, static member constraints (that are used by functions like Seq.sum) are not able to discover members that are added by type extensions (essentially, extension methods), so I don't think there is a direct way of doing this.
The best option I can think of is to creat a simple wrapper around the System.TimeSpan struct. Then you can define all the required members. The code would look like this:
[<Struct>]
type TimeSpan(ts:System.TimeSpan) =
member x.TimeSpan = ts
new(ticks:int64) = TimeSpan(System.TimeSpan(ticks))
static member Zero = TimeSpan(System.TimeSpan.Zero)
static member (+) (a:TimeSpan, b:TimeSpan) =
TimeSpan(a.TimeSpan + b.TimeSpan)
static member DivideByInt (n:TimeSpan, d:int) =
TimeSpan(n.TimeSpan.Ticks / (int64 d))
let ts = [ TimeSpan(10L); TimeSpan(99L) ]
let sum = ts |> Seq.sum
let avg = ts |> Seq.average
I called the type TimeSpan, so it hides the standard System.TimeSpan type. However, you still need to write ts.TimeSpan when you need to access the underlying system type, so this isn't as nice as it could be.
Mhh the following is rather ugly, but it works. Does it help? I define a wrapper for TimeSpan that can implicitly be converted back to a TimeSpan.
type MyTimeSpan(ts : TimeSpan) =
member t.op_Implicit : TimeSpan = ts
static member (+) (t1 : MyTimeSpan, t2 : MyTimeSpan) =
new MyTimeSpan(TimeSpan.FromTicks(t1.op_Implicit.Ticks + t2.op_Implicit.Ticks))
static member Zero = new MyTimeSpan(TimeSpan.Zero)
static member DivideByInt (t : MyTimeSpan, i : int) =
new MyTimeSpan(TimeSpan.FromTicks(int64 (float t.op_Implicit.Ticks / float i)))
let toMyTS ts = new MyTimeSpan(ts)
let l = [TimeSpan.FromSeconds(3.); TimeSpan.FromSeconds(4.)]
|> List.map toMyTS
|> List.average