qehgt
qehgt

Reputation: 2990

generic function in F#

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[]...

Upvotes: 2

Views: 403

Answers (3)

Gus
Gus

Reputation: 26204

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.

Upvotes: 2

kvb
kvb

Reputation: 55195

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.

Upvotes: 4

pad
pad

Reputation: 41290

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

Upvotes: 3

Related Questions