Last active
March 28, 2016 11:48
-
-
Save eiriktsarpalis/0d7378212dd8b4c32c20 to your computer and use it in GitHub Desktop.
Strange issue in F# constraint solving
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Test = | |
static member F<'T>(t : 'T option) = () | |
static member F<'T>(e : 'T -> 'T) = () | |
type Foo = { A : int } | |
type Bar = { A : int } | |
Test.F(fun (r:Foo) -> { r with A = 32 }) // type checks | |
Test.F(fun (r:Bar) -> { r with A = 32 }) // type checks | |
Test.F<Foo>(fun r -> { r with A = 32 }) // type error | |
Test.F<Bar>(fun r -> { r with A = 32 }) // warning |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
F#'s type inference is a bit wonky to begin with, but explicit type arguments make things particularly ugly. Fortunately, there's really only one place you absolutely must use them, the usual case being when you want to parameterize a record, sum, or class by type(s)--but for function calls, as you're doing here, and in fact even when making calls whose overload will be selected via SRTP/constraints rather than simple overloads, you can avoid them entirely:
Notice the dummy
_resolve
argument being passed inShow.invoke
--and yes, it's stupid that you have to do this at the term level in order to make a type-level mechanism function correctly, but it works, and it avoids the problem you were having. In fact, this technique avoids a much worse problem that often occurs if you attempt to use explicit type parameters instead: the compiler will demand that you affix a constraint signature that it can solve, but which is syntactically invalid and thus impossible to write!