dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.92k stars 786 forks source link

One of our negative test cases had an error that changed from attractive to spaghetti #13696

Open KevinRansom opened 2 years ago

KevinRansom commented 2 years ago

The error produced by:

module Applicatives =
    open System

    type Ap = Ap with
        static member inline Invoke (x:'T) : '``Applicative<'T>`` =
            let inline call (mthd : ^M, output : ^R) = ((^M or ^R) : (static member Return: _*_ -> _) output, mthd)
            call (Ap, Unchecked.defaultof<'``Applicative<'T>``>) x 
        static member inline InvokeOnInstance (x:'T) = (^``Applicative<'T>`` : (static member Return: ^T -> ^``Applicative<'T>``) x)
        static member inline Return (r:'R       , _:obj) = Ap.InvokeOnInstance      :_ -> 'R
        static member        Return (_:seq<'a>  , Ap   ) = fun x -> Seq.singleton x : seq<'a>
        static member        Return (_:Tuple<'a>, Ap   ) = fun x -> Tuple x         : Tuple<'a>
        static member        Return (_:'r -> 'a , Ap   ) = fun k _ -> k             : 'a  -> 'r -> _

    let inline result (x:'T) = Ap.Invoke x

    let inline (<*>) (f:'``Applicative<'T->'U>``) (x:'``Applicative<'T>``) : '``Applicative<'U>`` = 
        (( ^``Applicative<'T->'U>`` or ^``Applicative<'T>`` or ^``Applicative<'U>``) : (static member (<*>): _*_ -> _) f, x)

    let inline (+) (a:'Num) (b:'Num) :'Num = a + b

    type ZipList<'s> = ZipList of 's seq with
        static member Return (x:'a)                              = ZipList (Seq.initInfinite (fun _ -> x))
        static member (<*>) (ZipList (f:seq<'a->'b>), ZipList x) = ZipList (Seq.zip f x |> Seq.map (fun (f, x) -> f x)) :ZipList<'b>

    type Ii = Ii
    type Idiomatic = Idiomatic with
        static member inline ($) (Idiomatic, si) = fun sfi x -> (Idiomatic $ x) (sfi <*> si)
        static member        ($) (Idiomatic, Ii) = id
    let inline idiomatic a b = (Idiomatic $ b) a
    let inline iI x = (idiomatic << result) x

    let res1n2n3 = iI (+) (result          0M                  ) (ZipList [1M;2M;3M]) Ii
    let res2n3n4 = iI (+) (result LanguagePrimitives.GenericOne) (ZipList [1 ;2 ;3 ]) Ii

repro:

c:\kevinransom\fsharp>fsc C:\kevinransom\fsharp\tests\fsharp\typecheck\sigs\neg119a.fs --langversion:6.0
Microsoft (R) F# Compiler version 12.0.6.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

C:\kevinransom\fsharp\tests\fsharp\typecheck\sigs\neg119.fs(40,20): error FS0071: Type constraint mismatch when applying the default type 'obj' for a type inference variable. No overloads match for method 'Return'.

Known return type: ((int -> int -> int) -> obj)

Known type parameters: < obj , Applicatives.Ap >

Available overloads:
 - static member Applicatives.Ap.Return: ('r -> 'a) * Ap: Applicatives.Ap -> (('a -> 'r -> 'a2) -> 'a3 -> 'a -> 'r -> 'a2) // Argument at index 1 doesn't match
 - static member Applicatives.Ap.Return: System.Tuple<'a> * Ap: Applicatives.Ap -> ('a -> System.Tuple<'a>) // Argument at index 1 doesn't match
 - static member Applicatives.Ap.Return: r: ^R * obj -> ('a1 -> ^R) when ^R: (static member Return: 'a1 -> ^R) // Argument 'r' doesn't match
 - static member Applicatives.Ap.Return: seq<'a> * Ap: Applicatives.Ap -> ('a -> seq<'a>) // Argument at index 1 doesn't match Consider adding further type constraints
c:\kevinransom\fsharp>fsc C:\kevinransom\fsharp\tests\fsharp\typecheck\sigs\neg119.fs --langversion:preview
Microsoft (R) F# Compiler version 12.0.6.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

C:\kevinransom\fsharp\tests\fsharp\typecheck\sigs\neg119.fs(40,9): error FS0030: Value restriction. The value 'res2n3n4' has been inferred to have generic type
    val res2n3n4: ^_a when (^_b or Applicatives.ZipList<int> or ^_a) : (static member (<*>) : ^_b * Applicatives.ZipList<int> -> ^_a) and (^_c or obj or ^_b) : (static member (<*>) : ^_c * obj -> ^_b) and (Applicatives.Ap or ^_c) : (static member Return: ^_c * Applicatives.Ap -> ((int -> int -> int) -> ^_c))
Either define 'res2n3n4' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

Do you see what I mean? The error may be more accurate, but probably needs formatting to make it readable by a tiny brain like myself.

neg119.fs is being renamed to neg119a.fs in a forthcoming pr, so, try that if you get file not found.

0101 commented 2 years ago

How do you like this formatting?

Value restriction. The value 'res2n3n4' has been inferred to have generic type
    val res2n3n4: ^_a when (^_b or Applicatives.ZipList<int> or ^_a) : (static member (<*>) : ^_b * Applicatives.ZipList<int> -> ^_a)
                  and (^_c or obj or ^_b) : (static member (<*>) : ^_c * obj -> ^_b)
                  and (Applicatives.Ap or ^_c) : (static member Return: ^_c * Applicatives.Ap -> ((int -> int -> int) -> ^_c))    
Either define 'res2n3n4' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

https://github.com/dotnet/fsharp/pull/13727