Show HN: C# 6.0 Functional Language Extensions
github.com
github.com
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 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.
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.)That seems like a reasonable approach.
- 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.
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.
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) Some(Some(x)) -> Some(x)
Some(None) -> Exception
Thoughts?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.
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.
This line changed my mind. You're absolutely right.
Thanks for the feedback :)
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.
The One Language to rule them all? Maybe not, but definitely making incredible progress.
Remember you can do full on ECMA 335 with C#.
http://stackoverflow.com/questions/267862/what-makes-lisp-ma...
.. and not the last: http://programmers.stackexchange.com/questions/97069/is-ther...
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.
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.
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.
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.
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.
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 ;)
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.
Wait, did I just agree with you?
-Neal (Roslyn team)
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.
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!
> 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.
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
ahem https://github.com/louthy/csharp-monad ;-)
> Here's a work-in-progress:
Cool, I'll take a look.
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...
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 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.
What would be the difference with "nullableVar ?? defaultValue" ?
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.
As an aside, I recognised your name in the example code on there - 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 :)
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.
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).
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!
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");
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.
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!
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 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.
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.
new { Name = "John", Age = 29 };
and you're there; no need for tuples and wrapping, etc.> 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.