Skip to content

Improve error message when attempting to override generic return type with unit #35

@KevinRansom

Description

@KevinRansom

[originally from Connect]

This kind of F# code doesn't work when passing unit as a type argument. If this is an intentional design restriction, the error message should reflect this.

type EDF<'S> =
    abstract member Apply : int -> 'S
type SomeEDF1 () =
    interface EDF<int> with
        member this.Apply d = // OK
            123
type SomeEDF2 () =
    interface EDF<unit> with
        member this.Apply d = 
            // [ERROR] The member 'Apply : int -> unit' does not have the correct type to override the corresponding abstract method.
            ()

comments
dsyme wrote May 29, 2014 at 6:58 AM [x]
Yes, for F# 3.x this is an intentional design restriction, see http://stackoverflow.com/questions/4485445/f-interface-inheritance-failure-due-to-unit/4485677#4485677 for a longer analysis.

You might like to add an issue to http://fslang.uservoice.com to track this, as the F# language spec and implementation could in theory be adjusted to do the appropriate insertion of a "unit" value in the implementation of the override.

I agree that the error message should be improved here.

dsyme wrote May 29, 2014 at 7:17 AM [x]
To summarize - in F#, an abstract slot with declared return type of "unit" gets compiled in .NET IL as a return type of "void". In contrast, an abstract slot with declared return type of "T" gets compiled in .NET IL as a generic return type of "T", which when T is instantiated by "unit" becomes "unit". This means these two slots have logically different characteristics. This difference is one of the few places where the unit<->void translation performed by F# is visible and yes, it affects the overriding rules of the F# language.

@antofik asked about the compiler implementation here, specifically IsExactMatch and the check "returnTypesAEquiv" (here in the GitHub code: https://github.com/fsharp/fsharp/blob/6ac6318858da25fe4f1d4728901c5824e9f3bf3a/src/fsharp/tastops.fs#L874 - when will codeplex support links to individual lines :) ).

This operation compares optional TType values representing the return type of the member. None indicates that the return type is logically "void" from the perspective of F# overriding rules, Some(unit-type) or Some (T) indicates the return type is logically "unit" or "T" from the perspective of F# overriding rules.

In terms on nomenclature in the compiler:

  • "CompiledForm" of a method signature means that return types are TType options,
    "None = type void", and "Some(T) = type T", and "Some(T) under substitution T-->unit = type unit".
  • "FSharpForm" of a method signature means that return types are TType, and where "void" doesn't occur at all

Note that in the debugger, "None" shows as "null".

dsyme wrote May 29, 2014 at 7:21 AM [x]
To add a better error message, the logical place to put it would be here: https://github.com/fsharp/fsharp/blob/6ac6318858da25fe4f1d4728901c5824e9f3bf3a/src/fsharp/typrelns.fs#L1191

"IsExactMatch" would need to be duplicated and adjusted to a "IsExactMatchUpToInstantiatedUnitReturnType". If an override exists satisfying that predicate, then a better error message can then be given, much as for code already present to give a better error message when there is an override satisfying IsPartialMatch or IsNameMatch.
Cheers!
don

latkin wrote Jun 20, 2014 at 6:24 AM [x]

Updating the title to capture specific goal for short term. fslang uservoice can be used to track this as a potential change to the language itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions