My wish-list for F#
By Laurent Le Brun on Tuesday, September 23 2008, 00:35 - [EN] F# articles - Permalink
Some suggestions to improve F#
F# is already a great language. The CTP version was a big step for F#, but, of course, there's still room for improvement. Here are some of my wishes.
Compile-time meta-programming
With CTP, quotation library has been improved. It's a very nice way to manipulate code at run time, evaluate it or compile it. I really enjoy this feature, which reminds me some Lisp (and the ' operator), but I wish I could do this code manipulation at compile-time. This would make meta-programming much easier, it would improve performances. I guess it's possible to find many uses of this feature. Lisp, OCaml (with camlp4) already have similar features.
Suggestion: First, the user writes a transformation function, with type Quotations.Expr<'a> -> Quotations.Expr<'b>. Then, he compiles it into a library. Finally, he tells the compiler to call his function on the source code.
With attributes, it would be possible to call transformation functions on a particular piece of code.
[fsharp] [<Transform("trace")>] let foo x = // ...
The "trace" function would be called to transform the foo function at compile-time.
I also believe such a feature would allow a stream fusion implementation.
Remove type annotations for objects, when possible
The following function is valid:
[fsharp] let f x = x + x
The type of x is determined by the context (i.e. how the function is called in the source code). I wish the following function was accepted as well:
[fsharp] let f' x = x.Length
Unfortunately, it requires explicit type annotations. It's the same behavior for inlined versions:
[fsharp] let inline g x = x + x let inline g' x = x.Length
Only g is accepted by the compiler. The generic solution for this uses an ugly syntax (see FSharp.Core/prim-types.fs for details).
Automatic code duplication
[fsharp] let f x = x + x
This function accepts either an int argument, or a float (or other types), but not both. Thus, this gives a type error:
[fsharp] f 2, f 2.3
A workaround is to duplicate the f function:
[fsharp] let f x = x + x let f' x = x + x f 2, f' 2.3
I wish the compiler duplicated the code itself, as C++ templates do. Of course, it's possible to use the "inline" keyword, but it's not always a good thing: it duplicates the code on every call, and some functions can't be inlined (e.g. recursive functions).
String interpolation
F# is often compared to scripting languages. String interpolation is a feature found in most dynamic languages. In most Unix shells, Perl and PHP, the syntax is:
"x = $x" or "x = ${x}"
In Ruby, it is:
"x = #{x}"
For backward-compatibility, string interpolation should be exlicit in F#. I suggest we add a $ prefix to strings with interpolation. For instance:
[fsharp] print $"x = $x" print $"1 + 2 = ${1+2}"
By the way, I'd like to see a "print" function in F# standard library: let print = print_any.
This feature is not very hard to implement, it can be syntactic sugar (which calls fprintf). I'm surprised most compiled languages don't have this feature.
Binary/unary operators
Some operators can be used only prefix, e.g. (!), (!!), (?+). Some operators can be used only infix, e.g. (+), (--), (%%).
Why not allow every operators to be used both infix and prefix? I'd like !!2 to be parsed as (!!) 2, and 4 !! 5 to be parsed as (!!) 4 5.
Operators precedence
Precedence rules are quite hard to master and not very flexible. For instance, I'm always confused with the sformat library: how is parsed the expression a $$ b -- c @@ d ++ e?
Some languages (e.g. Haskell) allow the user to change operators precedence. I'd like to see this in F# too.
Duck typing
It's possible to have duck typing in F#, but the syntax isn't pretty. We often don't need duck typing in F#, thanks to the great type system, but I believe it would be sometimes nice to have it. Boo language is a statically typed language, but it has syntactic sugar for duck typing. Have a look at this page for details: http://boo.codehaus.org/Duck+Typing
Extension methods
Extension methods are great, but they sometimes don't work.
[fsharp] let inline f x = x + x
f works for any type with a (+) operator. If you define a new type with a (+) operator, it will work with f. But, if you use extension methods to add a (+) operator to an existing type, f doesn't accept it.
[fsharp] type A() = static member (+) (x:A, y:A) = 3 type System.Boolean with static member (+) (x:bool, y:bool) = 3
Then, A() + A() is allowed, but true + true isn't.
FSI
I also have a few ideas to improve FSI (improve history, see the list of bindings, etc.). As FSI is somewhat outside the compiler, it seems to be quite easy to modify and improve it. I might give a try (feel free to send me suggestions for a better FSI).
Comments
All your suggestions seem really cool for me. I would add to your "Binary/unary operators" the ability of using any function as an infix operator. That's something that i really miss from Haskell.