简体   繁体   中英

F#: Inconsistent SRTP static extension method type matching

I am trying to use PrintfFormat to type enforce resolution of parsers and it initially appeared to work for int but then same approach for string did not work... while float did work, so I thought was Value/Ref type issue but then tried bool and that didn't work like String.

int & float work, string & bool do not!?

(ParseApply methods are dummy implementations for now)

    type System.String  with static member inline ParseApply (path:string) (fn: string -> ^b) : ^b = fn ""
    type System.Int32   with static member inline ParseApply (path:string) (fn: int   -> ^b) : ^b = fn 0
    type System.Double  with static member inline ParseApply (path:string) (fn: float -> ^b) : ^b = fn 0.
    type System.Boolean with static member inline ParseApply (path:string) (fn: bool -> ^b) : ^b = fn true

    let inline parser (fmt:PrintfFormat< ^a -> ^b,_,_,^b>) (fn:^a -> ^b) (v:string) : ^b 
        when ^a : (static member ParseApply: string -> (^a -> ^b) -> ^b) =
        (^a : (static member ParseApply: string -> (^a -> ^b) -> ^b)(v,fn))

    let inline patternTest (fmt:PrintfFormat< ^a -> Action< ^T>,_,_,Action< ^T>>) (fn:^a -> Action< ^T>) v : Action< ^T> = parser fmt fn v

    let parseFn1 = patternTest "adfadf%i" (fun v -> printfn "%i" v; Unchecked.defaultof<Action<unit>> ) // works
    let parseFn2 = patternTest "adf%s245" (fun v -> printfn "%s" v; Unchecked.defaultof<Action<unit>> ) // ERROR
    let parseFn3 = patternTest "adfadf%f" (fun v -> printfn "%f" v; Unchecked.defaultof<Action<unit>> ) // works
    let parseFn4 = patternTest "adfadf%b" (fun v -> printfn "%b" v; Unchecked.defaultof<Action<unit>> ) // ERROR

The error I get on result2 function format string input is The type 'string' does not support the operator 'ParseApply' , similarly, result4 error is The type 'bool' does not support the operator 'ParseApply' .

I don't know why there is this inconsistency, is it a bug or am I missing something?

I think this is still an open gap in the F# compiler, ie that Extension Members are not visible to type constraints. There's a WIP PR here that bridges the gap.

As @ChesterHusk said, at the moment extensions are not visible to trait calls.

See also Error on Extension Methods when Inlining

At the moment, the way to make it work is using an intermediate class with an operator-like trait call (operators normally look into their own class and in user defined classes).

open System

type T = T with
    static member inline ($) (T, _:string) : _ ->_ -> ^b = fun (path:string) (fn: string -> ^b)-> fn ""
    static member inline ($) (T, _:int)    : _ ->_ -> ^b = fun (path:string) (fn: int   -> ^b) -> fn 0
    static member inline ($) (T, _:float)  : _ ->_ -> ^b = fun (path:string) (fn: float -> ^b) -> fn 0.
    static member inline ($) (T, _:bool)   : _ ->_ -> ^b = fun (path:string) (fn: bool -> ^b)  -> fn true

let inline parser (fmt:PrintfFormat< ^a -> ^b,_,_,^b>) (fn:^a -> ^b) (v:string) : ^b = (T $  Unchecked.defaultof< ^a> ) v fn

let inline patternTest (fmt:PrintfFormat< ^a -> Action< ^T>,_,_,Action< ^T>>) (fn:^a -> Action< ^T>) v : Action< ^T> = parser fmt fn v

let parseFn1 = parser "adfadf%i" (fun v -> printfn "%i" v; Unchecked.defaultof<int>)
let parseFn2 = parser "adf%s245" (fun v -> printfn "%s" v; Unchecked.defaultof<string>)
let parseFn3 = parser "adfadf%f" (fun v -> printfn "%f" v; Unchecked.defaultof<float>)
let parseFn4 = parser "adfadf%b" (fun v -> printfn "%b" v; Unchecked.defaultof<bool>)

This can be written with named methods by replicating the way operators trait call are desugared.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM