-
Notifications
You must be signed in to change notification settings - Fork 145
Create FS-1150-Literal-inferred-types.md #800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
pending changes from fsharp/fslang-suggestions#1427 (comment) |
|
@T-Gro Should there be new static constraints associated with these inferences? |
T-Gro
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to emphasize that the biggest advantage is not with explicit type annotation for standalone values (seen here in examples but also typical for C# not using var).
The motivating examples should come from passing arguments to existing methods/functions. The benefits are then multi-fold:
- Easier code with fewer boilerplate
- Fewer runtime conversions
- Possibilities for "improve perf by recompiling" scenarios, e.g. if a library changes it's arguments to support stack allocated ROS.
T-Gro
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One aspect I would like to see in bigger detail, e.g. as a diff to existing spec, is how this plays with method resolution in case of multiple ambiguous overloads - non-generic as well as generic. Since this RFC touches literals, it does not have much overlap with function parameter inference - even though scenarios combining literals and symbols like
let myFunc x =
let _ = [ 1; x ; 3]mean it has to be considered as well.
Could you elaborate please? I am not sure I follow what exactly you mean. |
let inline f a b = a + b
let a: int64 = f 1L 2L
let b: byte = f 1uy 2uy
// val inline f: ^a -> ^b -> ^c when (^a or ^b): (static member (+): ^a * ^b -> ^c)
// works similarly to
let inline g() = 1
let c: int64 = g()
let d: byte = g()
// val inline g: unit -> ^a when ^a: 1then this inline function needs a new static constraint, yes? let inline myFunc x = [1; x; 3]
// val inline myFunc: ^a -> ^b when ^a: 1 and ^a: 3 and ^b: [^a]
let x: int list = myFunc 2 // yes
let y: byte array = myFunc 2 // yes
let z: ResizeArray<uint> = myFunc -1 // error: uint cannot express the integer -1This also means a potential breaking change let f a b = a + b
let a = f 1uy 2uy // f: byte -> byte -> byte
let b = f 1 2 // error
// similarly...
let x = 1
let y: int64 = x // infers x as int64, but currently uses int32->int64 implicit conversion
let z: int32 = x // error?Tooling can provide a codefix to specify a type for |
So the type constraint would be with the semantics I haven't thought about such deep and invasive impact on type inference - this would likely change any piece of code working with literals in basic arithmetic ways. This probably mean that the feature would have to be rejected due to the impact on existing code. |
|
@T-Gro Yes, the implementation will probably introduce additional static constraints like the ability to admit specific literals like 1 + 1.5 + 2such invasive impacts on type inference is restricted to This may have some impact on existing code, but this also enables a lot of succinctness (no one has to understand what the |
open System.Numerics
type Vector3 with
static member op_Implicit struct(x, y, z) = Vector3(x, y, z)
let inline f() = [
1, 2, 3
4, 5, 6
]
// val inline f: unit -> ^a when ^a: [^b] and ^b: 1 and ^b: 2 and ^b: 3 and ^b: 4 and ^b: 5 and ^b: 6
let x: Vector3 array = f() // works
let y: Vector3 array = [
1, 2, 3
4, 5, 6
] // worksAn alternative to these static constraints is to disallow literals with no target type, such that |
What algebra do you anticipate for expressing these constraints? And when materialized after inlining/type-direction, every member would be checked for compile-time translation or fail otherwise? let intArrayFunc (x: int array) = Array.sum x
let floatArrayFunc (x: float32 array) = Array.sum x
let _ = intArrayFunc [1.0;2.0] //OK
let _ = floatArrayFunc [5;-1;nan,infinity] // OK
let _ = intArrayFunc [1.0;2.5] // FSxxx Literal '2.5' cannot be safely used as 'int'
let _ = floatArrayFunc [0.3] // FSxxx Literal '0.3' cannot be safely used as 'float32'
//-----------------------^^ Would this report at compile-time? Or think of big int64 literals used for float32, where loss of precision also occurs |
I agree especially when it comes to name resolution and type inference. This RFC would introduce:
The RFC should indicate how the spec will change, and aim to use existing terminology from the spec. |
|
I realized that this would also change let a = ["a"; "b"; "c"]
let b = a :> obj // Should this now produce an error after inferring as ReadOnlySpan?
System.String.Concat(",", a) |
In the general case, this is hard - type inference works top to bottom, and doing a decision system which can effected previously decided bindings will be a massive change. In this specific case, where a warning comes from PostInferenceChecks (post-inference), like the case with byref safety, this could likely be achieved like you write - infer based on This however introduces inconsistency, or rather leaks the implementation details of checks (during inference or after it) to the end user. |
|
I will do another review round during August and will come back to the suggestion with my current opinions ( approve in principle | generally agree | undecided | probably not) on each sub proposal listed in the RFC. Now that we have the details written out (thanks 👍 again for doing that, I think it does bring clarity into the breadth of the proposal and the different phases that can be decided and implemented separately), we can refer to them and achieve a more focused discussion. |
|
@T-Gro 👍 My attention is currently diverted to fold loops so this can be on hold for now. |
Click “Files changed” → “⋯” → “View file” for the rendered RFC.