F# Language

This User Voice was for suggestions about the future evolution of the F# Language and Core Library.

I suggest we ...

You've used all your votes and won't be able to post a new idea, but you can still search and comment on existing ideas.

There are two ways to get more votes:

  • When an admin closes an idea you've voted on, you'll get your votes back from that idea.
  • You can remove your votes from an open idea you support.
  • To see ideas you have already voted on, select the "My feedback" filter and select "My open ideas".
(thinking…)

Enter your idea and we'll search to see if someone has already suggested it.

If a similar idea already exists, you can support and comment on it.

If it doesn't exist, you can post your idea so others can support it.

Enter your idea and we'll search to see if someone has already suggested it.

  1. easier interop with FSharp.Core from other .net languages

    A first example is FSharpOption<T> and it's usage in C#/VB, extend FSharp.Core with this module to make it easier to use:

    http://fssnip.net/so

    There are other efforts to be made to make FSharp.Core look not too alien in other languages, like being able to await on FSharpAsync.

    Having design guidelines for this type of interop doesn't remove the need for basic F# constructs to be exposed in a convenient way to other dotnet languages.

    11 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →
  2. 15 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    1 comment  ·  Flag idea as inappropriate…  ·  Admin →
  3. DefaultValue attribute should require 'mutable' when used in classes and records

    For structs, adding the DefaultValue attribute to a val declaration (a field) results in a check that the field is mutable, since it doesn't make sense to use an immutable field which only ever has the default value.

    [<Struct>]
    type S() =
    [<DefaultValue>] val x : C

    AssemblyReader.fs(4304,23): error FS0880: Uninitialized 'val' fields must be mutable and marked with the '[<DefaultValue>]' attribute. Consider using a 'let' binding instead of a 'val' field.

    For some reason, this condition is only checked for fields declared in structs. We should likewise give a warning for records and classes:

    type C() =
    [<DefaultValue>] val…

    1 vote
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    0 comments  ·  Flag idea as inappropriate…  ·  Admin →

    Approved for inclusion in a future release of F#.

    Please consider contributing to F# by providing an implementation, with adequate testing. The code to be adjusted is around here: https://github.com/fsharp/fsharp/blob/212c3359bf6d83e30c12e53fd2ef283d3257b328/src/fsharp/PostInferenceChecks.fs#L1422. For some reason, the code is only activated for struct/enum type definitions, but it should also apply to record and class type definitions.

    Don Syme, F# Language Evolution

  4. Have String.ofChars in the standard library

    Wouldn't it be convenient if String.ofChars function was part of the standard library? The implementation is quite straightforward:

    module String =
    let ofSeq (seq: char seq) =
    seq |> (System.Text.StringBuilder() |> Seq.fold (fun sb c -> sb.Append(c))) |> string

    Currently, we have to use an inefficient chaining to convert a seq of characters to a string:

    ['a'; 'b'; 'c'] |> Array.ofList<char> |> System.String

    which can be simplified with String.ofChars:

    ['a'; 'b'; 'c'] |> String.ofChars

    String.ofSeq also can be handy for profiles like PCL where the String type is not regarded as seq<char>.

    10 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    1 comment  ·  Flag idea as inappropriate…  ·  Admin →
  5. Add System.Collections.ICollection implementations to F# list/set/map

    ICollection is a useful interface that inherits IEnumerable. Inputs implementing ICollection are utilized by libraries such as LINQ, Nessos.Streams and MBrace in order to optimize partitioning of data. Curiously, none of the F# collections (list/set/map) implement ICollection (however the latter two do implement ICollection<T>, which is a different interface designed for mutable collections). It is currently impossible to extract the length/count of a boxed list/map without performing some sort of reflection, which is not always desirable.

    I suggest that future versions of F# core implement this.

    See also https://github.com/Microsoft/visualfsharp/issues/570#issuecomment-128000307

    10 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    7 comments  ·  Flag idea as inappropriate…  ·  Admin →
  6. Add "complement" and "logicalNot" operators to resolve the confusion with "~~~"

    When used on user-defined types, the ~~~ operator resolves to op_LogicalNot rather than the expected op_OnesComplement. See https://github.com/Microsoft/visualfsharp/issues/457#issuecomment-104900399 for a workaround.

    This should be fixed FSharp.Core. An attempt to fix this transparently failed, see https://github.com/Microsoft/visualfsharp/pull/458.

    A plan to address this going forward is at https://github.com/Microsoft/visualfsharp/pull/458#issuecomment-127711336 but will require an update to FSharp.Core.

    1 vote
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    0 comments  ·  Flag idea as inappropriate…  ·  Admin →
  7. Relax some of the indentation rules

    Currently, the general rules for indentation in F# is that the code on the next line should be indented further than the thing that determines its starting point on the previous line.

    There are a number of cases where this quite annoyingly means that you have to indent things very far (or, to avoid that, add lots of unnecessary line breaks). One example is when you have nesting in a method call. For example:

    Chart.Geo(growth)
    |> Chart.WithOptions(Options(colorAxis=ColorAxis(values=[| -100;0;100;200;1000 |], colors=[| "#77D53D";"#D1C855";"#E8A958";"#EA4C41";"#930700" |])))

    Now, there is almost no way to make this code snippet look decent. I would want to write…

    55 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    7 comments  ·  Flag idea as inappropriate…  ·  Admin →
  8. 1 vote
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    0 comments  ·  Flag idea as inappropriate…  ·  Admin →
  9. Add atom syntax

    Add a syntax for atoms a.k.a. keywords a.k.a. self-evaluating symbols.

    :foo (or 'foo, or #foo - whichever causes the least problems)

    Where foo otherwise follows the rules for an identifier, is simply turned into "foo" at compile time. (It would also be nice of '-' were also added to the list of allowable characters for the atom.)

    E.g.:

    ("fido", :is-a, :pet) is of type string*string*string

    4 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    3 comments  ·  Flag idea as inappropriate…  ·  Admin →
  10. Generic `TypeWithMeasure` and `TypeWithoutMeasure` functions for adding units of measure on any type

    Currently, the Core.LanguagePrimitives module only provides the following functions to add units of measure to decimal, double (= float) and single (=float32), respectively:
    DecimalWithMeasure : decimal -> decimal<'u>
    Float32WithMeasure : float -> float<'u>
    FloatWithMeasure : float32 -> float32<'u>

    and the Core.Operators module overloaded functions for removing units of measure on decimal, double (= float) and single (=float32):
    decimal : ^T -> decimal
    float : ^T -> float
    float32 : ^T -> float32

    Even though all the other primitive numeric types are also annotated with [<MeasureAnnotatedAbbreviation>], you cannot use them to carry a unit of measure, because the needed conversion functions…

    2 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    4 comments  ·  Flag idea as inappropriate…  ·  Admin →
  11. Allow quotations of expressions used as Type Provider arguments

    This would enable scenarios where the type provider can rewrite, or abstract at compile time F# code and emit new one.
    It could replace a macro system without the need for new syntax or tooling.

    30 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    3 comments  ·  Flag idea as inappropriate…  ·  Admin →
  12. Extend custom numeric types to support floating point literals

    Currently you can define custom numeric types for integer values.
    It would be nice to be able to handle floating point values too, e.g.

    type complex = Complex of double * double
    type imaginery = Imaginery of double
    with
    static member (+) (lhs:double,Imaginery(rhs)) = Complex(lhs,rhs)

    module NumericLiteralI =
    let FromZero () = Imaginery 0.0
    let FromOne () = Imaginery 1.0
    let FromInt32 (x) = Imaginery (double x)
    let FromInt64 (x:int64) = Imaginery (double x)
    let FromDouble (x:float) = Imaginery x // extension

    let polar = 1.5 + 2.5I

    13 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    3 comments  ·  Flag idea as inappropriate…  ·  Admin →
  13. Make call syntax simpler for statically resolved member constraints

    The idea is basically explained by the following code:

    // this works as expected
    type Example() =
    member __.F(i) = printfn "%d" i

    let inline f (x : ^a) =
    (^a : (member F : int -> unit) (x, 1))
    (^a : (member F : int -> unit) (x, 2))

    f (Example())

    // this doesn't work
    let inline f (x : ^a when ^a : (member F : int -> unit)) =
    // the compiler knows that there must be member F and its signature
    // so the following should be possible
    x.F(1)
    x.F(2)

    You can also view this code…

    29 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →
  14. 3 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →

    This is approved for inclusion in a future release of F# subject to an implementation and detailed design. A pull request

    to implement this feature will be necessary and we encourage contributors to submit one with adequate design detail and

    testing to http://github.com/Microsoft/visualfsharp. Discussion of the particular version for this to be included in can

    be made once an implementation is available.

    Design detail can also be discussed below.

    Don Syme, F# Language and Core Library Evolution.

  15. Make the result of the dict function implement IDictionary

    The fact that the type returned by dict doesn't implement IDictionary makes reflection difficult and inefficient, it also make this type different than all other .NET Core Dictionary implementations

    21 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →
  16. Allow access modifies to auto properties getters and setters

    member val Property = 10 with get, private set

    30 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    1 comment  ·  Flag idea as inappropriate…  ·  Admin →
  17. Override `ToString` for discriminated unions and records

    It's a pain and dirty to add `override x.ToString() = sprintf "%A" x` to every type in order to make `String.Format()` happy:

    type T1 =
    { Id: int
    Version: string }
    override x.ToString() = sprintf "%A" x

    type DU =
    | C1 of int
    | C2
    override x.ToString() = sprintf "%A" x

    I think it's very easy to teach the compiler generate this override automatically for all user types.

    181 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    4 comments  ·  Flag idea as inappropriate…  ·  Admin →
  18. Cartesian product function for collections

    It's often useful to compute the Cartesian product (cross join) of two collections. I always end up writing something like this:

    let cross xs ys =
    seq {
    for x in xs do
    for y in ys ->
    x, y
    }

    I think it would be useful to have this in the standard collection modules.

    8 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →
  19. Please make a correct ‘modulus’ for F#

    Please fix a bug in the F# compiler. Everybody needs correct values for negative numbers. The algorithm calculating a modulo should respect the existing math rules. For example, 11 % 5 = 1 (correct), but -11 % 5 is -1 (bug), should be 4 (correct value).
    For instance, m mod n: for integers m and n, m mod n is the integer for which 0 <= r < n and m-r is a multiple of n. For example, 11 mod 5 = 1, and -11 mod 5 = 4.
    Details of discussion and suggested corrections to the algorithm of the…

    13 votes
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    5 comments  ·  Flag idea as inappropriate…  ·  Admin →
  20. Record type inference suggestion

    Note that if the record {a: int; b: int} is declared last, then type + argument name type inference will not work. Switching the order around resolves the issue. However, this behaviour can be a little mentally jarring.

    The lightweight syntax is very useful. It'd be great to strengthen it up.

    module Eg1 =
    type AB = {a: int; b: int}
    type A = {a: int}
    type B = {b: int}
    let a = {a=1}
    let b = {b=2}
    let ab = {a=1;b=2}

    module Eg2 =
    type A = {a: int}
    type B = {b: int}
    type AB = {a:…

    1 vote
    Vote
    Sign in
    (thinking…)
    Sign in with: facebook google
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    2 comments  ·  Flag idea as inappropriate…  ·  Admin →

F# Language

Feedback and Knowledge Base