fslang-suggestions icon indicating copy to clipboard operation
fslang-suggestions copied to clipboard

Allow type definitions at expression scope

Open baronfel opened this issue 9 years ago • 7 comments

Submitted by Gustavo Guerra on 3/21/2014 12:00:00 AM
36 votes on UserVoice prior to migration

The suggestion is to allow "type X = ..." in an expression, e.g.

let f () =
  type X = A | B
  let x = (A,A)
  let y = (B,B)
  if (x=y) then true else false

Original UserVoice Submission Archived Uservoice Comments

baronfel avatar Oct 20 '16 01:10 baronfel

This is a generalization of https://github.com/fsharp/fslang-suggestions/issues/277 .

charlesroddie avatar Aug 01 '19 17:08 charlesroddie

@dsyme Now that #277 has been approved in principle, any chance that this can be approved as well?

Happypig375 avatar Feb 07 '21 17:02 Happypig375

Presumably the scoping here is purely about reducing accessibility, and that these types would be "static" and not have access to variables in the scope. E.g. this would not be allowed:

let y(x:int) =
    type T() =
        member _.X = x // Error: x is not defined
     T().X

This issue is then one of several that are about allowing static things to be defined in smaller scopes:

In all these suggestions, things defined in expression scope could be treated as if defined as private things at a higher level in the same file (the nearest module or namespace). (Except that F# reduces their access to the smaller expression scope rather than the larger private scope.)

I assume it would be OK to keep the type name the same, and give an error there is a name conflict.

module M

// If at least 2 of the three A definitions are present, there is a `duplicate definition of type A`

type A() = class end
let x =
    type A() = class end
    ()
let y =
    type A() = class end
    ()

Alternatively the name could be automatically appended to to give uniqueness.

charlesroddie avatar Feb 07 '21 17:02 charlesroddie

Alternatively the name could be automatically appended to to give uniqueness.

This will be problematic because once the outer type is shadowed, you cannot access it anymore because expression is not module/namespace

yatli avatar Feb 07 '21 17:02 yatli

Having nested types inside of F# OOP types would be useful to have as well. It will allow to minimize use of Anonimous Record types and tuples as result of private functions. Example

type External() = **type IntegmediateResult= { A: int; B: int } **let someInternalFunction(): IntegmediateResult = failwith "TODO" **member _.Foo() = ****let i = someInternalFunction(); ****i.A + i.B

I think that IntegmediateResult should be compiled as C# nested type.

oleksandr-bilyk avatar Jun 10 '22 13:06 oleksandr-bilyk

@oleksandr-bilyk this example looks like a worse alternative to tuples with named fields https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names. Also I don't see what's wrong with anonymous records for such case, even allocations doesn't matter because records can be structs

jl0pd avatar Jun 10 '22 15:06 jl0pd

@jl0pd will reply to your comments

@oleksandr-bilyk this example looks like a worse alternative to tuples with named fields https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names. Also I don't see what's wrong with anonymous records for such case, even allocations doesn't matter because records can be structs

  1. I provided oversimplified sample. In my real code record type was more complicated.
  2. I also think that F# should support Tuple Field Names but in my case I needed complex internal structure and wanted record type.
  3. Complex Anonimous record types when they passed through few private members are not readable well. If forces us to declare type outside of OOP type.
  4. Ther are nested types in C#. According to .NET Framework Guidelines they may be result of public functions. I wish it be supported by F# as well.

oleksandr-bilyk avatar Jun 13 '22 06:06 oleksandr-bilyk