Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: C# 6.0 Functional Language Extensions (github.com)
164 points by louthy on Nov 19, 2014 | hide | past | web | favorite | 87 comments



A comment on the option type: I think transforming Some(null) into None is wrong.

- There is an x for which Some(x) is not a Some. This violates useful invariants. - Some((int?)1) is a Some, but Some((object)(int?)1) is a None. - optionValue.Cast<object>().Cast<T>() is not the identity function. - I can't use your option type in existing code without doing careful null analysis. - As a rule of thumb, generic code you should treat null as just another instance of T with no special treatment (beyond the acrobatics to make the code not throw an exception). That way both users enforcing a non-null constraint and users allowing nulls can use your type.


Just replied to MichaelGG on the same subject [1]. I understand what you're getting at. I'm not particularly looking to create an algebraically perfect type. The explicit reason for this is to kill null as a possible output from a method, so that the consuming code only has Some or None to deal with. If you want to return null, don't use Option<T>.

[1] https://news.ycombinator.com/item?id=8634015


It only has Some or None to deal with. Some<T> or None. Also, do you take it all the way? By the current logic, Some(Some(null)) should also return None.


If Some(null)is None, then shouldn't Some(Some(null)) be Some(None)?

But I'd agree that Some(null) should just be Some(null) and not magically become None. Now, there should be a a generic function something like NullableToOptional<T> that returns an Option<T> by mapping any non-null value v to Some(v) and null to None, but it shouldn't be the core behavior, because you should be able to wrap a nullable value in an option.


This whole thread is food for thought definitely. I'll open an issue on the github page to get some feedback. I think if Some(null) doesn't become None then it should throw an exception instead rather than wrap null. I'm explicitly trying to control the number of possible outcomes, passing null back shouldn't be valid at all imo.

So perhaps the conversion logic should be:

    x = Some(x)
    null = None
    Some(x) = Some(x)
    None = None
    Some(null) = Exception
    Some(None) = Exception
    Some(Nullable null) = Exception
    Some(Nullable x) = Some(x)


I don't see any reason not to have Some(None) = Some(None).


I see your point, but what does it mean? It means returning an Option<Option<T>>. I think I'd rather lift the value out automatically:

    Some(Some(x)) -> Some(x)
    Some(None)    -> Exception
Thoughts?


> I see your point, but what does it mean? It means returning an Option<Option<T>>. I think I'd rather lift the value out automatically

Lifting the value out automatically loses much of the value of having option types. A large part of the value of Option types is that they preserve information that gets lost when using Nullable types for a similar purpose because you can nest them -- that is, the fact that Option<Option<T>> is a thing you can use where Nullable<Nullable<T>> isn't is a big part of the point of using an option type. Extracting the value is done explicitly.


Could you give a concrete example? I've personally never found myself wanting to preserve nested options. What additional information does it preserve? I could perhaps see the value if C# had decent pattern matching, I'm not currently convinced it would bring much benefit to the average C# app.


> I've personally never found myself wanting to preserve nested options.

Sure, but you never need to just because you can -- the most common use of chaining calls that can produce Options in any language that supports them is mapping functions accepting the wrapped type over the Options, which both results in in one non-nested Option at the end of the chain and makes the fact that the input is coming from an Option transparent to each of the calls in the chain. If you don't need the information preservation of nested Options (and its true that its less common that you do, and often Either is a better choice than Option for preserving information), then you just don't bother to explicitly wrap an Option in another Option.

Magically special casing it so that wrapping an Option in an Option flattens it to a single, unnested Option doesn't seem to me like it serves much purpose but ruling out a use case that Option types enable over nullable types, because there's no reason you'd ever explicitly wrap an Option in an Option unless you wanted them nested -- and it makes the implementation more complicated to impose that unnecessary limitation.


> because there's no reason you'd ever explicitly wrap an Option in an Option unless you wanted them nested

This line changed my mind. You're absolutely right.

Thanks for the feedback :)


Coercing Some (null) to None seems like a bug. Null is a valid value in the type and should be allowed (F# allows it too).

Otherwise, a function might accidentally return None and cause a bug. For instance, a function that looks up s user and returns the name. The name might have null as a valid option. Some(null) indicates user found with no name. None indicates user not found.


I hear what you're saying, but I felt that the reason for using Option in C# would be explicitly to deny nulls. If you are OK with accepting null, don't use Option and return null.

I think that's slightly different with F# where you mostly deal with value types that definitely have values. It's rare that you need to pull null out of the bag (unless interop'ing with .NET libs).

I understand why you might call it a bug, but I think I make the reasoning clear in the documentation. So I hope it's unlikely to cause confusion.


The proper way to do this is to have something like in Scala where you say

    Option(foo)
if you want "foo" coerced to either Some(...) or None based on its nullity. It doesn't prevent you from using

    Some(null)
if that's what you really mean. (Though I think it might be a reasonable idea to actually throw an error in such a case. However, there should be a way to construct a Some(null), perhaps with a name that suggests that it's not recommended. Perhaps something with "unsafe" or "unguarded" in the name.)


> Perhaps something with "unsafe" or "unguarded" in the name.

That seems like a reasonable approach.


I feel like throwing a NullReferenceException would be the more appropriate way to deal with it. Either treat Some(null) as a valid value, or treat it as an error. Silently coercing it will lead to problems down the line, like with the Some(Some(null)) case mentioned.


I'm definitely starting to lean that way. Thanks for the feedback.


Due to popular demand, there's now a NuGet package. My first attempt at a NuGet package, so let me know if I've messed something up:

https://www.nuget.org/packages/LanguageExt/0.0.1-beta


This is the general link, rather than directly to a version.

https://www.nuget.org/packages/LanguageExt


I hope this does not come across to negative, definitely a nice project, but is this really useful in practice? If you want F# or Haskell syntax, why not just use F# or Haskell? Why bent C# until it looks like something that already exists? If you work together with (pure) C# developers, you will just confuse them. If you work together with Haskell or F# developers, you can just use that languages.

If this is just a fun project, scratch what I said, I love building useless stuff myself, but if it is intended to be used seriously I don't get the point.


I'm involved with a 9 year old C# project, it has seen almost every version of C#. When generics turned up that was nice, then LINQ, very nice, etc, etc. I like to see how far I can bend the language in general, but that wasn't the main driver.

Over the past few years I've done some Haskell and also we've started creating satellite projects around our core project that are developed in F#. F# has been great to work with, and I love how a few key language decisions can make whole classes of bugs disappear. It's frustrating to run back into those bugs when returning to C#.

It's great to see the direction that C# is going in, it's clearly going toward a more centrist position midway between OO and functional. But a few key things won't go away, ever. null for example, won't ever leave the language. Mutable first won't either. And because of this I'm always trying to think of ways of removing those classes of bugs that hurt the most.

This library is currently just a pet weekend project (so it's exactly 5 days old at the moment). But I would like to take it further if there's enough interest. As I say in the introduction, I know it won't sit well with plenty of people, and that's fine.


> Why bent C# until it looks like something that already exists?

Because in the industry at large, JVM == Java and .NET == VB.NET == C#.

So bending Java, C# and C++ to have features from OCaml, F#, Haskell... is the only way many of us can ever aspire to do FP at work.

For example, on my case we always use the software stack that is already in place and used by the customer IT. It is expected that the software gets built with the said tools.


Our organization (big C# codebase, newer apps in scala) has doubled down on extremely functional C# (using the NICTA XShapx library which has a lot of overlap with this one). It's been a tremendous help. C#'s linq comprehension makes it one of the most powerful languages doing functional style (w\ Haskell and then scala at the top).

Since LINQ supports any monad and with extension methods, you can write a lot of pure, concurrent/parallel code with minimal fuss. This has mainly been helpful from a testing perspective, since the majority of our logic is 'pure', we can run more tests without having to set up test DBs/Webservers, etc.


The importance lies in that C# is becoming one of the first truly, for lack of a better word, "programmable" programming languages.

The One Language to rule them all? Maybe not, but definitely making incredible progress.


C# imo doesn't qualify as a programmable programming language and even if it did it wouldn't be the first.


I'm not sure what you mean by that. I was under the impression that Common Lisp macros were the first metaprogramming feature

http://stackoverflow.com/questions/267862/what-makes-lisp-ma...

.. and not the last: http://programmers.stackexchange.com/questions/97069/is-ther...


Well, Lisp macros as a meta-programming facility were introduced in 1963 by Timothy Hart.


How is C# at all "programmable"? Even all the neat C# features, like async or duck typing are hard-coded into the language and not extensible or generic.


IL weaving, CodeDom, Roslyn... All built in ways of powerful manipulation of your C# code.

Remember you can do full on ECMA 335 with C#.


What about Lisp?


On a side-note, I love that your handle is BBC BASIC. My first language. It's quite a way from this lib!


Yes I remember the days when you edited a line by using the arrow key to go up then copying character by character, then edit the bits you want as you go along. Or something like that. Magic!


It took me a while before I realised you could do that. Before that I would type the whole thing out again. When I realised you could copy parts of the line my mind was blown! Ah.. those were good days..


Dare to dream:

http://bbc.godbolt.org


That brings back memories, I even had the cub monitor with mine.


I was a big proponent of functional C# and even started writing s book on it, back in 2008. But my final conclusion is that C# is ultimately too clumsy and verbose to really work with highly generic and highly functional code. You end up fighting too much.

This librarydoes look nice, and top-level findings really do make a difference. But I doubt it's enough to really make up for all of C#'s shortcomings. But if you're stuck with C#, might as well make the best out it.


> But if you're stuck with C#, might as well make the best out it.

That's definitely the crux of it. There's no way you can tame all of the issues of C#, but it's possible to smooth some of the rough edges, just.


It is not just 'syntax'. Also I think a substantial portion of developers or the programming industry at large do not think these sort of tools are `useless`.


I am not saying the functionality is useless, but it already exists in other languages. Why force C# to look and behave like F# instead of just using F#?


I think most of the people creating and using these libraries would use F# if they could sadly. (However I am one of the lucky ones doing clojure in my day job.) Also a lot of existing software that needs to be maintained in C# would benefit from this library or others like it, just like they would benefit from having tools like linq, immutable collections, tpl available.


Beautiful. I've been trying to pick up C# simply due to Xamarin, and this definitely looks like it will make me feel more at home. Has anyone had success with 6.0 on Xamarin? (Is it even out yet? I saw announcements, but they looked like previews).


Just use F# from Xamarin. It's the real deal. C# is just a stop-gap measure to suck in the enterprise devs.


I beg to differ. C# is a perfectly usable tool to introduce you to programming until you're ready to move on to F#.

Wait, did I just agree with you?

-Neal (Roslyn team)


How's the pattern matching spec coming along? ;)


Except that all the docs are in C#, which means if I want to consult them, I have to either know C# or know how F# interops with C# (and therefore understand C#). Trust me, I would love to, but I don't have the time to learn two languages for one project.


There is essentially no language specific documentation in well made APIs. There is a common type system so there is very little that's different. Mainly if you need a semicolon and how you prefer your parentheses.

There are some poorly made APIs, like ASP.NET MVC that exploit C#-specific side effects to compensate for C#'s verbosity. These APIs ignore the common type system and just pass object everywhere, breaking tooling and type safety.

But as long as Xamarin didn't come up with terrible designs, you're all set. And, even if they do rely on something strange, F# is such a better language, that the small setback of dealing with interop (which doesn't require learning C#) is totally worth it.


I'm an F# developer, but still decided to not use F# to build an android app. Dealing with the UI libraries like support.v4 was just too painful from a functional perspective. If you don't need any object oriented dependencies, then it's smooth sailing.


F# is not meant for implementing the complete app anyway. Just use it for business logic where it makes sense.


I've only heard that in context of A: missing tooling, B: Microsoft, in their quest to make customers feel C# is still the real language and F#'s just some science toy. I've used F# for a Web UI via WebSharper and it was lovely. It's just a poor reflection on the state of UI kits if they don't work with F#.


"It's just a poor reflection on the state of UI kits if they don't work with F#." - well at least they don't start using lower-case names randomly.


Out of interest what language are you coming from?


I'm all over the map. My day job requires tons of quick and dirty data analysis or concept prototyping which tends to get thrown away, which moves me towards declarative langugages (lots of SQL and Prolog) as well as pragmatic dynamic languages (Clojure,R,Ruby). But if I'm actually building anything that is meant to last more than a month, I tend much more strongly to an ML philosophy. Currently that is mostly Scala and some Rust, but I've been wanting to play with F#/OCaml for a long while.


You might want to consider F# over C# then, there's very little cognitive barriers to using the .NET framework with F#. For example:

    open System.IO                 // F#

    using System.IO;               // C#
    

    let thing = new Thing(a,b,c)   // F#

    var thing = new Thing(a,b,c)   // C#


    Console.WriteLine("blah")      // F#

    Console.WriteLine("blah")      // C#
Essentially C# parameter lists become tuples in F#. So interop is mostly quite straight-forward. It's mostly quite a mechanical process to convert C# sample code into F#.

It's harder to go the other way, and take F# code into the C# world, because functions in F# allow for partial application. The type inference system is awesome, so you'll end up with more generic functions and use first class functions more. That stuff is tricky to bring back (though not insurmountable).

The OO grammar is a little tricky in F#, but I tend to shy away from that. It's not needed most of the time.

Then at least you don't have to deal with all the stuff this library is trying to solve ;)


Exactly the kind of attitude that hinders the F# growth.

I mean F# is a pretty useful numeric DSL, but let's not blow things out of proportions. Modern languages have R#-like refactoring tools.


Many C# 6 features work in Xamarin's compiler, but aren't necessarily understood by the IDE, for example. `using` static classes and the ?. operator both work.


Funny I come across this today. Only yesterday did I start turning some of my helper functions and extension methods into a little NuGet library. It's amazing how much overlap there is between your library and mine, though I'm probably going to steal some of your tricks. :)

While I do have an Option type with all the relevant LINQ operators defined, I've resorted to using Nullable<T> for all string parsing functions that return primitives as Nullable is a type that's actually supported databases. For any nullable, I can use a nullableVar.DefaultTo(defaultValue) to convert it to a non-nullable primitive type. Personally, I don't find Option types in C# all that compelling because the pattern matching mechanism doesn't compel you to handle all cases like F#.

All in all, it looks like an interesting library.

Thanks for posting!


Heh, that's really interesting! Do you have a link to your work?

> Personally, I don't find Option types in C# all that compelling because the pattern matching mechanism doesn't compel you to handle all cases like F#.

In my library you are compelled. You must provide both a Some and None handler, even on the fluent variant. That was one aspect I absolutely wanted to nail after doing a reasonable amount of F# work recently.

The only exception to that is you can ignore the Some branch, and just handle failure. The Some branch then just uses an identity function behind the scenes. So you're still compelled to deal with the failure case, which is where I feel most of the C# null carnage comes from.


It's a good idea but i think in reality people will be lazy, not know what to do, and just throw their own NullPointerException inside the None-handler. And then we are full circle back on square one.

It's like checked exceptions in java. Sounds good on paper but in practice this happens: You are in the middle of just bursting out code to quickly get a response from a local rest api and you hit compile to suddenly get a whole list of "NetworkException/JsonParseException/WhateverBloatException not handled". At the moment you couldn't care less and just convert everything into a RuntimeException and tada! you don't have checked exceptions anymore.

I think it might also cause unnecessary bloat when mixing code that uses the Option type and not, I've seen it happen with code contracts, either everything or nothing must be annotated. Once you start mixing you will have to add assertions before or after every call to the unannotated library.


> Heh, that's really interesting! Do you have a link to your work?

I do and I don't. I actually wrote a lot of these things in VB.NET 4-5 years ago and I've recently returned to the .NET world, so I'm in the process of converting it to C#.

There's a ton I haven't converted yet (Option types, lazy thunks that use LINQ, Parser monads, LINQ Trees, etc.), and there's a ton I'm going to let die out. :)

Here's a work-in-progress: https://github.com/steego/Steego.NET


> There's a ton I haven't converted yet (Option types, lazy thunks that use LINQ, Parser monads, LINQ Trees, etc.),

ahem https://github.com/louthy/csharp-monad ;-)

> Here's a work-in-progress:

Cool, I'll take a look.


> ahem https://github.com/louthy/csharp-monad ;-)

Very nice! You may save me a bunch of work yet. We should get some of your stuff into NuGet packages so you can spread the love. I saw on Twitter that Miguel de Icaza approves of your library. :)

BTW, I just pushed up my little Tree library to my repo. Feel free to incorporate and change it however if you like it. https://github.com/steego/Steego.NET/tree/master/Steego.Tree...


> I saw on Twitter that Miguel de Icaza approves of your library. :)

That's awesome! Thanks for sharing. I wouldn't even know how to get something onto NuGet. I guess my weekend project might have to become a bit more serious ;) Time to do some research.

EDIT: research done... https://www.nuget.org/packages/LanguageExt/0.0.1-beta


It's great that people see the value in having these kinds of functional tools! I also put together a library that looks very similar to this, and it sure has been handy. I used it as a foundation to build some solid algebraic APIs (typesafe parser combinators based on the Brzozowski derivative and pickler combinators).

I like how the catamorphism is called "Match" rather than "Fold" (which is what Scala uses). Using named parameters whenever Match is called really does help with the readability, and they make the "case analysis" explicit.


> For any nullable, I can use a nullableVar.DefaultTo(defaultValue) to convert it to a non-nullable primitive type.

What would be the difference with "nullableVar ?? defaultValue" ?


Good point. I originally wrote it for VB.NET, though IF(nullableVar,defaultValue) would be better now...


Impressive looking stuff - I'm currently learning Scala and getting deeper into functional programming, and a lot of the concepts I come across (e.g. Option types) make a lot of sense, so it's cool to see them being implemented in other languages - hopefully with the recent MS announcement, I'll be able to get back into C# development in the future as it's a pretty great language all in all.

As an aside, I recognised your name in the example code on there - I used to occasionally hang around on 4four :)


> I used to occasionally hang around on 4four

Nice. 4four's still going, but it's very quiet these days. Killed by the Facebook behemoth. That's life, it was fun while it lasted :)


Feel free to pilfer some ideas from my own take on a similar lib for C#. https://github.com/danstone/lambit - includes some basic pattern matching support for example.

This sort of library is kind of necessary I feel for things like poor tuple support at least.

I'm not entirely sure about the casing conventions. I mostly code in clojure/haskell/f# but 'when in rome'. Its likely the sort of thing that will turn off a lot of stubborn developers.


Yeah I've implemented a similar pattern matching library before (not public though). I always felt the syntax to be a bit 'bulky' because there were always two lambdas, one for the predicate and one for the handler.

I like your take on list matching though, I think that's excellent and much more usable. Not 100% sure how it could be adapted in a general way to the C# Tuple though; because Tuple<A,B> is a different type to Tuple<A,B,C>. So you could only ever match on one type (if you don't take the bulky predicate route that is).


List matching tends to be fairly useful even its very limited form in my lib. Its basically arity match + de-structure. It doesn't nest like full fat pattern matching.

I don't see how tuple matching is as useful (over your 'With' fn or my 'Apply') unless it can nest, especially with matches on union types. e.g (Maybe a, b) | (Nothing, b)

Basically I feel like such a thing is going to be very hard in C# to achieve with any sort of usable api. I personally took the route of the api being more important than whether any sort of decent abstraction is actually there behind the scenes - I didn't mind writing 100's lines of boilerplate to get the result and behaviour I wanted.

Its great to see more functional support for C# from the community and I look forward to using your lib in the future!


This is a really interesting experiment, although personally I'd find using:

var ab = tuple("a","b");

awful. It's very hard to read what is going on here- someone else reading your code would be utterly confused. The method name starts with a lowercase character, and I'm not sure I like the possibility to omit class names for static classes (granted this is not your invention but a new C# feature right?); to me it would seem it is missing the 'new' keyword.

I'd rather use Tuple.Create<T1,T2>(T1 first, T2 second) until they add proper support for:

var ab = ("a","b");


That's fair enough. I knew implementing it this way would ruffle a few feathers, and I totally understand why. I too would love to see ("a","b"), and proper pattern matching... and ..

This was a weekend project that kind of grew over the past few days. Initially I wasn't going to make everything lowercase, but I started thinking about the fact that a lot of these functions are now essentially global and the potential for clashes in real code. So I felt having a core set of useful functions that look more like the lowercase keywords of C# rather than library methods would work better. It definitely won't be everybody's cup of tea though.


I realise my comment is probably a bit too negative- I really like your ideas (especially the Option type). Things like Tuple<T1,T2,T3> etc. make me wonder if we would not all be better off though if we just started using F#... although I still like C#.


> I realise my comment is probably a bit too negative

Not at all, you're entitled to your opinion. Whatever works for you :)

> we would not all be better off though if we just started using F#... although I still like C#

That would be nice. Unfortunately it's not always quite as easy as that. At least in my corner of the world!


The first time you might go wonder what the function "tuple" is, and after that you're set. It looks vastly better than the verbose "Tuple.Create" call. Less characters, same information.

Top level functions were sorely missing from C#, so it's nice they addressed it. You're right though, we'd all be better off using F#. But there is a ton of unjustified people not wanting to use it, as well as fear from people incapable of doing so. Might as well make the best of C#.


I wonder what the impact is on performance of routinely wrapping your types in Option<T>?

I am assuming it is minimal, because the struct would remain on the stack, and the object on the heap. There is just an unwrapping and wrapping cost but no more than Nullable? However this may be a naive view.

I like the syntax though. Where I work we use Code Contracts. This reduces bugs due to nulls but sometimes 25% of the code is Contract.XXX(...) which is annoying to read. And more typing too.


As you say the overhead should be similar to Nullable. And the struct should be allocated on the stack, but the CLR doesn't guarantee that, so it can be on the heap too.

There is however an 'if' in the constructor of Option<T>, so there's a little extra potential branching overhead. For most scenarios I suspect this is all moot, but if you're looking for the absolute maximum performance then you would probably want to do some profiling before committing too heavily.


The CLR has suboptimal codegen for structs, so there is likely an impact.


Cool stuff. Isn't there still a problem with Option, where when a method takes Option<T> as a parameter, invocations can pass null for that Option<T>? So there's still the possibility of null exceptions.


From the article:

> Option<T> works in a very similar way to Nullable<T> except it works with all types rather than just value types. It's a struct and therefore can't be null.


Exactly. Option<T> has a implicit conversion operator that converts a type of T to Option<T>. So passing T to the function constructs a new Option<T>, and the constructor checks for null. It's also likely (though not guaranteed) that the Option<T> will be allocated on the stack, and should therefore not pose too much of an overhead.


Awesome.


I didn't realy get the tuple part, if you want named properties, you can always just do

  new { Name = "John", Age = 29 }; 
and you're there; no need for tuples and wrapping, etc.


You can't return anonymous types from functions.


I was thinking of writing something like this myself. This looks great!


That definition of cons() angers me. It's only useful for constructing lists... not for constructing tuples and trees :(


This is a really cool exploration project, but hopefully I will not see this in any production code. Way too un-idiomatic.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: