views:

837

answers:

9

Are the functional programming features provided in C# rich enough? Would a LISP programmer miss LISP when programming in C#.. whats missing??

+14  A: 

Support for immutability, primarily.

It should be easy to create an immutable type and verify that it's immutable. It should be easy to use an immutable type - support like object and collection initializers, but for the immutable case.

After that: better type inference capabilities, tuples, pattern matching, and supporting libraries (again, immutable lists etc).

EDIT: I should say that I'm not a functional programmer, but I'm a C# programmer trying to learn more functional ways. I'm currently helping out (in a small way) with a functional programming book, so I'm learning lots of stuff there.

However, you will hopefully be pleased with LINQ. It does make life much easier for working with sequences of data.

Jon Skeet
Immutability is nice, but when you've got types with generics nested a bit deep, the lack of inference is also going to be murder.
MichaelGG
For the record, LINQ isn't really a functional feature. It's more of a declarative feature. There *is* a difference.
Jason Baker
I was under the impression that functional programming, sometimes, ends up being declarative. But that's not necesarily the case with LINQ.
MichaelGG
I'd say the actual language integration is a declarative feature, but the LINQ operators themselves are a functional feature, if you see what I mean.
Jon Skeet
@MichaelGG - Actually, it's the other way around. Functional programming is declarative, but declarative programming isn't necessarily functional. But really I suppose "declarative programming" is a bit of an ambiguous term.
Jason Baker
@Jason, hmm -- maybe purely functional. But just functional programming encompases tons of stuff that isn't declarative. Unless even specifying a callback is declarative, in which case I don't know what declarative means.
MichaelGG
+20  A: 

Doing functional programming in C# is technically possible (well, any language that has function pointers or delegates equivalent can be "functional") -- but C# gets very very painful if you try to do much.

Off the top of my head, in no particular order:

  • Type inference
    • Only exists for locals now
    • Should apply to pretty much everything
    • The #1 problem I have with C# is this. Particularly when you declare a local function... Func<...> = ouch.
  • Full first class functions
    • Delegates aren't the answer, since they aren't structually equivalent. There's no canonical type to represent a function of a certain type. Ex: What is "increment"? Is it a Func? Is it a Converter? Is it something else? This in turn makes inference more complicated.
  • Automatic generalization
    • Sucks to have to calculate and specify all the generic type parameters and their constraints
  • Better support for immutability
    • Make it trivial to declare simple data types
    • Copy-with-modify type stuff (var x = oldX { SomeField = newVal })
  • Tuples
  • Discriminated unions (sum types)
  • Pattern matching
    • Makes tuples and sum types much more valuable
    • Allows more expression-oriented code
  • General monad syntax
    • Makes things like async code much easier to write
    • After you've nested 2+ layers of BeginXXX/EndXXX, it gets quite ugly.
  • Easy syntax for function blocks, so you don't end up with closing lines like "});});"

Edit: One more:

  • Function composition

    • Right now it's painful to do much of any sort of function composition. Currying, chaining, etc. LINQ doesn't get as hurt here because extension methods take the first parameter like an instance method.
  • C# should emit tail.call too.

MichaelGG
+1 for tail call optimization...
Erik Forbes
+1 for monadic notation (like in Haskell or F#)
SealedSun
+4  A: 

To expand somewhat on Jon's point, the real value for me in functional programming languages isn't what they've got, but what they're missing.

Most of the "big" languages these days have (or can be tricked into having) the basic functional programming primitives like recursion, higher order functions, etc. So if you really want to, you can write in a functional programming style in most languages (hell, if you have tail-call optimisation in your C compiler, you can pretty much write functional-style code in C).

On the other hand, what mainstream languages also have is a wealth of things that you don't have in a pure functional programming language, and those things can really "break" the functional model, and make it impossible to reason about your functional-style program in the same way as you would about a pure-functional language. Mutable local and global variables, all of the paraphenalia around mutable state concurrency (traditional multi-threaded programming), even iteration if mishandled, all make your functional-style code non-functional (as it were).

Even though you might be able to restrain yourself from misusing the features in your mainstream programming language, it's pretty unlikely that everyone who touches the code over time would be able to restrain themselves, so you can't make assumptions about the functionality of your codebase without really thorough code reviews. Some of the "non-functional" problems are quite subtle, too, so people who want to write functional code can slip up.

The best way to make sure you're being functional is to learn and use a functional programming language. I've managed to learn Erlang to the point that I'm now writing a game server in about two weeks of my very limited spare time, so it's not that much of a challenge -- I believe that functional programming languages, once you wrap your head around the basic concepts, are easier to learn because they're so much more spartan.

womble
Sure, you can do functional style in all sorts of languages. But a real functional language will make it easy. As far as not having features -- I only half a gree. Mutability and imperative can come in handy. As Dilbert says "Sometimes the straightest path is through the mud."
MichaelGG
+3  A: 

In addition to Jon's answer

  1. Tuples - Tuples are possible with a library but it lacks the intrinsic syntax that most functional languages have.
  2. Options - Pretty much the same as tuples
  3. Deep Type Inference Support - C# added basic type inference support in VS 2008 but it's limited to locals inside method bodies. Most functional languages, ala F#, have deep type inference support and it really makes the language more readable IMHO

Eric on why C# doesn't have deep inference: http://blogs.msdn.com/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx

  1. Tail recursion: C# has basic tail recursion support but it is not ingrained into the language. See my blog post on where C# tail recursion and continuations break down

http://blogs.msdn.com/jaredpar/archive/2008/11/10/comparing-continuations-in-f-and-c.aspx

JaredPar
Aren't options just a special case of sum types in general?
MichaelGG
+1 for tail recursion
Brian Rasmussen
@MichaelGG, I'm not quite sure what you mean by "sum types"
JaredPar
I think he's referring to discrimiated unions as seen in F# and such.
harms
Yes, sum type, discriminated union, variant, algebraic data type, etc. Not sure what the most standard name is :)
MichaelGG
+2  A: 

Multiple return values. "out" and "ref" is unreadable compared to returning tuples.

Better support for immutability, just like Jon said. Right now immutability on classes are possible due to human discipline.

But the most lacking feature is a solid collection framework designed around immutability.

In my current project I anticipated immutability would be a big win, and it was, but the collection framework is a horrible match.

For example, say you want to add an employee to a company, like

public Company AddEmployee(Employee employee)
{
    var newEmployeeList = new List(EmployeeList); 
    newEmployeeList.Add(employee);
    return new Company(newEmployeeList.AsReadOnly(), ....)
}

just isn't very performant or nice to implement.

At one point, I even considered making my own collections starting with a simple immutable Cons class. :)

On the other hand, Linq worked out very nice for us. I really like the methods it introduces. I found the special Linq syntax not in my taste.

EDIT: I've too low rank to comment so I'll reply to Jon's comment here. Jon, I decided it was a bad idea due to maintainability. The people that will maintain this project over the next decade probably don't have much Lisp/functional programming exposure. That reminds me of another thing. I wonder how many months of maintenance it will take before my immutable objects become mutable. (Disclaimer, IT business being something of a gold rush era at times I'm painfully aware that many developer don't even get what features like immutable and closures are.)

Tommy
Creating your own immutable collection isn't too hard, so long as you restrict yourself - e.g. only insert at head or tail. Eric Lippert has a great series of posts on this.
Jon Skeet
+7  A: 

If you're asking a Lisper as opposed to an ML-family functional programmer, you won't hear anything about tuples or inference. They will say one thing, smugly: MACROS.

munificent
Yea but at the price of LISP syntax? ;)
MichaelGG
With the _added bonus_ of Lisp syntax!
Matthias Benkard
+4  A: 

Per your question, as a Lisper, what I miss when programming in Java (sorry, I don't use C#):

  1. Good syntax: s-expressions.
  2. Dynamic typing: no complex type declarations.
  3. Closures.

C# addresses (2) and (3) with var and =>, respectively, as I understand it. So my main hang-up is curlies and semi-colons, along with verbose, confusing generic type declarations.


Per your title, as a functional programmer (Haskell), I miss what all those other guys said: immutability, full type inference, tuples, pattern matching, easier generics because no subclassing, strongly typed libraries.

Also, as a Haskell programmer, I still don't like curlies and semi-colons. :)

Nathan Sanders
Haskell has curlies and semi-colons.
ja
I know. How often do you see code that uses them? My point exactly. :)
Nathan Sanders
+3  A: 

I am, and I do. That is, I use Lisp at home and C# at work and I do miss Lisp when in C#. The question is a little funny because I don't consider (Common) Lisp any more "functional" than C# or Ruby or any other multi-paradigm language.

In general, C#'s functional features are becoming pretty decent, to the point where they're probably Good Enough, if a bit awkward. (Common Lisp's support for functional programming sure isn't perfect, either!) But Common Lisp is a multi-paradigm language; C# doesn't (and may never) match Lisp on syntactic abstraction, code generation, method dispatch flexibility, and so on.

If you're looking for an excuse to avoid learning Lisp because you think C# is just as powerful, sorry. :-)

Ken
A: 

A little creativity combined with Linq (which basically is a functional language embedded in C# as becomes more obvious when watching Dr. Erik Meijers great video series on Haskell) will allow you to make your code look more functional.

Here an example without using Linq:

Haskell:

        minThree :: Int -> Int -> Int -> Int
        minThree x y z 
          | x <= y && x <=z   = x
          | y <=z             = y                      
          | otherwise         = z

C#:

    int MinThree(int x, int y, int z)
    {
        return
              x <= y && x <= z    ? x
            : y <= z              ? y
            : z;

    }

Just don't let Resharper Code Cleanup run over your code as it will make it look like this:

    int MinThree(int x, int y, int z)
    {
        return
            x <= y && x <= z
                ? x
                : y <= z
                      ? y
                      : z;
    }

:(

Thorsten Lorenz