F # type restrictions and overload resolution

I am trying to emulate a type class system in F #; I would like to create a paired printer that automatically creates the correct call sequence for print functions. My last attempt, which is inserted here, fails, since F # cannot identify the correct overload and immediately refuses:

type PrintableInt(x:int) = member this.Print() = printfn "%d" x let (!) x = PrintableInt(x) type Printer() = static member inline Print< ^a when ^a : (member Print : Unit -> Unit)>(x : ^a) = (^a : (member Print : Unit -> Unit) x) static member inline Print((x,y) : 'a * 'b) = Printer.Print(x) Printer.Print(y) let x = (!1,!2),(!3,!4) Printer.Print(x) 

Is there any way to do this? I do this in the context of game development, so I can’t afford the overhead during reflection, redialling and dynamic casting: either I do it statically through inlining, or I don’t do this at all :(

+4
source share
2 answers

What you are trying to do is possible. You can emulate models in F #, as Thomas said, perhaps this is not as idiomatic as in Haskell. I think in your example you mix type classes with duck type, if you want to go to typeclasses type approach, don't use members, use functions and static members instead.

So your code might be something like this:

 type Print = Print with static member ($) (_Printable:Print, x:string) = printfn "%s" x static member ($) (_Printable:Print, x:int ) = printfn "%d" x // more overloads for existing types let inline print p = Print $ p type Print with static member inline ($) (_Printable:Print, (a,b) ) = print a; print b print 5 print ((10,"hi")) print (("hello",20), (2,"world")) // A wrapper for Int (from your sample code) type PrintableInt = PrintableInt of int with static member ($) (_Printable:Print, (PrintableInt (x:int))) = printfn "%d" x let (!) x = PrintableInt(x) let x = (!1,!2),(!3,!4) print x // Create a type type Person = {fstName : string ; lstName : string } with // Make it member of _Printable static member ($) (_Printable:Print, p:Person) = printfn "%s, %s" p.lstName p.fstName print {fstName = "John"; lstName = "Doe" } print (1 ,{fstName = "John"; lstName = "Doe" }) 

Note. I used the operator to avoid writing constraints manually, but in this case you can also use a named static member. Read more about this method here .

+7
source

What you are trying to do is not possible ( edit: apparently it can be done - but it may not be idiomatic F #), because the constraint language cannot fix the constraints needed for the second Print operation. Basically, there is no way to write recursive constraints saying that:

Let C be a constraint indicating that the type either provides Print or is a two-element tuple, where each element satisfies C.

F # does not support class types, and so most attempts to mimic them (possibly) will be somehow limited or look very unnatural. In practice, instead of trying to emulate solutions that work in other languages, it is better to look for the F # idiomatic solution for the problem.

The pretty print you use as a model is likely to be implemented using Reflection or by packing not only integers, but also tuples.

+4
source

Source: https://habr.com/ru/post/1403411/


All Articles