Search code examples
f#type-constraints

Is it possible in F# to factor out type constraints


I'm using the below bit of code

// Neat method of finding the TryParse method for any type that supports it.
// See https://stackoverflow.com/a/33161245/158285
let inline tryParseWithDefault (defaultVal:'a) text : ^a when ^a : (static member TryParse : string * ^a byref -> bool) = 
    let r = ref defaultVal
    if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r.contents)) 
    then !r 
    else defaultVal  

but I notice that the type constraint

^a : (static member TryParse : string * ^a byref -> bool

is used twice. Is there any way to do the following

constraint Parsable a = ( a : ^a : (static member TryParse : string * ^a byref -> bool)

and use Parsable like

// Neat method of finding the TryParse method for any type that supports it.
// See https://stackoverflow.com/a/33161245/158285
let inline tryParseWithDefault (defaultVal:'a) text : Parsable = 
    let r = ref defaultVal
    if (^a : (Parsable) (text, &r.contents)) 
    then !r 
    else defaultVal  

Solution

  • As the existing answer says, you do not explicitly need to repeat the constraint in the type signature because the F# compiler can infer it. One more way of further factoring out the code that involves the type constraint would be to just have tryParse function which invokes the TryParse method (and has the type constraint) and then call this function from your tryParseWithDefault.

    This way, you separate the "core" logic of invoking the member from any extra logic. When you do this, you again don't need to repeat the constraint, because the compiler infers it:

    let inline tryParse text = 
        let mutable r = Unchecked.defaultof<_>
        (^a : (static member TryParse: string * ^a byref -> bool) (text, &r)), r 
    
    let inline tryParseWithDefault (defaultVal:'a) text =
        match tryParse text with 
        | true, v -> v
        | _ -> defaultVal