IronJS Blog

All things IronJS and F#

Why not F#?

with 10 comments

This post is a response to a discussion that was held on twitter as a result of me stating that the next version of IronJS will be completely in C# and not contain any F# code at all. Before I explain my exact reasoning behind this I want to state that I find F# to be an excellent language and it’s a joy to work in, Don Syme & Co have done an excellent job with it. If you have followed IronJS over the past two years it should be clear that I have tried really hard to make it work in F#, but as a firm believer in “the right tool for the job” I’ve come to the conclusion that it’s not a feasible to build a high performance dynamic language on top of F# while staying true to F# itself.

The key statement here really is “while staying true to F# itself”, as both F# and C# run on top of .NET they have (at least in theory) the exact same performance characteristics. F# has access to the same things as C# in terms of .NET “low level” features such as native objects, mutability, structs, p/invoke calls, etc. It even adds a couple of things on top of this that C# doesn’t have such as the inline keyword and the ability to mix code and IL instructions inside source files.

But F# adds so much more on top of C#, such as: Immutable ValuesComputation Expressions, Pattern Matching, Discriminated Unions, Fast Native Functions, etc. – this list can be made very long. But if you inspect all of these features, you will notice that they are all abstractions on top of the existing .NET functionality (or a compiler feature in the case of immutable values). While these abstractions allow you to write very elegant and concise code, they are often slow. For example with computation expressions and discriminated unions you can create a very elegant AST definition and an equally gorgeous parser, as demonstrated by Matthew Manela.

But again, the parser will be very slow compared to one that is written in hand optimized C#. The AST is immutable, so when you want to optimize a node far down in the AST you need to throw away and re-create every parent node to it, this ends up being slow also due to the overhead of the garbage collector and heap allocations. Now, if you don’t need the absolute best performance you could squeeze out of .NET (which most don’t) then all of these constructs will give you clear code, that is easy to understand and reason about. But in the case of IronJS not being able to squeeze all the performance out of the .NET run-time is not an option.

A lot of people will now say something like: “But F# supports mutability, native .NET objects, etc”. Yes, it does. But the “mutability/imperative/OO” features in F# are hamstrung by several crippling issues, and what you end up with is what feels like a slightly crappier version of C#. Just to mention a few things (there are a lot more, both major and minor):

  • Mutually Recursive types must be defined in the same file, so when two classes need to be able to refer to each other they need to be right next to each other in the source also (I understand why F# works like this). This is fine for a few grouped objects, but when you end up with a 2.5k line behemoth that used to be Runtime.fs (open at your own risk) inside IronJS, it’s not fun any more. Whenever I bring this up in ##fsharp on irc.freenode.net everyone tells me that this is due to bad library design. While there are several ways to break apart two classes from each other, they all end up being slow compared to just directly accessing a field on the object instance. Maybe it’s fast enough for you, but it’s not fast enough for IronJS.
  • Constructors if used incorrectly will cause a run-time over head on every method call, to verify that the class has been initialized properly. There are also multiple other problems with initializing an instance inside both the implicit and explicit constructor syntax.
  • Interfaces can only be implemented explicitly, so you end up having to cast your objects into their interface type constantly.
  • Collections the specialized F# collections are slow compared to their BCL counter-parts, especially Map<K, V> compared to Dictionary<K, V>, while I understand why they are slower, it doesn’t change the fact that they are.

Again, a lot of people will say something like: “All things you said is true, but just use mutability where you need it for speed and stick to immutability everywhere else”. Oh, how I wish it was this simple. The problem with mutability is that once you let a little of is get in, it spreads like a wild-fire through out your code. Even if a piece of code itself looks immutable, if it ends up depending on something deep down in your code base that uses mutability, then it is by definition is not immutable any more.

In conclusion: I love F#, it’s a great language and if you can stick to what it does really well (immutability and FP) it’s a joy to work in. But as soon as the mutability starts creeping it’s way into the code  due to performance reasons (which is exactly what happened to IronJS) then it falls apart very fast and you end up with code that is hamstrung by several crippling issues and that is very hard to follow due to a bunch of quirks in the F# syntax. I usually describe F# OO as a bad version of C#.

Advertisement

Written by Fredrik Holmström

April 19, 2012 at 8:36 am

Posted in IronJS

10 Responses

Subscribe to comments with RSS.

  1. Maybe you should consider Nemerle: http://nemerle.org/About/

    Serv

    April 22, 2012 at 12:27 pm

    • F# syntax is ugly. Nemerle is better and more powerful than F#

      ReverseBlade

      July 2, 2012 at 7:04 am

    • 1) “Mutually Recursive types must be defined in the same file, so when two classes need to be able to refer to each other they need to be right next to each other in the source also (I understand why F# works like this). This is fine for a few grouped objects, but when you end up with a 2.5k line behemoth that used to be Runtime.fs (open at your own risk) inside IronJS, it’s not fun any more. Whenever I bring this up in ##fsharp on
      [19 Apr 12 09:55] * Associat0r: irc.freenode.net everyone tells me that this is due to bad library design. While there are several ways to break apart two classes from each other, they all end up being slow compared to just directly accessing a field on the object instance. Maybe it’s fast enough for you, but it’s not fast enough for IronJS.”

      Abstractions on the wrong level, your perf sensitive stuff shouldn’ be on that recursive boundary anyway.

      2) “Constructors if used incorrectly will cause a run-time over head on every method call, to verify that the class has been initialized properly. There are also multiple other problems with initializing an instance inside both the implicit and explicit constructor syntax.”

      Just don’t use “this” in the ctor of transient objects which is good practice anyway and you don’t have this problem,
      if you use “this” in a ctor F# does a runtime check to ensure the object is fully initialized before accesing it, for example calling a virtual member in a ctor in C# will silently fail, but F# will give you a runtime error.
      See here http://www.bluebytesoftware.com/blog/2010/06/27/OnPartiallyconstructedObjects.aspx and here http://msdn.microsoft.com/en-us/library/ms229060.aspx

      3) “Interfaces can only be implemented explicitly, so you end up having to cast your objects into their interface type constantly.”

      You haven’t abstracted your code properly if you have to cast all the time.

      4) “Collections the specialized F# collections are slow compared to their BCL counter-parts, especially Map compared to Dictionary, while I understand why they are slower, it doesn’t change the fact that they are.”

      Those so called specialized F# collections are persistent data-structures, F# is a general purpose multi-paradigm .NET language and there is nothing non-F# about using Dictionary
      it is also used for examples in the Expert F# book.
      In short, use the right data-structure for your problem.

      5) “Again, a lot of people will say something like: “All things you said is true, but just use mutability where you need it for speed and stick to immutability everywhere else”. Oh, how I wish it was this simple. The problem with mutability is that once you let a little of is get in, it spreads like a wild-fire through out your code. Even if a piece of code itself looks immutable, if it ends up depending on something deep down in your code base that uses mutability, then it is by definition is not immutable any more.”

      If you don’t let the mutability escape then there is no problem. the F# core libs also use mutability under the hood a lot for many of it’s pure functions.

  2. Can you post a bit more info or provide a link that explain this (I’m just interested in why there’s a difference):

    “… the specialized F# collections are slow compared to their BCL counter-parts, especially Map compared to Dictionary, while I understand why they are slower, it doesn’t change the fact that they are.”

    matt

    May 9, 2012 at 12:57 pm

  3. I look forward to the new C# version.

    Verious

    May 15, 2012 at 3:41 pm

  4. 1) “Mutually Recursive types must be defined in the same file, so when two classes need to be able to refer to each other they need to be right next to each other in the source also (I understand why F# works like this). This is fine for a few grouped objects, but when you end up with a 2.5k line behemoth that used to be Runtime.fs (open at your own risk) inside IronJS, it’s not fun any more. Whenever I bring this up in ##fsharp on irc.freenode.net everyone tells me that this is due to bad library design. While there are several ways to break apart two classes from each other, they all end up being slow compared to just directly accessing a field on the object instance. Maybe it’s fast enough for you, but it’s not fast enough for IronJS.”

    Abstractions on the wrong level, your perf sensitive stuff shouldn’ be on that recursive boundary anyway.

    2) “Constructors if used incorrectly will cause a run-time over head on every method call, to verify that the class has been initialized properly. There are also multiple other problems with initializing an instance inside both the implicit and explicit constructor syntax.”

    Just don’t use “this” in the ctor of transient objects which is good practice anyway and you don’t have this problem,
    if you use “this” in a ctor F# does a runtime check to ensure the object is fully initialized before accesing it, for example calling a virtual member in a ctor in C# will silently fail, but F# will give you a runtime error.
    See here http://www.bluebytesoftware.com/blog/2010/06/27/OnPartiallyconstructedObjects.aspx and here http://msdn.microsoft.com/en-us/library/ms229060.aspx

    3) “Interfaces can only be implemented explicitly, so you end up having to cast your objects into their interface type constantly.”

    You haven’t abstracted your code properly if you have to cast all the time.

    4) “Collections the specialized F# collections are slow compared to their BCL counter-parts, especially Map compared to Dictionary, while I understand why they are slower, it doesn’t change the fact that they are.”

    Those so called specialized F# collections are persistent data-structures, F# is a general purpose multi-paradigm .NET language and there is nothing non-F# about using Dictionary
    it is also used for examples in the Expert F# book.
    In short, use the right data-structure for your problem.

    5) “Again, a lot of people will say something like: “All things you said is true, but just use mutability where you need it for speed and stick to immutability everywhere else”. Oh, how I wish it was this simple. The problem with mutability is that once you let a little of is get in, it spreads like a wild-fire through out your code. Even if a piece of code itself looks immutable, if it ends up depending on something deep down in your code base that uses mutability, then it is by definition is not immutable any more.”

    If you don’t let the mutability escape then there is no problem. the F# core libs also use mutability under the hood a lot for many of it’s pure functions.

  5. “A lot of people will now say something like: “But F# supports mutability, native .NET objects, etc”. Yes, it does. But the “mutability/imperative/OO” features in F# are hamstrung by several crippling issues, and what you end up with is what feels like a slightly crappier version of C#. Just to mention a few things (there are a lot more, both major and minor):”

    OO in F# doesn’t feel crippled, see here http://trelford.com/blog/post/LighterCSharp.aspx
    also Imperative code is made to look more verbose than Functional code on purpose, since immutability is the default and it’s not that bad compared to C#.

  6. I’ve seen many peoples writting f# code as “perfect translations from c# code” and it’s ugly, it’s ugly because f# is mainly functional language and it has a set features let writte elegant and clean code without class..normally you only must use class for c# – f# communication….then..I agree with you…F# OO as a bad version of C# and if you wanna an expressive language keep writting OO then you must use Nemerle…

    Collections are slow, and they are slow in any functional language…the trick is use c# collections when you need performance and no need inmutable values..if you need concurrency then inmutable and f# is the best option…

    andrew

    July 2, 2012 at 6:55 pm

  7. Regarding the problem of mutually dependent types, I can imagine how annoying it must be for you to be told “that’s just you doing it wrong”, and I wish people would provide more helpful advice. You may know about these tricks, but I think they are worth mentioning anyway for your readers who don’t know about them:

    1. Split the API of your class in several layers. The lowest layer should be composed of setter and getters that enforce whatever consistency constraints you have on the fields.

    2. Other layers providing additional functionality should be implemented in terms of the lowest layer as out-of-class functions or type extensions (see http://msdn.microsoft.com/en-us/library/dd233211.aspx)

    If you follow these steps, you are limiting the cause of dependencies to fields and signatures of the low-level API, which can help keep it to a minimum.
    Dependencies incurred by higher level functions can be moved to separate files, which helps keep file sizes to reasonable levels.

    3. Additionally, if that’s not enough and the code from step 2 gets messy too, you can introduce mutually dependent interfaces, and implement each concrete type in its own file. As you point out, this has a cost, although it seems the .NET jit has some smart tricks to avoid branch prediction failures and cache misses, assuming it’s always the same concrete implementation that’s used.

    As an alternative, you can go another way and use duck typing with inline and ^T type parameters (see http://codebetter.com/matthewpodwysocki/2009/06/11/f-duck-typing-and-structural-typing/). I haven’t used that trick because I don’t like repeating the member constraints, but it might work.

    I took a quick look at Runtime.fs, and I don’t think it’s worth breaking up. Sure, it’s big, but it’s also quite simple and regular. Lot of boiler-plate code to implement union types in an efficient manner. I’m not sure letting this boiler-plate code spread over multiple files would actually improve things, and rewriting that part in C# may not have all the benefits you expect. It’s a matter of taste, I guess.

    Johann

    July 3, 2012 at 12:29 pm

  8. “the ability to mix code and IL instructions inside source files.”
    Could you please provide an example of this? I can’t seem to find anything on the internets

    dmitry

    July 5, 2012 at 8:51 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: