What is F# lacking for OO or imperative? [closed]

2020-05-23 03:58发布

问题:

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 8 years ago.

Many times I hear that F# is not suited to particular tasks, such as UI. "Use the right tool" is a common phrase.

Apart from missing tools such as a WinForms/WPF/ORM designer, I'm not sure what exactly is missing in F# -- honestly! Yet, particularly with UI, I'm told that C# just does it better. So, what are the actual differences and omissions in F# when using it imperatively?

Here is a list I came up with:

  • Lots of missing tool support

  • F# is still beta

  • Your developers don't know F#

    • I'd like to not consider those points, as they aren't really intrinsic to F#
  • Mutables need "mutable" or need to be ref, ref needs ! to dereference

  • Mutables assign with <- and ref uses := ( they're both 1 more character than just = )

  • val needs DefaultValueAttribute to get a default value

  • F# doesn't emit implicit interfaces

  • Protected members are more difficult to deal with

  • No automatic properties

  • Implemented virtual members on abstract classes require two definitions

  • Quotations-to-LINQ-Expression-Trees produces trees slightly different than C#/VB (annoying for APIs that expect their Expressions in a specific format)

  • No stackalloc

  • F# doesn't have the ?: conditional operator

  • Pointers might be considered more cumbersome in F#

  • Delegates/events might possibly be considered more cumbersome (I'd argue they're easier, but at a minimum they're different)

  • No automatic type conversions (like int to float, or implicit casts)

  • No special syntax support for Nullable (C#'s ? type annotation and ?? operator, as well as using operators on nullables.)

  • No automatic upcasting to common base class or boxing (ex: let x : obj = if true then 1 else "hi" // this won't typecheck)

  • Values can't be discarded without a warning ("ignore" to get around it)

  • Doesn't have C-style syntax :)

To the question: Which of these are a hindrance to writing imperative or OO code? Why (short examples)? Which ones did I miss? What are the best workarounds, and why are they not enough?

Please note, I'm not talking about writing so-called idiomatic F#, and I'm certainly not talking about functional programming. I'm more interested along the lines of "If I were to force myself to write UI or imperative/OO code in F#, using F# OO/imperative features and class types, what hurts the most?"

Bonus If you don't know F# but use C# or VB.NET and think it's a better tool for some situations, please indicate the specific language features and syntax you find appealing.

回答1:

With regards to OO stuff, my list might be

  • You mentioned some things about interfaces and virtuals that can be annoyances or hindrances; lack of auto-props you mentioned is also a shame
  • OO frameworks sometimes require many mutually recursive classes, which F# currently forces you to put in one file (in a 'type ... and ... and ...' block)
  • If you need to call two different base constructors in a derived class, you have to use the 'explicit' class syntax (as mentioned here)

That said, F# has strengths in the OO department too, like type inference, local functions, succinct syntax, ... so overall I might call it a wash in the 'heavily OO' department.

(I have done almost no UI programming of any kind, so am not qualified to weigh in there.)

(EDIT: I also want to interject that, even though you're explicitly choosing to exclude 'tooling' from the question, I think tooling matters a bit, and in this respect the other managed VS languages excel.)



回答2:

I'm not a huge fan of this question as it complains about F# not supporting idiomatic C#. For example, I don't think it's fair to criticize F# for using <- and := for variable assignment because the language makes things immutable by default.

Regardless, there are several imperative / object-oriented things you can do in C# that you simply cannot in F#.

  • F# doesn't not support nested classes. In C# you can declare a class within the body of another class as a mechanism for scoping types. F# does not support this.

  • F# doesn't allow you to implement the same generic interface twice. For example, in C# you can implement IComparable<int> and IComparable<string> on the same type.

  • In F# you must have architectural layering. Due to F#'s type inference you can only use classes that have been declared 'before' or in the same 'block' of type declarations. In C# however you can have any class reference any other. (This actually enforces some good programming practices.)

  • F# doesn't have 'native' support for LINQ, as in no from keyword. However, you can use LINQ APIs and lambda expressions to achieve the same result.

Things that F# can do that C# can't:

  • Discriminated unions. This makes it trivial to create tree-like data structures in F#, where in C# you'd have to resort to a complex type hierarchy.

  • Asynchronous workflows. This feature of the F# library makes asynchronous and parallel programming much more paletable by abstracting away all the pain associated with the APM.

  • Pattern matching and active patterns.

  • Units of measure for eliminating bugs associated with using the wrong units. (E.g., adding 'feet' to 'meters'.)

  • etc.

You shouldn't focus on what F# 'can't do like C#' because the whole point of learning F# is to be presented with a new way to think about problem solving. If you write idiomatic C# code in F# you aren't actually gaining anything.

AFAIK F# isn't missing any 'must haves' for .NET / COM interop. In F# you can do things like have out and byref parameters, declare string literals, and support putting attributes on just just about anything.



回答3:

Imperative programming in F# is much better than people would lead you to believe. F#'s match statement is awesome. Why other languages have not implemented it I don't know. As far as things like syntax (mutable, ref, etc) they are easy to work with. You get spoiled by F#'s sparseness and it's easy to complain when the syntax is bigger than normal. Tuples are also great. They will be in C# 4.0 too. Currying is another bonus for Imperative.

Concerning OO I find I rarely use inheritance in pure F# projects and favor composition and interfaces instead. This is mainly due to using the primary contructor that allows you to use it's parameters as private properties in your methods (not sure if I worded that correctly). Other language constructs such as pattern matching pull you away from inheritance too. I've not done any mixed projects C#/F# so I can't comment on that.

F# isn't all roses.

My biggest issue with F# and game programming is performance. Implementing in F# is really fast and I often get a prototype up and running for what I want to do the same day I think of it however I find myself rewriting code for performance reasons way more often than in C#.

Part of the problem is my inexperience with functional programming style that is to use Seq, List, Map and all their accompanying methods such as map, iter, fold, scan. My first functional solution is almost never the fastest while my first procedural solution is almost always close to the best possible. I want to say part of this isn't me. That functional programming doesn't lend its self to performance in some situations.

I use less of the functional data types in F# now than when I started.

EDIT:

Many months have gone by since I've posted this and I no longer have issues with performance. My first functional solutions are often simpler and nearly optimal now and immutable data structures are simple. The only performance issues I have now are with the CLI and I can always do c++/cli if I need to. I do use some inheritance besides interfaces now but it's only for anonymous objects.



回答4:

My biggest pain with statically typed functional languages is inability to have variable arguments on a method that needs to be inferred. Like the map function. In F# you need map2 map3, etc.

On the other hand C# lacks this too, unless you want to go via reflection.

Take Scheme for example, that is not statically typed. You do not have an issue to define a single function that can handle all the cases of map_1 ... map_n. Sure you loose static type safeness, but it pales in comparison to the additional convenience of writing concise code.



回答5:

Just for the record: F# has stack allocation - try an infinite recursion that's not tail recursive and you'll get a stack overflow. It's really no different to stack allocation in C#, aside from you knowing that you can use tail recursion safely F# (in C# it depends).

Also, I'd point out that when you get to really heavy event oriented code, F# scales better than C# in terms of allowing you to build up a framework to do the "plumbing" of dispatching events to handlers, etc without the plumbing becoming the majority of your code. For a simple UI, C# or VB may suit more people. For complicated situations, F# pulls ahead, and personally I need help with the complicated situations more than the simple ones.