views:

881

answers:

27

I've been reading and coding in F# as a hobby for a year now. As I prepare to deploy some code in production, I would like to hear what the SO community thinks could be improved in the language, and hopefully have the F# Team take some ideas from this thread and implement them in future releases.

One feature per answer would make it easier for the community to upvote you.

Edit: should I place my suggestions here or with the answers down there?

+17  A: 

In F#, I would change the syntax for tuple types, from the awful ML

'a * 'b

to the beautiful Haskell

('a,'b)

Similarly, I would rename 'unit' to '()'. ML got it ugly, and Haskell came along later and made it pretty. Alas, F# is an ML derivative and this will never change.

Brian
This would definitely add to readability and make the learning curve a little smaller for the language.
gradbot
I agree, though it's worth pointing out that the F#/ML syntax would actually be more intuitive to mathematicians. A tuple of sets is typically represented as A X B X C X D, for example, in set theory.
Noldorin
I'd rather add `float^3` to denote `float * float * float`.
Jon Harrop
+6  A: 

I would get rid of the ML-style generic types like

'a list
('key,'value) Dictionary

in favor of only having the .Net style type names like

list<'a>
Dictionary<'key,'value>

Alas, F# is an ML derivative and this will never change.

Brian
"Alas, F# is an ML derivative and this will never change." This makes me a sad panda.
gradbot
Why? "int option" "string array" -- those read quite nicely, don't they?
MichaelGG
@MichaelGG - because there should not needlessly be two equivalent ways to do the same thing. option<int> and array<string> read just as nice, and there's one fewer thing people would need to learn.
Brian
Hmmph. You guys been doing usability studies or something? ;)
MichaelGG
F# being a ML derivative is also good, since a lot of ML libraries ca be ported to F# easily.
Alexandru Nedelcu
Anyways, as a more serious response, I don't think having two equivalent ways is a bad thing. Sometimes, one might read easier than the other - it makes the language flexible. But I understand as a product, there are other costs and considerations involved, like overwhelming new users. Even so, I think F# is so much simpler than, say, VB or C#, so there should be plenty of room in F# for cute things like this...
MichaelGG
I guess my view point is a bit different. To me F# is a dot net language and I have no intention of using any OCaml code with it. Having both styles isn't a horrible thing though.
gradbot
A: 

I would make the let keyword optional when using #light syntax, meaning by default. It is perfectly understandable for me to write:

let squares = [1..5] |> Seq.map (fun x -> x * x)

As it is to write:

squares = [1..5] |> Seq.map (fun x -> x * x)

For a language that attempts to reduce the signal-to-noise ratio, I think we could do without let.

Edit: moved the fun suggestion into its own answer.

sker
We can't do without let because of ambiguity with the boolean equality test.
Ganesh Sittampalam
This answer makes you look like a pythonista...Really, I appreciate seeing where things are declared and the let keywords allows me to notice that at a glance. This would reduce the length as much as the readability, imo.
emaster70
This is really two suggestions, for let I disagree with you, but for lambdas it always strikes me as funny that F# is MORE verbose than C#.
Benjol
Please split into two answers. I'd like to upvote removal of fun (yes, I want to take the fun out of F#), but downvote removal of let.
James Hugard
I splitted the answer, as requested. @emaster70 you're correct. I took that idea from Python, Ruby, Haskell. Those languages seem to be doing alright when it comes to readability. I would like to see more of other languages and less of OCaml in F#. Sure OCaml is nice but that doesn't mean F# has to be constrained by its predecessor.
sker
As Ganesh points out, this doesn't work. let test x = let a = 1 a = xIs test unit or bool?
MichaelGG
A: 

I think this has been discussed before and it's probably not going to happen, but I have to ask for it anyway.

Implicit type conversion among compatible types, so that we are able to do:

let a = 3
let b = 4L
let c = a + b

..without having to cast it.

sker
That's one thing I actually love, having been bitten by sometimes magical implicit conversions in C#.
MichaelGG
+1, I like implicit conversions where they make sense.
missingfaktor
+1  A: 

I also would like to have something like Mixins. I often find myself typing Seq.xxx a lot of times and I always felt weird calling what look like static methods. With Mixins we could do:

import Seq

let oddSquares = [1..10] |> map (fun x -> x * x) |> filter (fun x -> x % 2 <> 0)

Which seems more functional in my opinion. And if we combine it with my other suggestions we'd get:

oddSquares = [1..10] |> map (x -> x * x) |> filter (x -> x % 2 <> 0)

That's less noise and more signal, no?

sker
That actually already works. Try it with "open Seq". However, in the latest CTP, this is now a warning, since Seq is marked to required qualified access. But in general, you can do this.
MichaelGG
That's nice, I hadn't thought of it. Too bad they're gonna get rid of it.
sker
It's only going to be a problem for specific modules marked that way. If the module isn't marked to require qualified access, you are still OK doing this.
MichaelGG
-1: You don't need mixins to open the `Seq` module and how will you distinguish between `fun` and `function` if you remove them?
Jon Harrop
A: 

Allow

let a = 3
    b = 4

instead of

let a = 3
let b = 4
Ganesh Sittampalam
Not needed, you can already write this: let a, b = 3, 4.
Juliet
That doesn't work well if you're defining functions, or if either definitions is quite long in itself. Also the values may not fit together logically.
Ganesh Sittampalam
I think that raises other ambiguities. I'd rather have "let .. and .." allowed on one line as OCaml does because it can be clearer than using a tuple.
Jon Harrop
+12  A: 

Remove the need to downcast to interfaces in order to access their members. For example allow


type Foo =
    abstract Lol : int -> int

type Bar() =
    interface Foo with
        member x.Lol i = i * i

let x = Bar()
x.Lol 0

That would improve .NET integration a lot and remove some tremendously ugly parenthesized downcast. Anyway don't know why but I've got this bad feeling that we're not gonna see this :(

emaster70
This is "implicit interface implementation". See fshub: http://cs.hubfs.net/forums/thread/6223.aspx -- Don Syme writes: "Implicit interface implementations have been requested and it is likely we'll support them in a future version of F#."
MichaelGG
I just hope it makes it into the version shipped with vs2010 final...
emaster70
+2  A: 

If the compiler could just be tweaked to infer thoughts as well as types...

Benjol
-1. If it did that, I'd have a lot of explaining to do to other people reading my "code".
MichaelGG
+9  A: 

Bind member selection more tightly than function application; e.g., this:

let x = "10"
printfn "%i" Int32.Parse( 10 )

rather than this:

let x = "10"
printfn "%i" (Int32.Parse( 10 ))
James Hugard
Yes Please, this drives me nuts to no end. It reminds me of Lisp.
gradbot
This is actually something explicitly put in, since without spaces after the member, parens are "high precedence application": http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec2.aspx#_Toc207785807 (Section 15.2). However, it then says that even though what you propose is allowed by the grammar, a "sanity check" prevents it from being legal. So, perhaps if enough people feedback, they'll remove the sanity check.
MichaelGG
Personally, I think the parentheses are clarifying, but on the other hand, I *like* LISP.
mquander
+12  A: 

I would make the fun keyword optional when using #light syntax, meaning by default. It is perfectly understandable for me to write:

let squares = [1..5] |> Seq.map (fun x -> x * x)

As it is to write:

let squares = [1..5] |> Seq.map (x -> x * x)

For a language that attempts to reduce the signal-to-noise ratio, I think we could do without fun.

sker
Doesn't that make it more complicated since it isn't obvious left to right what's going on?
MichaelGG
Maybe at the beginning, but as you get used to it, I think you would be able to spot a lambda immediately. C# seems to be doing fine without fun.
sker
No, I meant more complicated from the language spec standpoint. For example: "f x -> x * x". That's function application "f x", but, no wait, there's a ->, so we have to go back and make those patterns? Seems messy. (Disclaimer: I don't know anything about languages.)But, I'd be willing to bet that C#'s core language is more complicated than F#'s core language. (Perhaps not after all the F# .NET interop things are added in though.)
MichaelGG
I know little about languages but I'm willing to bet the only reason they keep the fun keyword is for compatibility with OCaml. Me, personally, I would get rid of it. I would remove brackets from range declaration and I would change the pipelining operator for a simple pipe. That plus my other suggestions would allow me to write: squares = 1..5 | map (x -> x * x) - Less noise, more signal, still understandable.
sker
Work work work - why do you want to take the fun out of programming?
Erik Forbes
Would that be equivalent to fun or function in general terms?
Jon Harrop
@MichaelGG: Scala seems to be doing fine without it. So can F#, I think.
missingfaktor
Same thing in Scala: `val squares = 1 to 5 map (x => x * x)`. High signal-to-noise ratio FTW! :-)
missingfaktor
+2  A: 

How about some default casting of literals by using an already inferred type.

Let mutable a = 1.0f
a <- 0

instead of

Let mutable a = 1.0f
a <- float32 0

The compiler could just throw a warning about possible lost of data even though in this case it's not a problem.

gradbot
What's wrong with "a <- 0.f"? I like F# being restrictive on hidden casting in general. Even though it can get annoying sometimes when not using ints, the safety is handy.
MichaelGG
Nothing is wrong with it. I was working on a XNA game which uses float32 for everything. It required a lot of magic numbers to get things working that's all. I'd just prefer it be a warning instead of an error.
gradbot
Maybe if somehow it was still opt-in and explicit? Perhaps a directive changing the default numeric literal to another integer type?
MichaelGG
+1, I like implicit conversions where they make sense.
missingfaktor
+1, I would like actual implicit casts, too, but on numeric types only.
Dan Finch
+9  A: 

I realize some of these aren't possible, but theres no harm in voicing some thoughts:

Statically-checked structural typing. For example, the function

let f x = x#someMethod()

Should accept any x so long as it has a method val someMethod : unit -> 'a.

Just one way to do it. I think in the beginning, OCaml compatibility was very important, leading to the development of a #light and non-light syntax, two mutually incompatible syntaxes for defining classes, and a bagillion different ways to define objects with generic parameters:

type 'a tree =
    | Node of 'a * 'a tree * 'a tree
    | Empty

type tree2<'a> =
    | Node of 'a * tree2<'a> * tree2<'a>
    | Empty

type tree3<'a when 'a :> System.IComparable<'a> > =
    | Node of 'a * tree3<'a> * tree3<'a>
    | Empty

type 'a tree4 when 'a :> System.IComparable<'a> =
    | Node of 'a * tree4<'a> * tree4<'a>
    | Empty

type ('a, 'b) tree5 =
    | Node of 'a * 'b * ('a, 'b) tree5
    | Empty

type tree6<'a, 'b> =
    | Node of 'a * 'b * tree6<'a, 'b>
    | Empty

type tree7<'a, 'b> when 'a :> System.IComparable<'a> and 'b :> System.IComparable<'b> =
    | Node

Now that #light is enabled by default and the F# library no longer cross-compiles with OCaml, we should consider OCaml compatibility no longer relevant. The language needs to be simplified.

Type signature should match type annotations: When I write a method like this:

let f (x : 'a :> #System.IComparable<'a>) = x

F# reports the type signature as val f : 'a -> 'a (requires 'a :> System.IComparable<'a>). I'd prefer to see the type signature reported exactly as written: val f : ('a :> #System.IComparable<'a>) -> 'a.

Intellisense should report variable names: Its hard to know what I'm calling when I can't see variable names. Case in point, F# lists the constructors for the StringBuilder class as follows:

val new : unit -> System.Text.StringBuilder
val new : int -> System.Text.StringBuilder
val new : string -> System.Text.StringBuilder
val new : string * int -> System.Text.StringBuilder
val new : string * int * int * int -> System.Text.StringBuilder
val new : int * int -> System.Text.StringBuilder

I've created a bagillion StringBuilders and know how to use most of these, but I keep having to go back to MSDN to figure out what exactly I'm passing in. I've figured out how to get constructor parameters by writing System.Text.StringBuilder;; into fsi, which gives me this output:

stdin(1,1): error FS0191: Invalid use of a type name and/or object constructor. If necessary use 'new' and apply the constructor to its arguments, e.g. 'new Type(args)'. Overloads are:  
    System.Text.StringBuilder() : unit
    System.Text.StringBuilder(capacity: int) : unit
    System.Text.StringBuilder(value: string) : unit
    System.Text.StringBuilder(value: string, capacity: int) : unit
    System.Text.StringBuilder(capacity: int, maxCapacity: int) : unit
    System.Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : unit

Barring changes to Intellisense, I'd just like some way to quickly expore type signatures. C# has an interesting feature where I can press F12 on any class, like the StringBuilder class, and it'll pull up a window with the class's complete public signature similar to what fsi shows above. The object browser works for now, but its too slow and doesn't always show me all the information I'm interested in seeing (i.e. it doesn't show me an object's base class).

Functional alternatives to existing libraries: this probably isn't the responsibility of the F# team, maybe it should be an open source project, but the .NET libraries don't make functional programming very easy. Everything in System.Data, the Microsoft Enterprise Library, and some third-party libraries like NHibernate rely heavily on mutable state.

Juliet
"Statically-checked structural typing." This exists in some form with inline functions and statically resolved type variables. See: http://www.atrevido.net/blog/2008/08/31/Statically+Typed+Duck+Typing+In+F.aspx
MichaelGG
If you break these up into separate answers, I might vote for some.
Brian
+4  A: 

Remove the C style generic type parameter names from the F# library. 'T 'U 'T1 'T2? 'a and 'b are more readable and fit in with existing literature.

MichaelGG
I'd rather they used the Greek symbols alpha, beta and did proper type setting.
Jon Harrop
+5  A: 

Allow \ and . for lambdas instead of fun ->:

let id = \x.x

Instead of:

let id = fun x -> x
MichaelGG
That's something I would use, specially with functions like sortBy where sometimes all I need is x.
sker
Why not just typeset "fun x" as "lambda x" as OCaml does?
Jon Harrop
+1  A: 

Allow cross-file mutually recursive type definitions, or cross-file intrinsic type extensions.

MichaelGG
+11  A: 

Allow user code to use the same things the F# library can, like static optimizations:

> let inline map (f: ^a -> ^b) (xs: ^c) =
-   Seq.map
-   when ^c : ^b list = List.map
-   when ^c : ^b array = Array.map;;

    when ^c : ^b list = List.map
  -------^^^^^^^^^^^^

stdin(40,8): error FS0191: Static optimization conditionals are only for use within the F# library
MichaelGG
What exactly does that do?
Jon Harrop
Static optimization conditionals allow you emit different code for different types. So in this case, I could use "map" on any sequence, but if it's an array or list, I'd get code emitted for those specific versions. It's used in the F# core libs as an easy way to get fast code while still keeping the generic signatures.
MichaelGG
+12  A: 

No one has said typeclasses yet?

MichaelGG
I'm surprised too! :)
Brian
Yeah, Haskell is *so* much better than ML/F# just because of this.
Zifre
Why would you want type classes?
Jon Harrop
+1, This is the number one feature I'd like to see in F#. :-)
missingfaktor
@missingfaktor: Why?
Jon Harrop
+10  A: 

The ability to access protected members in lambdas. I can't figure out why it's a restriction, but it's annoying:

> type TestEx() =
-   inherit System.Data.DataException()
-   member x.ShowHR() = printfn "%A" x.HResult // This is fine
-   member x.Oops = fun () -> x.HResult // No? Why?
- ;;

    member x.Oops = fun () -> x.HResult // No? Why?
  ----------------------------^^^^^^^^^

stdin(19,29): error FS0191: The member or object constructor 'HResult' is not accessible. Private members may only be accessed from within the declaring type. Protected members may only be accessed from an extending type and may not be accessed from inner lambda expressions
MichaelGG
The restriction is because the inner lambda will be implemented by a separate class.
Ganesh Sittampalam
So, why isn't the closure created as an inner class? Then it'd have access to private and protected. (And perhaps that'd let private members be emitted as private, not assembly?)
MichaelGG
the c# compiler goes to the trouble of emitting the required closure classes as inner classes to allow this. I do find it annoying when the f# compiler does not.
ShuggyCoUk
+8  A: 

Allow automatic upcast to return type, if specified. I know I might sound like a hypocrite given that I usually don't like automatic conversions, but I think if the type of the expression is explicitly specified with #, it'd be ok.

I don't want to have to do this:

let getEx n : Exception = 
  match n with 
  | 1 -> upcast OutOfMemoryException()
  | _ -> upcast ArgumentException();;

I want this to work:

> let getEx n : #Exception =
-   match n with
-   | 1 -> OutOfMemoryException()
-   | _ -> ArgumentException();;

    | 1 -> OutOfMemoryException()
  ---------^^^^^^^^^^^^^^^^^^^^^^

stdin(37,10): warning FS0064: This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near 'stdin(35,14)-(35,24)' has been constrained to be type 'OutOfMemoryException'.

    | _ -> ArgumentException();;
  ---------^^^^^^^^^^^^^^^^^^^

stdin(38,10): error FS0001: This expression has type
        ArgumentException
but is here used with type
        OutOfMemoryException
MichaelGG
+2  A: 

OK, not a F# language feature, but having IntelliSense in ASPX pages would change my year.

MichaelGG
+9  A: 

Add shorthand syntax for (fun x -> x.Foo)

Ganesh Sittampalam
Yea, it'd be nice to somehow be able to refer to instance methods without an instance.
MichaelGG
+1, something like `(_.Foo)` would be very handy.
missingfaktor
+1  A: 

eval

F# already has eval.
Jon Harrop
+3  A: 

Partial Classes.

First step in making a GUI builder :-)

James Hugard
with a gui builder, i'd never use any other .net language :)
dde
+1  A: 

Proper tools for non-#light syntax, like autoindentation.

Jon Harrop
+5  A: 

Typesetting and visualization both in the source editor in Visual Studio and in interactive sessions. I want typeset maths and graphics in my code, comments and produced as output. Also, I want structured source code with collapsible headings and subheadings.

Jon Harrop
+1  A: 

Better support for lexing and parsing

Firstly, F# should catch up with where OCaml already was:

  • fslex and fsyacc should have integrated VS support as the equivalent OCaml tools do with OCaml editors like Emacs.

  • fslex should support named subexpressions as ocamllex does, so you can match '"' ([^"]* as s) '"' and have s bound for you efficiently without having to do it inefficiently by hand using a separate regular expression.

Secondly, F# should take advantage of .NET to leapfrog the alternatives:

  • These tools should all be available at run-time, generating and compiling CIL code on-the-fly like Regex does.
Jon Harrop
A: 

"use" bindings in implicit class constructors which would cause the class to automatically implement IDisposable with calls to .Dispose() for each "use" binding

Dan Finch
Please elaborate. :|
missingfaktor
missingfaktor - I reworded it, it was pretty unclear, thanks
Dan Finch