Simplify SRTP resolved on return type - f#

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.

Related

Implementing Tagless Final Encoding in F# with SRTP

I'd like to transform my F# OOP version of Tagless Final into a typical FP approach and I'm thinking to use Statically Resolved Type Parameters of Type Classes from OO.
What I've done is
open System
open FSharpPlus
type UserName = string
type DataResult<'t> = DataResult of 't with
static member Map ( x:DataResult<'t> , f) =
match x with
| DataResult t -> DataResult (f t)
creating the SRTP I need
type Cache =
static member inline getOfCache cacheImpl data =
( ^T : (member getFromCache : 't -> DataResult<'t> option) (cacheImpl, data))
static member inline storeOfCache cacheImpl data =
( ^T : (member storeToCache : 't -> unit) (cacheImpl, data))
type DataSource() =
static member inline getOfSource dataSourceImpl data =
( ^T : (member getFromSource : 't -> DataResult<'t>) (dataSourceImpl, data))
static member inline storeOfSource dataSourceImpl data =
( ^T : (member storeToSource : 't -> unit) (dataSourceImpl, data))
and their concrete implementations
type CacheNotInCache() =
member this.getFromCache _ = None
member this.storeCache _ = ()
type CacheInCache() =
member this.getFromCache user = monad {
return! DataResult user |> Some}
member this.storeCache _ = ()
type DataSourceNotInCache() =
member this.getFromSource user = monad {
return! DataResult user }
type DataSourceInCache() =
member this.getFromSource _ =
raise (NotImplementedException())
by which I can define a tagless final DSL
let requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
and that kind of works as follows
[<EntryPoint>]
let main argv =
let cacheImpl1 = CacheInCache()
let dataSourceImpl1 = DataSourceInCache()
let cacheImpl2 = CacheNotInCache()
let dataSourceImpl2 = DataSourceNotInCache()
requestData cacheImpl1 dataSourceImpl1 "john" |> printfn "%A"
//requestData (cacheImpl2 ) dataSourceImpl2 "john" |> printfn "%A"
0
The problem is that I'm getting the warning
construct causes code to be less generic than indicated by the type
annotations
for both cacheImpl1 and dataSourceImpl1 and so I can't reuse requestData for the other case.
Is there a way to detour this issue?
I'm not familiar with the abstraction you're trying to implement, but looking at your code it seems you're missing an inline modifier here:
let inline requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
As a side note, you can simplify your map function like this:
type DataResult<'t> = DataResult of 't with
static member Map (DataResult t, f) = DataResult (f t)
I am familiar with final tagless, but I'm not sure why you would use SRTPs.
Final tagless uses type classes, and these can be emulated with interfaces (see the way scala emulates typeclasses).
The approach is similar to (basically the same) as "object algebra", which can be implemented using standard OO constructs.

Can I reprogram op_Equals in F#

I'm doing a fun project in F#, which is a DSL for Camel.Net.
At some point, I want to check conditions. But the conditions entered by the programmer should evaluate to an object tree. So I want the experssion "a = b" evaluate to "SomeType.Equals(a,b)"
Is that even possible in F#?
I have this:
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
with
static member (=) (l:Macro, r:Macro) = Equals(l,r)
static member (=) (l:Macro, r:string) = Equals(l,Const(r))
and Comparison =
| Equals of Macro * Macro
Now everything in "Macro" will work as "Macro.Func" - with "Func"; a function is executed with "Message" as input param and will output the string. So the Equals(a,b) will evaluate to a string comparison during runtime.
But this code has a problem. Operator (=) does compile (it has a warning), but it can't be used as I would like.
This does not compile in the fsi:
let c1 = Header("property") = "somevalue"
I did read another question about this topic, and a bit more.
It does not answer my question.
[<NoEquality; NoComparison>] - completely shuts off the (=) operator.
[<CustomEquality; CustomComparison>] - wants you to implement an (=) operator which returns bool.
Is it even possible in F# what I want? And assuming that I can find a way, does match x with still work?
Sure, I did this reimplement to the operator in terms of System.IEquatable<T> for performance reasons:
#nowarn "86" // F# doesn't like it when you do this
[<AutoOpen>]
module FastEquals =
let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y
let inline (=) x y = eq x y
let inline (<>) x y = not (eq x y)
Just an example, you'll need to adapt for your own purposes.
Thanks to Asik's answer above, in combination with a reread of this post:
This works in the fsi:
type Message = class end
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
type Comparison =
| Equals of Macro * Macro
type Operators = Operation with
static member CompareEquals (Operation, l:Macro, r:Macro) = Equals(l,r)
static member CompareEquals (Operation, l:Macro, r:string) = Equals(l,Const(r))
#nowarn "0086" "0064"
let inline (=) (l:'N) (r:'M) = ((^T or ^N or ^M) : (static member CompareEquals : ^T * ^N * ^M -> _) (Operation, l, r))
let c1 = Header("property1") = Header("property2")
let c2 = Header("property") = "somevalue"
Note that it does not work when the static "CompareEquals" methods are located in the "Macro" type.
If you look at the signature of op_Equals:
val inline ( = ) :
l: ^N -> r: ^M -> 'a
when (Operators or ^N or ^M) : (static member CompareEquals : Operators * ^N * ^M -> 'a)
That is a really weird syntax. I don't understand the part after "when". It works, that counts.

In F#, is there a way to simplify this wrapper for OpenFile and SaveFile dialogs'

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())

Better default values for a given type

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.

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

Resources