Allow methods with the same name as DU counstructor when different parameters #532

Open
xperiandri opened this Issue Feb 5, 2017 · 3 comments

Projects

None yet

3 participants

@xperiandri
xperiandri commented Feb 5, 2017 edited

Allow to define a static method on DU which name is New<DU case name> and a set of parameters is different from the set of values of the DU case with name <DU case name>.

Sometimes DU case has composite values which can be set one by one or separately.
Imagine a ventilation channel which part has length and profile where profile has width and height.

type ElementDimensions =
    | Inherited of Length : Dimension
    | TypeSizeDimensions of TypeSize : TypeSize * Length : Dimension
    | CustomDimensions of Profile : Profile * Lenght : Dimension

So the last case can be constructed from profile and length or form width, height and length.
But neither this

type ElementDimensions =
    | Inherited of Length : Dimension
    | TypeSizeDimensions of TypeSize : TypeSize * Length : Dimension
    | CustomDimensions of Profile : Profile * Lenght : Dimension
    static member NewCustomDimensions (width, height, length) =
        CustomDimensions(Profile(width, height), length)

Nor this does work

    static member CustomDimensions (width, height, length) =
        CustomDimensions(Profile(width, height), length)

Pros and Cons

As this is a construction of the same DU case it would be natural to overload this method especially for consumer from C#. However be able to use NewCustomDimensions (width, height, length) the same way as CustomDimensions (Profile, Length) would be nice in F#.
Anyway DU case constructor is tupled.

Extra informtion

Estimated cost (XS, S, M, L, XL, XXL): I guess it is S, I don't have enough knowledge to estimate.

Original issue

Affadavit (must be submitted)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I would be willing to help implement and/or test this
  • I or my company would be willing to help crowdfund F# Software Foundation members to work on this
@theprash
theprash commented Feb 5, 2017

It seems common - in the F# core library at least - to have a module and a type with the same name, where the module contains functions for transforming or creating the type. Although you do need to know the magic module attribute that make this possible without causing a duplicate definition error: [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]

This way I believe you can achieve the API that you're looking for:

type ElementDimensions =
    | Inherited of Length : Dimension
    | TypeSizeDimensions of TypeSize : TypeSize * Length : Dimension
    | CustomDimensions of Profile : Profile * Length : Dimension

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<RequireQualifiedAccess>]
module ElementDimensions =
    let newCustomDimensions width height length = CustomDimensions(Profile(width, height), length)

ElementDimensions.newCustomDimensions 1 2 3

Note the RequireQualifiedAccess attribute, which just means the module cannot be opened and you must always prefix with the module name, as if it were a class.

An existing example of this pattern is the List type, which has a corresponding List module with constructor functions like List.empty and List.singleton.

So I believe this pattern already provides what you ask for but it has some downsides:

  • The extra module definition is quite verbose and unpleasant to look at with those attributes.
  • Module functions cannot have overloads like methods can. Of course, this also has the benefit of enforcing that they always work will with type inference when passed around as values.

An alternative language feature request might be to have a keyword to put after module that just adds those two attributes. I think it would make this pattern more discoverable and legitimise it as idiomatic F#, because right now I think it looks and feels a bit like a hack.

@cartermp
Member
cartermp commented Feb 6, 2017

@theprash Note that in F# 4.1, [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] is now implicit.

@xperiandri

It is an option for F# consumers, but for C# there are no options now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment