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.87k stars 781 forks source link

{||} cannot be used as a type #6941

Open Happypig375 opened 5 years ago

Happypig375 commented 5 years ago

{||} cannot be used as a type.

Expected behavior


Microsoft (R) F# Interactive version 10.4.0 for F# 4.6
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> {||};;
val it : {||} = {}

> let x = {||};;
val x : {||} = {}

> let x : {||} = {||};;
val x : {||} = {}

Repro steps and actual behavior


Microsoft (R) F# Interactive version 10.4.0 for F# 4.6
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> {||};;
val it : {||} = {}

> let x = {||};;
val x : {||} = {}

> let x : {||} = {||};;

  let x : {||} = {||};;
  ----------^^

stdin(3,11): error FS0010: Unexpected symbol '|}' in field declaration. Expected identifier or other token.

Known workarounds

Let the compiler infer the type.

Related information Microsoft Visual Studio Community 2019 Version 16.1.1 VisualStudio.16.Release/16.1.1+28922.388 Microsoft .NET Framework Version 4.7.03056 Installed Version: Community Visual C++ 2019 00435-60000-00000-AA419 Microsoft Visual C++ 2019 Application Insights Tools for Visual Studio Package 9.1.00429.1 Application Insights Tools for Visual Studio ASP.NET and Web Tools 2019 16.1.429.50124 ASP.NET and Web Tools 2019 Azure App Service Tools v3.0.0 16.1.429.50124 Azure App Service Tools v3.0.0 C# Tools 3.1.0-beta4-19266-03+9d80dea7fe1b14043b9b2ac4d0b59ed26f508742 C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Common Azure Tools 1.10 Provides common services for use by Azure Mobile Services and Microsoft Azure Tools. Extensibility Message Bus 1.1.77 (master@24013d5) Provides common messaging-based MEF services for loosely coupled Visual Studio extension components communication and integration. Microsoft JVM Debugger 1.0 Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines Microsoft MI-Based Debugger 1.0 Provides support for connecting Visual Studio to MI compatible debuggers Microsoft Visual C++ Wizards 1.0 Microsoft Visual C++ Wizards Microsoft Visual Studio VC Package 1.0 Microsoft Visual Studio VC Package Mono Debugging for Visual Studio 16.1.1 (2473f22) Support for debugging Mono processes with Visual Studio. NuGet Package Manager 5.1.0 NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/ ProjectServicesPackage Extension 1.0 ProjectServicesPackage Visual Studio Extension Detailed Info ResourcePackage Extension 1.0 ResourcePackage Visual Studio Extension Detailed Info ResourcePackage Extension 1.0 ResourcePackage Visual Studio Extension Detailed Info Syntax Visualizer 1.0 An extension for visualizing Roslyn SyntaxTrees. TypeScript Tools 16.0.10506.2004 TypeScript Tools for Microsoft Visual Studio Visual Basic Tools 3.1.0-beta4-19266-03+9d80dea7fe1b14043b9b2ac4d0b59ed26f508742 Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Visual F# Tools 10.4 for F# 4.6 16.1.0-beta.19253.3+42526fe359672a05fd562dc16a91a43d0fe047a7 Microsoft Visual F# Tools 10.4 for F# 4.6 Visual Studio Code Debug Adapter Host Package 1.0 Interop layer for hosting Visual Studio Code debug adapters in Visual Studio Visual Studio Tools for Unity 4.1.1.0 Visual Studio Tools for Unity VisualStudio.Mac 1.0 Mac Extension for Visual Studio Xamarin 16.1.0.542 (d16-1@68b985244) Visual Studio extension to enable development for Xamarin.iOS and Xamarin.Android. Xamarin Designer 16.1.0.418 (remotes/origin/d16-1@5b958bb10) Visual Studio extension to enable Xamarin Designer tools in Visual Studio. Xamarin Templates 16.2.112 (4db4af4) Templates for building iOS, Android, and Windows apps with Xamarin and Xamarin.Forms. Xamarin.Android SDK 9.3.0.22 (HEAD/8e7764fdf) Xamarin.Android Reference Assemblies and MSBuild support. Mono: mono/mono/2018-08@3cb36842fc4 Java.Interop: xamarin/java.interop/d16-1@5ddc3e3 LibZipSharp: grendello/LibZipSharp/d16-1@44de300 LibZip: nih-at/libzip/rel-1-5-1@b95cf3f ProGuard: xamarin/proguard/master@905836d SQLite: xamarin/sqlite/3.27.1@8212a2d Xamarin.Android Tools: xamarin/xamarin-android-tools/d16-1@acabd26 Xamarin.iOS and Xamarin.Mac SDK 12.10.0.153 (750a879) Xamarin.iOS and Xamarin.Mac Reference Assemblies and MSBuild support.
auduchinok commented 5 years ago

What type do you expect it to be?

Happypig375 commented 5 years ago
> let x = {||};;
val x : {||} = {}
Happypig375 commented 5 years ago

For reference, this works.

> let x : {| x:int |} = {| x  = 3 |};;
val x : {|x : int|} = {x = 3;}
cartermp commented 5 years ago

I consider it a bug that {||} is allowed as a type, since you cannot specify an empty record type:

type R = {} // error FS0010: Unexpected symbol '}' in field declaration. Expected identifier or other token.

@dsyme thoughts?

Happypig375 commented 5 years ago

{} should not be allowed as a type because there would be no way to disambiguate type R1 = {} and type R2 = {}. However, all {||}s in an assembly are the same type and value. There is no ambiguity. Therefore, it should be allowed.

matthid commented 5 years ago

@Happypig375 Not sure about that argument. That argument holds for all records with the same fields. Regular "solutions" for this are type annotations and declaration order (as the compiler prefers the last declaration). Both would work for empty records just as well.

Therefore I'm with @cartermp: Either we don't allow it or we allow both...

Happypig375 commented 5 years ago

You can disambiguate regular records:

> type R1 = { x:int }
type R2 = { x:int }
{ R1.x = 3 };;
type R1 =
  {x: int;}
type R2 =
  {x: int;}
val it : R1 = {x = 3;}

Not possible with empty records.

matthid commented 5 years ago

@Happypig375 Ah, I wasn't even thinking about this particular syntax as it either makes you repeat the typename or adds some asymmetry which I don't like.

But the following would work:

type R1 = { x:int }
type R2 = { x:int }
{ x = 3 };;
//type R1 =
//  {x: int;}
//type R2 =
//  {x: int;}
//val it : R2 = {x = 3;}

let r1 : R1 = { x = 3 };;
//val r1 : R1 = {x = 3;}
({ x = 2 } : R1);;
//val it : R1 = {x = 2;}

So I hope this clarifies my last comment. What I mean is that you can always specify which one you want to use in any expression via type annotations. The (impossible) syntax you used is not needed to solve the ambiguity:

type R1 = { }
type R2 = { }
{ };; // R2
//type R1 = {}
//type R2 = {}
//val it : R2 = {}
let r1 : R1 = {};; // R1
//val r1 : R1 = {}
({ } : R1);; // R1
//val it : R1 = {}
cartermp commented 5 years ago

Just to clarify my point: Anonymous Records are intended to just be records without a name. This lack of symmetry feels more like a mistake than an intentional design point. I think that is the case, otherwise it would be allowable to specify {||} in a type signature.

dsyme commented 5 years ago

I consider it a bug that {||} is allowed as a type, since you cannot specify an empty record type:

I agree with this

cartermp commented 5 years ago

Okay. Resolution:

shanoaice commented 1 year ago

I personally feels that there are cases that empty record types are desirable, such as having a placeholder, or sometimes in JSON deserilization there are cases that a field should be an empty object ({}) instead of null or undefined.