The Problem with F# Evangelism
thomasbandt.com
thomasbandt.com
The problem with the F# community is that they keep asking how to convince C# developers to use F#. I'm convinced that is not the right way to grow the F# ecosystem.
Most C# developers tend to come from enterprise and have a set of values and practices that is generally at odds with open-source, functional languages. They are used to producing codebases that can never be open-sourced, using tooling provided by a single vendor. They don't tend to be the ones contributing interesting or new libraries. In fact, this is a large issue with .NET in general. Most strong libraries are spiritual or literal copies of something from the JVM world.
This urge hurts in other ways. For instance, Don Syme has indicated he doesn't want to put things like Type Classes or Dependent types into the language because it adds a cognitive load to the language that's hard to balance vs the gains in productivity. But their absence means that many of the dev's you actually want contributing to the ecosystem, just use other languages. They adopt Haskell/Idris/Scala/whatever and start contributing blog posts, tweets, libraries, conference talks, etc.
I actually think a large part of this decision is that many of these things would make F# pretty incompatible with C#, which is trumpeted as a feature, but again, it waters down the language to the point that C# dev's look at it and say, "Why bother? It's compatible like VB is, so C# can already accomplish the same things." And in the meantime, it prevents new features that would attract others.
What sounds more enticing to developers, "More Functional C#" or "Haskell With Better Tooling"? What's more, there are even examples of how some of these features could be implemented (both Type Classes and Dependent Typing exist in some capacity through feature requests and F*, an experimental Microsoft language).
Basically, F# should be more about being different than the rest of .net, than trying to be ".net through an OCaml set of glasses".
Immutable data is also becoming a common pattern even in C# but it's tedious as hell to write.
Functional programming is also seeping in but again tons of boiler plate.
Biggest problem with F# is that it has it's own warts + it's a big upfront switching cost and a steep learning curve but it offers many marginal improvements - when you sum those improvements it ends up being worth it IMO but that's a very hard sell.
https://github.com/rspeele/Rezoom.SQL
It can't check everything that the other providers can, but IMO you get most of the benefit without requiring any onerous build-time dependencies.
Here's the context that makes it not a big deal:
1) If there is some fundamental issue with having a dev DB then you would just use another ORM, just like in C#. Really, this specific type provider is not an ORM, it's for SQL server data munging. Most every project I've seen in a decade has multiple dev DBs, many used in testing, many used for staging... being able to talk to a valid schema source should be relatively straightforward... but if it isnt then get a different type provider or use an ORM with explicit code generation.
2) Compile time guarantees about data sources is a huge source of comfort when dealing with DBs owned by external actors or teams. For data scientist, integrator, sys admins, and such the norm is connecting to operational DBs that you have very little control over. This specific Type Provider ensures that your scripts will not compile if there has been a breaking schema change. That means you're seeing these issues during deployment, not as costly runtime errors that may or may not corrupt your application state. Less debugging, more safety, better guarantees, immediate feedback on issues; all in realtime.
To summarize: if compile time validation of JSON structures, XML structures, SQL Schema, or custom file formats creates some kind of pain, then don't use a provider of types that relies on analyzing data structures to create code :)
For the other 99.9999% of the time type providers are a DREAM for working with external data sources, provide most of what we want ORMs for, and are very lightweight in comparison. It's rapid data exploration with built in prototyping. Very nice.
You can pass `LocalSchemaFile = "foo.ssdl", ForceUpdate = false` to the provider, and it will only try to connect to the database if the file foo.ssdl doesn't exist; if it does, then the provider just uses it to compute the schema. When the schema changes, you just need to set ForceUpdate to true, wait for the IDE to pull the changes from the server and write foo.ssdl, then set ForceUpdate back to false, and you can commit the changed file.
The bar has moved so much on this one. I remember when C#'s wonderful lambda syntax, along with all the extension methods that LINQ added, qualified it as having pretty good functional programming support. Now that other languages have gone out and copied sane lambdas, and "Map" is in libraries left and right, C# has to keep reaching for that higher and higher bar.
* Immutable record types
* ADTs with completeness pattern matching
* Higher-kinds
F# is also missing higher kinds, but can do the others. Did you have something else in mind?
Immutable records also makes it easier to share data safely, thus making scaling across cores easier in some cases, without worrying about plan interference.
Immutable records are already possible today in C#, even if a bit more verbose than on F#, plus EF code is anyway mostly generated.
I found that a good solution is to write a generic sql serialiser that does all the basic operations (insert, update, select, delete, read one or multiple from readers) and do the rest by hand. It avoids most of the tedious code/sql while not getting in your way to do things like transactions, bulk inserts, etc
In pretty much all of them we ended up writing a simple wrapper around the database that was much easier to control.
Usually the result is performance problems.
Contrast that with type providers - they expose SQL to the compiler - this way you get compile time SQL query checking, type checking (it generates types for SQL queries) and it's manipulating data as input and output with known standard SQL semantics.
I can simplify it for you:
public class TestClass : Record<TestClass>
{
public readonly int X;
public readonly string Y;
public readonly Guid Z;
public TestClass(int x, string y, Guid z)
{
X = x;
Y = y;
Z = z;
}
}
That is an immutable type that has structural equality and ordering, a strong GetHashCode() implementation, as well as a default ToString() which previews the members, and serialization and deserialization constructs. It's a feature I recently added to language-ext [1].If you need `With` functionality, then add the following member:
public TestClass With(int? X = null, string Y = null, Guid? Z = null) =>
new TestClass(X ?? this.X, Y ?? this.Y, Z ?? this.Z);
Then you can use named parameters to do partial updates: value = value.With(X: 456, Y: "World");
Obviously it's still more boilerplate than F#, but C# is never going to have significantly less until we get proper record types and ADTs.> Functional programming is also seeping in but again tons of boiler plate.
Do you have an example? It's something I've worked on quite heavily over the past few years, so I'm always looking for areas to try and improve the lot of C# devs trying to write functionally.
[1] https://github.com/louthy/language-ext/releases/tag/v2.1.0
public class TestClass
{
public int X { get; }
public string Y { get; }
public Guid Z { get; }
public TestClass(int x, string y, Guid z)
{
X = x;
Y = y;
Z = z;
}
}
And C# 7.0 shortens this to: public class TestClass(
int X,
string Y,
Guid Z);Why would I want to? It's more boilerplate for no gain. I have never understood the obsession with properties over fields. I realise that to some serialisation or interop libraries that rely on reflection to access properties it's important, but to my mind, those solutions should also support public fields. And obviously inheritance-land with interfaces, but I find writing in a functional style that interfaces tend to be a rarity rather than the default setting.
> though I have no idea if this works with your "Record" class.
Yes, field backed properties are supported in the structural equality and ordering.
> And C# 7.0 shortens this to...
It doesn't. That maybe coming in a later release, but neither C# 7.0 nor 7.1 support the record syntax.
public class TestClass
{
public int X { get; }
public string Y { get; }
public Guid Z { get; }
public TestClass(int x, string y, Guid z) => (X, Y, Z) = (x, y, z);
}
Disclaimer: I don't combine expression-bodied constructors and tuple construction/deconstruction in my code. I will use either feature but the combination seems likely to annoy my coworkers. I also haven't measured the costs of this idiom.[0] https://www.manning.com/books/c-sharp-in-depth-fourth-editio...
Encapsulation, allowing to change the internal representation without requiring clients to update or recompile their code.
Ensuring the class invariants are always kept valid.
This benefit is exaggerated. In practice, classes end up being either record-like where you need visibility on all the actual fields, or they are protocol-like/more fully encapsulated with few public members and more methods. The addition of properties was a big mistake IMO.
Read/write fields should just be part of interface specifications. There is little value beyond that.
Properties go all the way back to Smalltalk, which doesn't expose class internal data.
> Read/write fields should just be part of interface specifications. There is little value beyond that.
No because you cannot ensure their invariants, specially if more than one field needs to change at the same time.
Right, because Smalltalk doesn't conflate the use of public and internal members. Properties are second class citizens on .Net, since you can't pass them as ref or out arguments, but we can't use fields which are first class citizens because they have inadequate permissions control.
Then properties exist, but also don't really exist at the reflection level, where you have to search for getters and setters by mangled name if they're private. It's a total frickin mess.
> No because you cannot ensure their invariants, specially if more than one field needs to change at the same time
Properties don't help with changing more than one field either, so I'm not what your point is.
And you can ensure invariants with field permissions, which is what I suggested. Publicly read-only but privately writable is all you need, then the values change via method invocations. Most use of properties just add permissions to field access anyway.
Read/write with default const/init-only fields should have been the default. Then you can't pass read-only fields as ref or out arguments due to permissions, not because they're second class citizens.
There is nothing at IL level that prevents it, they can eventually lift the restriction if they feel like it.
> Properties don't help with changing more than one field either, so I'm not what your point is.
The setter can do a cascade set of changes while keeping the invariants.
While theoretically true, it will never happen. The whole point of ref/out parameters is efficient, direct pointer access to a member. This can never be made efficient for properties. The CLR GC is carefully tuned to handle internal pointers like this, so quite a bit of engineering has gone into making this efficient and safe, which means efficiency is important.
Furthermore, properties are entirely redundant with the simple change I suggested. They literally serve no purpose once you have permissions on fields, since methods cover the remaining use cases.
> The setter can do a cascade set of changes while keeping the invariants.
And so can methods. If you have mutating this much state, you should be invoking a method anyway. Properties are not the place for sophisticated behaviour.
Without it it's useless - and do that example when fields aren't named X, Y, Z but have actual descriptive property names + 5-10 properties like DB tables.
It gets ridiculous really fast.
>Do you have an example?
Record types, result piping (best you can do is extension methods - again boilerplate), pattern matching, etc.
I recently wrote a C# report integration service - it was a C# service that connected to a DB at specific time intervals, did a bunch of data transformations then passed it off to different services over file system or TCP. Wrote it functional style with C# (immutable collections, record types, static functions).
I then rewrote the same project in F# because I wanted how much simpler the code would be in practice - off the top of my head I managed to reduce a ~20k LOC project filled with boilerplate to ~6k LOC of pure business logic data transformation and protocol specific data formatting. IIRC I had 5 logically grouped source files in F# that had connected logic inside of them (ie. record types were "one liners" defined in the same file they were used not a separate file like the C# tediousness)
The resulting code was easier to read because there was just less crap, easier to test from REPL/debug isolated issues and unit test.
C# is not Java but it's still ridiculously verbose for high level programming.
Rust does its best given the circumstances, but people's top complaint is that its hard to learn.
Scala never went Mainstream because you can't hire developers, and training them takes many months.
F# should not repeat that mistake.
This is not some desire for ReSharper in F#, it's basic stuff like compilation speed, the amount of time it takes for the red-squiglys to turn up (lulling one into a false sense of security), build issues: I have a C#->F#->C# dependency graph in a new satellite project I'm working on. If I change the root C# project then the F# project won't re-build/link, then at runtime I'll get errors that the F# DLL expected a function or type to exist that I'd removed from the C# one. And the absolute classic about the necessity for ordering files in the project in dependency order... I get that this reduces cyclomatic complexity, and that's laudable, but it's just a pain once a project gets to a reasonable size.
For my open-source projects I had to drop F# APIs from the projects because I couldn't create net451 and netstandard1.X project files that would build, pack, and be deployable to nu-get.
Also the interop story between C# and F# really isn't that good. Even though it's shouted from the rooftops as a main selling point. Everything prefixed with FSharp is ugly, FSharpFunc can't implicitly convert to Func, FSharpOption evaluates to null for None, so you get none of the safeness in C#, FSharpAsync is reeeaaally difficult to consume from C#, etc. This means we have to build an interop layer and that always feels like needless effort. It would be much better if there was some tooling support that could create proxies between the F# and C# worlds.
It always feels like there's something in the way. And the way of human inertia is that those seemingly minor niggles will tend to put people off full-scale adoption. I love F# as a language (well, not its OO grammar, but I don't use that), but C# + language-ext actually gives me pretty much everything I need (bar ADTs).
https://github.com/mcintyre321/OneOf https://github.com/mcintyre321/ValueOf
Basically C# and F# are 2 different languages that are good at solving different problems, and are mastered by different people. C# is a solid infrastructure language. F# is a good business modeling language.
What I think is best about F#/C# is to have data scientists / mathematicians work in a language and have infrastructure people work in another one, and yet have those 2 languages interoperate well.
The main issue for me is that they don't actually interoperate very well...
If F# is pushed too far from C#, what's the point? Just use Haskell...
But you said it yourself:
> I think the main strength of F# lies in its interoperability with C#.
I've even had devs ask me if we can use Scala instead after a couple days!
Programmers learn how to program with C-style languages. C, C++, Java, C#, JavaScript, Python (sorta) . . . they're all in the same family. People, especially while learning, associate programming with specific constructs and idioms of C-style languages. It's incredibly difficult to break that barrier.
I think that functional programming is on the rise, and that more and more developers are starting to realize that the types of bugs that hold them back can be reduced by using a more powerful language. But it's got a very long way to go, because there is a lot of unlearning to do. The article alludes to this a bit. For example, immutability is one of the key concepts in nearly every FP language. Functional programmers take it for granted, but it's transformational and completely changes the way you have to solve a problem. This hump is often too big for many people to get over, at least in their first trial with a functional programming language. It's hard to solve a task you know how to solve when you now need to re-think your approach because the language doesn't let you change x in-place.
Additionally, I believe that many programmers aren't necessarily aware of all of the problems they're having. The next time you hear someone say, "and make sure to check for null here", watch them to see if they're seeing that as a general problem hurting their ability to get bits out the door or not. In my experience, it's not usually viewed that way. Yes, null is a necessary evil in many environments, but it's one that can be tamed, and different languages have already figured out how to do that. But unless it's seen as a problem by people at large, they won't see that as a carrot when you wave things like Optional types in front of them.
Overall, I'm still excited. Jet.com is a billion-dollar company that built their stuff with F#. There are similar stories of companies successful with F# (and other functional programming languages), and it feels as though this is happening more often. And as the mainstream (read: C-style languages) adopt more functional programming techniques, I think this will only get better. I think that people will see the value, then see a language which does it better, and give it a try.
I hear this a lot with FP but would love to see some real world examples of this if anyone has any links.
The draw to F# for me, when I was learning it, was I needed to build a DSL in a large system I was working on (side note: there must be a law somewhere stating that any large system will have a DSL in it somewhere). I didn't know about libraries like FParsec or anything (or a C# equivalent), so I just googled "how to write a DSL C#" and ended up somewhere showing F# code. I was curious.
A week later, I wrote the whole thing in F#, with tests, and all requirements were fulfilled. Upon re-writing in C# (because reasons...), I had to re-think the problem. I used F# types and pattern matching everywhere, and Active Patterns to make stuff really concise and readable. A lot of the branching logic in my C# rewrite didn't really exist in F# because of this, and it was really hard for me to track down how everything worked in C#. I'm still convinced that someone unfamiliar with F# could understand the F# version better than the C# version because of how much less code and branching logic was involved.
For a more professional case study, Microsoft Security Risk Detection is a fuzz testing service written in F#: https://blogs.msdn.microsoft.com/dotnet/2016/12/13/project-s...
We blogged about their experience, and they do have practical examples there.
C#:
ResultObject MyFunction(SomeObject some, AnotherObject another)
You have no idea if every value you pass in is required, or if you would always get a ResultObject or also need to expect null. Could possibly throw an Exception as well.Swift:
func myFunction(some: SomeObject, another: AnotherObject?) throws -> ResultObject
Forces you to give SomeObject, but you can pass nil to AnotherObject if you would like to, and the code inside this function will be forced to deal with it. Also it will always return ResultObject unless it throws an Error.You will have no surprises using Swift while C# forces you to guess unless you know the inside of the function. F# works the same as Swift in that regard. You need to be explicit about nullability and mutability, and if it is nullable you are forced to deal with it it before you use it. Immutable and non-nullable is just easier to use.
The leap between C# and F# is much bigger because C# is really good and F# is a real functional language where Objective-C was older and Swift is less functional. Same goes for Java vs. Kotlin. Android Java is terrible compared to C# while Kotlin is much more similar in syntax.
http://fsharpforfunandprofit.com/posts/is-your-language-unre...
AttributeError: 'NoneType' object has no attribute 'foo'
(or its equivalent in your favourite language)?* sum types
* parametric polymorphism
* pattern matching
at which point you may as well also add
* first class functions
and then if you don't describe your language as "typed functional" then how do you describe it?
class Power {
public static HoursUsed(
float totalBillDlrs, float deviceWattage, float rateDlrsPerkWh) {
return 1000.0 * totalBillDlrs / (deviceWattage * rateDlrsPerkWh);
}
}
Then call it like this: var totalBillDlrs = 10.95;
var deviceWattage = 100.0;
var rateDlrsPerkWh = 0.15;
Power.HoursUsed(deviceWattage, totalBillDlrs, rateDlrsPerkWh);
But, oops! that was actually supposed to be: Power.HoursUsed(totalBillDlrs, deviceWattage, rateDlrsPerkWh);
In F# you can use units of measure to prevent this kind of thing at compile time: [<Measure>] type dollar
[<Measure>] type watt
[<Measure>] type kilowatt
[<Measure>] type hour
module Power =
let hoursUsed
(totalBill : float<dollar>)
(deviceWattage : float<watt>)
(rate : float<dollar/(kilowatt hour)>) : float<hour> =
1000.0<watt/kilowatt> * totalBill / (deviceWattage * rate)
let totalBill = 10.95<dollar>
let deviceWattage = 100.0<watt>
let rate = 0.15<dollar/(kilowatt hour)>
// Compile error
Power.hoursUsed deviceWattage totalBill rate
And, yes, in C# you can use things like named parameters to reduce the chances of something like this, but you can't eliminate it at compile time, which is what F# does.In such situations, I wonder if something that came into being organically and in the open (and helped on by a sugar daddy) has better chances/purchase.
"For long time .NET developers, a common question is "when will Visual Studio support F# .NET Core projects". We’re working hard to get this functionality ready, but unfortunately, we’re not quite happy enough with the quality in the 15.3 update to announce that it’s fully supported. While Visual Studio 2017 version 15.3 is able to open the new F# .NET Core projects, build, and debug them, IntelliSense does not yet work correctly"
From
https://blogs.msdn.microsoft.com/dotnet/2017/08/14/f-and-net...
And this is the reason why none of our customers wants to even discuss integrating F# into C# projects.
https://docs.microsoft.com/en-us/visualstudio/ide/editorconf...
I'd say this is the main problem with the programming community at large, in 2017.
We ended up choosing Scala for some new projects despite having an inferior IDE experience and being unable to use some of our other libraries. I don't think I can emphasize how much not having type classes and HKTs hurts productivity once you buy into the fully functional mindset. We were extremely productive in Scala because the language was a bit more powerful.
I would love to use F# again, but not until the language adds those features.
1. IDE experience is much better.
2. I like the fact that F# emphasizes the functional aspects, it's a bit of a 'tighter' language than Scala (despite the two crucial missing pieces) and so I think people could learn it a bit faster. Scala has things like structural typing, all kinds of optional syntax, multiple inheritance, etc.
3. Lack of flexibility in syntax is a plus imo for F#. Some of Scala's flexibility is nice, but some of it just confuses the newbies (like the various usages of `_`).
:(
Ocaml folks have these. Haskell folks have these. Heck, even psuedo-functional languages like Clojure and Scala have these.
The other problem is one of tooling (disclaimer, last time I tried this was just over a year ago). F# not only has pretty obscure tooling, but in conventional tooling it introduces a total linear order to your entire source tree, and it's one YOU must maintain with a GUI list. What's more, the F# community is very keen to disseminate the language.
I think F# would be more popular if it did more to differentiate itself from imperative programming. There is a longstanding argument that this is "unfriendly to data scientist", but I'm really not convinced by this, as I meet very few mathematicians outside of the stats world who don't on some way, shape or form espouse functional programming.
Eta will never even approach Scala in terms of adoption, and I had only heard about Purescript a few weeks ago reading about Elm.
Typescript will dominate functional JS-world (already is, tbh) and Scala is still crushing function JVM-world, and if Dotty ever actually materializes, adoption will get even better. Additionally, Scala-Native and ScalaJS are making progress and could eventually be real players. Not likely to take food away from Haskell or Typescript, but they will certainly maintain larger usage than PS or Eta.
edit: I'll concede that Clojure matters a bit in JVM-world, but it's dying quickly.
I'm not defending how obnoxious it CAN be to write Scala, but say it's stagnant is just dishonest or misinformed. Or both.
Also SBT. Fuck sbt.
It's difficult to point to a more poorly managed language project than Scala. The net result is a mess with tons of gotchas, performance pitfalls, technical debt and a 3-way split tie in the community.
Scala has driven away a lot of brilliant people.
My dad has been writing COBOL for 30+ years, but I think we could both agree that it's a dead language in a certain sense.
My first language was Scheme. I wrote some stupid BATCH scripts at school and decided I wanted to learn to program. I worked my way through half of SICP, etc etc. I LOVE Lisps. I was rooting for Clojure a few years ago.
However, I think Clojure is embracing types too little too late. Large systems are hard to do in dynamic languages. There's no easy way around that. Sure, immutability helps, and having a strong style guide helps and having strong interactive programming helps, but it never goes away that it's harder to do when you can't reason about the structure of something without evaluating that thing.
That there are more Clojure jobs sprouting up as development is drying up screams disaster maintenance to me.
I'd love to be proven wrong. I would LOOOVE for Clojure 2.0 to fully embrace a strong type system and maybe some stricter semantics but it just isn't going to happen. Very few people are familiar with Clj internals, fewer people still are still working on it, and fewer people still plan on continuing that work for a long period of time.
Call me crazy, but I think there are two major threats to Clojure's current reign in the enterprise lispverse.
Racket's rewrite to Chez. That's big. That will mean big performance gains and I think could provide incentive for someone industrious to wrangle some of the crazier parts of racket into a coherent biz-ready language.
The other is Elixir. Now, I hate elixir but people seem to eat that kind of stuff up. They have a strong macro story, BEAM is very powerful in the right hands, and as time goes on it becomes more and more clear that it's easy to write high-concurrency, low-latency, SMP-enabled systems in Elixir.
Elixir doesn't have a type story (racket does, but it's kinda lame), but that never stopped Clojure up to this point.
Also, Clojerl (clj on erlang/otp) exists, which could strengthen the motivation for moving away from Clojure by making pure-clojure applications vulnerable to completely automated porting thus eliminating the need for clojure maintenance programmers.
That's my take on that whole situation.
Is it? Do you have evidence of this?
So ScalaJS could be a "real player" but PureScript is barely able to exist? Forgive me if I don't just take your word for it.
I'm not making this shit up out of thin air, look at usage numbers.
Purescript is not going anywhere, most likely ever.
I know you aren't OP, but it doesn't sound like they have jack shit to back up their statements.
The ionide project is doing tremendous job of support on vscode.
It has improved compared to year, and definitely much better, in a month or two all the major IDEs including MonoDevelop will likely have .net core 2.0 support with F# baked in (speculation on my part).
#ME: "Hey that stuff at pycon was pretty cool, huh?"
#FRIEND: "Yeah it's interesting the directions they're going with the language."
#ME: "Yeah the optional types could help clean up big code bases, and open up the door for quickcheck-like fuzzy testing."
#FRIEND: "Yeah."
#ME: "And some of the functional programming stuff is pretty neat too, I know it's easier for me to test."
#FRIEND: "It's so much easier."
#ME: "And the asyncio stuff is gonna be really nice to work with when it catches on more."
#FRIEND: "Yeah it seems a lot more ergonomic of a solution."
#ME: "And the binary distributable packaging tools look like they might actually work this time."
#FRIEND: "Probably not, but we can hope."
#ME: "I think I should introduce you to my friend F#."
Then just do some stuff in fsx with a type provider or some other neat stuff and at least you've got the message across.
People get excited about solutions to their problems. If you start giving them more problems then you're only adding to the constant subconscious cognitive load that they bear in regards to software development.
If you show it to them in terms of problems that they already have, you're reducing the cognitive load. Everybody loves having less stress.
But this is just how I approach it. F# is like Python4 for me.
I mention it just because I've had almost a carbon-copy of that conversation but I showed them Julia and now that person actually is writing Julia for their startup. That person is doing technical chemical engineering stuff, so the fit is a bit more pronounced, but I think it's a neat language aside from that. Parametric types and multiple dispatch and lisp-macros, oh boy!
I used to dismiss this as academic noodling till I got tired of OO and started going back to my procedural roots with just plain functions organized into namespaces. I was pretty happy with it till the codebase grew to a point I couldn't keep track of the flow of data. Objects help you to some extent here since they group related values into one instance, but I didn't want objects anymore.
This was when I started seeing references to OCaml, especially "Why ML/OCaml are good for writing compilers" (http://flint.cs.yale.edu/cs421/case-for-ml.html), and also saw a presentation by a programmer I respected where he built a general game playing engine in OCaml. Seeing someone use it for practical, advanced programming was the first catalyst.
Then Jordan Walke mentioned somewhere that he first prototyped React in OCaml, and shortly after they released ReasonML. The type system was amazing. All values are typed, even complex nested structures have a type. There are variants which mean I can express different shapes of the same value using types, and the compiler will tell me if I ever forget to handle a particular case of this variant.
I've been programming with Reason/OCaml for about six months now, and Typed Immutable Programming still takes me more time than just gluing together a pack of cards with imperative code. But once the code is written, it sits on an iron-clad foundation.
Most systems as they grow start to fold under its own weight, but here the accretion of code is an almost unmitigated good thing. It means my system's cognitive wealth is increasing without an exponential increase in maintenance cost.
But how can you help someone understand all this in a single conversation? A lot of people come to this because they worked on systems with pain points that Typed FP can address. But for most others who haven't had that sort of clarifying experience, what can you tell them to give it the non-trivial amount of effort it requires?
A lot of debugging is: This <object> has ended up in a weird state. Whereas the alternative could be that if the code compiles then it's impossible for that <thing> to ever be in a weird state.
F# has a huge opportunity in the enterprise. A ton of enterprise C# code is not object oriented at all, it's big, poorly thought out clumps of imperative script wrapped in methods and classes so it can be compiled or fit into the framework that's calling it. Sometimes it'll get a quick last pass run on it to make it "more object oriented", because that's what C# code is supposed to be; this usually just makes it worse. It's written in a scripting mindset: the developer needs to get things done, and adds code and bangs on stuff until it compiles and looks right when it runs in the debugger.
F# is actually well suited to this style of development, but it's hard to find anything about it, because every F# tutorial, book and blog post is actually about functional concepts, functional design, functional thinking and functional programming, including this one.
If you want to sell F# to enterprise developers, show them how they can write their C# code in it without having to pretend that it's object oriented anymore, and how that's a good thing.
As enterprise consultant, for the types of customer projects I work with, lack of tooling support for Blend, WPF, UWP, EF, ASP.NET is a no-go to move from C# into F#.
There is no barrier to mingling F# and C# libs to focus on specific tasks or parts of the app. Having application libraries in C# and domain code in F# is a common pattern. Particularly at the GUI level, extra-particularly with anything that uses databinding so your domain types can be used directly.
F# does not get the same amount of tooling love as C# for the "latest and greatest" from microsoft. If you look at the Enterprise track record of Microsofts "latest and greatest" since they started pushing ADO.net to present day... well, I would argue that's a huge benefit. Enterprise customers need tooling stability and platform stability, so letting ASPvReplacedInThreeMonths come and go without impacting projects is a big money saver.
Try that on .NET Native apps.
> F# does not get the same amount of tooling love as C# for the "latest and greatest" from microsoft.
No tooling for Blend, WPF, UWP, EF, ASP.NET, no reason to switch and suffer a productivy drop.
Furthermore, I have a few years of RAD development experience, and I know as well as you do that not everything may be coded with a couple of mouse clicks. As soon as you hit the business logic, you need to use the keyboard, you need to devise algorithms, you need to evaluate their complexities, and you need to code them down.
That being said, even just getting used to a new language gives you new insights about coding practices.
> Try that on .NET Native apps.
For a real-world workaround, here: https://docs.microsoft.com/en-us/windows/uwp/porting/desktop...
To quote from the thread: [by] “.NET Native doesn’t support F#,” what’s meant is “.NET Native doesn’t support all of the IL produced by the Visual F# compiler.”, which is one of the MANY reasons MS is now allowing UWP variations.
> No tooling for Blend, WPF, UWP, EF, ASP.NET, no reason to switch and suffer a productivy drop.
This is a reply to a comment saying "Having application libraries in C# and domain code in F# is a common pattern. Particularly at the GUI level, extra-particularly with anything that uses databinding so your domain types can be used directly."
That directly addresses what you're talking about. And, because I've done it myself: it's not entirely true what you're saying here. Those languages bind and analyze .Net objects. What you're talking about is entirely GUI level, and already recommended to take in C#.
The domain modelling productivity advantages CRUSH any gui nonsense. That's before we look at stability and correctness. And error rates. And maintenance productivity.
At the same time, to be blunt: there's no reasonable, scaled, approach to developing applications for any of those technologies that doesn't involve separating domain logic and GUI logic. Using DDD, MVVM, three-layered-app design, or any separation of concerns really and your app will already have the logical divisions required for a C#/F# split, so you can combine the high productivity on the back end with whatever button tweaking you require.
There is no "one best language" or even "one best programming style". Functional has worked for some people to address the problems they have, but not everyone has those problems.
For example: I once worked on a video switcher for TV stations. You have maybe 100 video sources, maybe 80 video destinations, and you can connect any to any. You also have about six different sources of commands to make connections. For responsiveness, each source has its own thread. So the nature of the problem is one giant shared mutable state. The functional people come along, and they say "shared mutable state is evil; don't do that". Yeah, well, that's actually not bad advice, but it doesn't help me at all with this situation, since I can't avoid it here.
There are other places where FP isn't the answer: Situations where you care about memory layout (high performance computing or just memory constrained), where one of the main concerns is sequencing (some industrial control operations, maybe robotics), where predictable timing is important (real time systems), maybe others.
Use the right tool for the job. FP is not the right tool for every job. Nothing is. The problem with evangelism is when it says "this is the One Right Way", instead of the humbler assertion "in some situations, this can help you with some of the problems that are slowing you down".
Don't get me wrong. FP has a good story. (Even if you don't use FP, learn its deepest lesson: Control your state space or die.) But FP is not always the answer. Evangelists might get further if they recognized that.
#1 tooling: visual studio integration of F# is poor. You don't get cross language usage reference, so you lose all the benefits of static typic. Projects and references are a mess. Being primarily a scripting language, it feels like a toy language
#2 compatibility with C# is poorly done. That F# functions cannot be consumed as C# lambda is a real mess. Boxing / unboxing doesn't feel FP ish at all. You often need to implement classes with interfaces to interact with C#'s core libraries.
Could you clarify what you mean here? Although things like Go to Definition and Find all References don't work across C#/F# boundaries (yet), but you absolutely get IntelliSense and the benefits of static typing. F# isn't a scripting language, although it does support F# scripts.
Also the benefits of static typing are that it makes it easy to refactor. Here if I change things in C#, I need to grep my F# scripts for usage, it's a real pain.
This is actually added in F# tooling for VS 2017! Both via a lightbulb code fix and with IntelliSense itself. If you have a reference to a .dll with symbols, the IDE can suggest an open statements for you.
I don't see F# as primarily a scripting language at all. It builds executables just as well as C# does.
F# and C# classes are very compatible, since they're both .NET. If you expose F#-only features (e.g. currying), then obviously C# isn't going to be able to use your API as well.
F# is the first language built on top of a mature .Net framework, and it shows. Much of what has made C# become so great was pushed through the F# research group (remember when C# didn't have generics?... yeah...).
It is not Microsofts favorite child. It will never be the implementation language of MS's next GUI platform du jour.
Personally, looking at old XAML, Silverlight, EF1, EF2, EF3, EF4, EF5, and EF6 projects... [oh hey, EF7 is out, and it _still_ has no second level cache story! Back to NHibernate everybody!... again!] ... I do not think it's a bad thing to have some distance between production and Microsofts first-run attempts at products.
I have to say that messing around in visual studio in F# just now was extremely painful. I had to press shift+enter to add a new line, just enter did nothing. Tab didn't indent. I don't know what indents. I don't know what key does the job backspace is supposed to do (delete the previous character). I don't really understand why everything is so hard. I don't have this problem with Visual Studio C++ files. O_o
To be honest I'd prefer using VS Code to write F# and build from the command line, do you know of any good getting started guides? I'll check the VS Code extension documents, maybe now that I have it working in VS Code this will be easier.
Thank you for your help so far!
VSCode and F# is a great experience! We have some guides about how to get started with F# here: https://docs.microsoft.com/en-us/dotnet/fsharp/get-started/
The first in person F# conf in North America (to my knowledge) Open Fsharp Conf. is on Sept 28, 29 in SFO. (http://openfsharp.org/).
My only association is I bought ticket for it.
By doing this, you separate yourself from the listener, giving a clear delineation between the noob and the l337 wizard.
It also helps to feign surprise, "you dont know about monadic entropy lenses?" or use the word _just_ as in "Just run the parser combinator in reverse, its easy".
For many programmers, the axioms of what a function are (a calling point to code which is contravariant in a product and covaraint in a single return value), what objects are (a unique closure over functions values with a syntactically supported lookup of named values and a system-provided identity function) are so unvarying in their experience as to be synonymous with the word we use to describe it without needing further quantification. That gets tricky when you want to say, "Ah, but this unique way of thinking about this datatype lets us pretend it's a function and then we need to talk about what it needs and what it provides in more general terms."
Since functional programming is actually all about completely tearing down the world to everything but a series of functions, it's unsurprising it needs different words.
Finding a way to entice users to join in the functional party is really difficult because asking a programmer to do it is roughly akin to asking them to learn to program again. And don't get me wrong, I think you emerge from the other side of it better and more productive in every way, but it's difficult to prove that and more difficult still to convince people of it.
A neat example of the later: dependency injection is a massively more complicated concept than just passing functions that provide a value, while providing even greater flexibility and more obvious reuse.
When I showed how to make an object system using closures, they were like, "Cool!"
Some people start with the math, some people finish with the math and others deny the math. But we can all be better programmers with functional programming. If our goal is to educate, our actions should be the opposite of alienation. Some people gravitate to FP because it is seen as hard, as bad-ass and want to show their mastery to others. Those people should probably not be teaching FP.
I'm slowly recording several sessions, and I think about 2 hours in enough to introduce Purescript, explain the basics of Functors, Applicative Functors (which come nearly for free), and even explain, motivate, and use monadic functions. But I don't know how to do this without at least giving folks some words (Functor is a name as good as any, and every other name for Monad is at last as if not more intimidating
It's just at some point if we observe the shape of what we're doing, we can generalize in a useful way further (e.g., the lenses-banannas-parethesis approach).
They've come up with different words for things, and it drives me nuts. Specifically mapreduce: They call filtering mapping, and mapping reducing. I think this is where Redux gets it's "reducers" from. They aren't reducers damnit! It took me weeks to figure out what the hell they were talking about.
We're now seeing the React community go bananas over "Higher-Order Components." It took one look at it and realized I was looking at Reader and thought, why all the verbiage?
Now I write my React applications using GHCJS and I don't have to mess around with reading terrible backtraces and all the detective work of figuring out how a type error manifested itself. But these communities are forced to repeat history instead of learn from them.
The salient point TFA made for me was that language and evangelism are part of the problem. You can't dive into Haskell if you've been a C# programmer for the last 10 years and expect to have the same level of proficiency you've enjoyed all that time. The most effective way to learn Haskell, F#, et al is to literally start over. And that's a hard sell.
The benefit is that these languages weren't invented: they were discovered. By learning them you'll be learning foundational principles from first-order logic, set theory, and category theory that will make you a better programmer. You can take these principles to any programming language and they will continue to work because it's all maths.
From: Florent Crivello @Altimor https://twitter.com/Altimor/status/873648344425299968
So first make F# a first class citzen on .NET and maybe many C# devs will look again to it.
MS hasn't funded F# enough, and has crippled the marketing on it to continue to push C# rather than admit they've made mistakes. The tooling issues and other random incompatibilities are why I don't use it as much as I used to - it's too much friction.
People at Microsoft repeatedly said "Simple Code for Simple Problems". C# programmers don't like repeating that.
I'm only repeating it to point out that MS itself did poor marketing for F#, clearly favouring C#.
People can have awfully cavalier opinions about switching languages, but it's a very heavy-weight change with strong implications for existing code, dev training (new syntax, new patterns, new pitfalls), available frameworks (often resulting in more new patterns), technology stack (which can reverberate out to testing, scalability, etc.)
Even if you assume F# is only better than C# and in no way worse, it's usually major investment to switch... typically at least an order of magnitude bigger than that bullet list of ways F# is better than C#.
Anyway. I work in a c# shop with a large monolithic code base developed by a small team with lots of fluctuations over more than 10 years. I have no functional programming experience at all.
I love c# because we actually try to keep the code as simple and linear as possible and only introduce complexity in certain areas if performance requires it. Yes there are hacks and dirty code passages, but if you stick to basic code constructs they are easy to decipher.
If your facing more than 10 million lines of code you start to appreciate simple code. Moreover if different developers, which come and go, are working on it. Living without refactoring and inspection capabilities of resharper is nearly unthinkable to me.
Now, how would you convince someone like me to try out f#?
There are only drawbacks: Obscure things we would need to learn (type classes? monads? xyz?). Probably hard to read, 'clever' code. A mixture of c# and f#. Unknown library, build and deployment issues. And I guess, f# developers are rare around here.
Even after reading this post, there's unfortunately no convincing reason for me and management to give f# a try...
Trivial example: what if you could do away with exceptions but still be able to "bubble up" errors in a lightweight way, so that your error handling still wouldn't get in the way of your straight-through business logic code? "Everyone knows" to never use exceptions as control flow, but it happens by accident anyway - have you hit bugs because of that? (I have). If you could avoid having them in the language, wouldn't that make things simpler?
You have less need to learn things like monads because they're just plain old library code; if need be you can click through to the function definition, just like any other function you call. That for me is one of the big things FP is about: reduce what's in the core language, push stuff out into libraries instead. There are a few language features to learn, sure, but they're language features you wanted anyway. Type classes are a small feature, and boil down to just a slight bit of extra syntax support for the strategy pattern. (Indeed I find most of the FP features come down to taking classic OO principles - SOLID, composition over inheritance, and the like - and actually following through on them).
And maybe as you get comfortable with it you'll be able to turn those 10 million lines into 5 million, or 1 million, or less (and maybe those lines are slightly more complex than the lines they replace, but that's a good tradeoff). Certainly that's what using Scala has felt like for me.
http://fsharpforfunandprofit.com/posts/is-your-language-unre...
Large projects in OO often end up as mutable monoliths which become harder and harder to work with. New features sometimes require large painful and scary changes. Hacks are irresistible. Of course you need refactoring tools because you have to change many things in many files.
OO promised modularity but the languages never delivered because of their unreasonable design.
Highly reasonable, concise and refactorable code is the answer to complex systems.
I'm happier writing Typed FP because the system is very concrete in my head as I work with it. In a dynamic language, there is always a nagging uncertainty about the system - if you're using dictionaries to pass data around, you don't have certainty in what keys it'll have, and what data will be available inside it, and what could possibly be null.
When you add a new kind of a domain object (a Guest user in a user management system for example), you have no idea where all you need to handle it. You fix the obvious places, and do a text search, and find a few more. And the rest you fix over time.
The runtime robustness of untyped systems in my experience generally is a function of time. The more it sits in production, the more bugs we discover in the most commonly trafficked codepaths. We fix them - duct tape works - and then it sits and chugs along. (Robustness to change is another matter altogether).
But if you use a Typed Functional language, when you introduce a new domain object the compiler will tell you exactly where they are being used and will refuse to compile till you've made sure you're handling it in all the right places.
Value-based programming (ala Immutability) lets you write code as if you were writing mathematical definitions (high-school mathematics; no category theory or monad). While we can be disciplined about not writing mutating code, there is a real difference between 99% confidence and the second-guessing it entails, and just not having to worry about it at all. Everything is a value. There are no objects/references.
All this adds up to a state of clarity when you program. You don't have to keep second guessing yourselves; you don't have to do defensive checks that makes you feel a little bit ugly inside; you don't have to deploy worrying about the unknown unknowns that can break your system.
So my point is; it all depends on how you code something, the language may do it at compile time to prevent runtime assertions, but that also comes with extra baggage (I assume you need to write more code to define your types, function call conditions and mappings)
Is it possible to seamlessly integrate a user managment system in f# into existing c# code? I guess, it's not that simple.
It also reminds me of an experience I had in a former job with C++. There was a very, very experienced senior who wrote a very clever library which allowed us to add varous metadata to domain objects in a a compile-time checked, type-safe and very performant way. When the guy left, everything went downhill and the code from then on was regarded as mysterious black box noone dared to touch.
Somehow I am reminded if this when I read about 'magic' solutions in FP. Is it possible to reach a state with FP where seeing through it all gets too troublesome?
I do follow F# somehow, and compared to other languages .. it actually seem to have a very pragmatic and active community
The F# community build a lot of interesting frameworks, suave, websharper, type providers ... to name a few
I dont think, anything will look popular, when compared to Java , C# or Python .. but compare it to Haskell, Ocaml, Clojure.. Dart, D, Nim .. and the plethora of languages that compile to javascript , and you will see F# is not doing so bad
and they do need a good ide
if you go to the F# .org site, installing F# without installing visual studio, is a 4 step process .. and when I tried it on windows 7 it just didnt work for me
i eventually gave up and install visual studio on linux, thing seems better, but .. still missing a solid ide
Many people are not like that, and stuff like a small community, worse tools and toolkits and less programmer availability are actually a huge deal, capable of tanking a good project, whereas having a 10% better language isn't a huge gain.
It's that simple.
I eventually tried F# because it was close enough to OCaml and while I was learning OCaml I always referred to this site http://fsharpforfunandprofit.com
I'm definitely an F# evangelist at work, but we are not a .NET shop. But I was would say that the introduction of ML features in other languages, (Swift, Rust) are good for F# evangelism, because it makes these concepts more familiar. I think the focus should be on moving F# away from .NET because F# itself, it is much more accessible that its cousins OCaml and Haskell. And if Microsoft won't give proper support, the community should move to other platforms. Some discussions are already happening here https://github.com/fsharp/fssf-ask-the-board/issues/4
I've actually written and run JScript scripts on Windows Script Host with BuckleScript, it's really nifty. You write strongly-typed code and get a JS artifact that can run on your target platform. For the curious, an example: https://github.com/yawaramin/wsh-bs-test
I'm a C# dev since 14 years (single project) hoping my next project will be an F# one. That might mean I have to switch jobs - and I hope there are enough F# shops when I need one.
I recently had a chance to play with F#. After being completely lost for a bit, I found I had to really rethink how I was reasoning about problems and the right way to solve them.
But that’s the point I want to make. I don’t expect to be good at FP for a while - it’s going to take time to grow, along with the tooling and community. But I appreciate having a way of framing a problem differently, and like the direction F# is headed.
It doesn’t mean I’m going to drop C# any time soon, I’ll likely just use both - e.g. my more math heavy libraries will be F#, and my networking and Web API code in C#.
It's already F# 4.x, but I can't remember any significant new features in last 3 releases at least, neither in language nor in the standard library.
For features added see https://en.wikipedia.org/wiki/F_Sharp_(programming_language)
Named union type fields Extensions to array slicing Type inference enhancements Printf on unitized values Extension property initializers Non-null provided types Primary constructors as functions Static parameters for provided methods Printf interpolation Extended #if grammar Support for 'fixed' Tailcall attribute Multiple interface instantiations Optional type args Params dictionaries Struct tuples which inter-operate with C# tuples Struct annotations for Records Struct annotations for Single-case Discriminated Unions Underscores in numeric literals Caller info argument attributes Result type and some basic Result functions Mutually referential types and modules within the same file Implicit "Module" syntax on modules with shared name as type Byref returns, supporting consuming C# ref-returning methods Error message improvements
.NET Native and UWP are not just UI,
> but your claim the language is not improving is wrong.
You're confusing me with the parent commenter https://news.ycombinator.com/item?id=15029008
I'd be interested to read a more in-depth description of the recent project which the OP chose to implement with F#.
First let me address those who are asking for Type Classes and Dependent Types. These would make the compiler too slow. The F# compiler works in the 100s of ms and the 100s of MBs ram. Where Scala is in the 1000s of seconds and the GBs of ram. People will give up on code completion if it takes more than 500ms and they generally don't like waiting longer than 200ms. Separately Martin Odersky (the creator of Scala) laments that the more advanced language features attracted the wrong type of people to the language and flooded the internet with overly academic tutorials.
I put a lot of the blame for the lack of F# adoption on Microsoft. Part of this is to do with the Dev Div vs Windows war that almost deprecated the whole .Net ecosystem. If Windows 8 wasn't such a disaster it's likely .Net would have been deprecated and would never had been open sourced. (at least something good came from Windows 8). Part of this war also ended with a lot of the best .Net people leaving to go to Facebook. This brain drain has really hurt Microsoft and I doubt they'll ever recover. Most of the old hands that know better are aging out and the new people at Microsoft don't know any better.
As for tooling, Microsoft has done and continues to do a bad job with F# tooling. There is is a not invented here culture at Microsoft Redmond which includes being hostile to Microsoft Research. Visual Studio is a bucket of bandaids and it takes an army of people just to keep it going. F# is given a low priority. It's worse with the latest release because they've gone overboard with compiler features with the Rosario release and have made it dog slow and painful to use. I hope they can fix it with updates but who knows. You can still get older versions. It's very possible to build a really good IDE around the F# compiler but unfortunately Microsoft refuses to do so. Thankfully, it looks like there are some good alternatives becoming available.
In comparing Scala and F# adoption, Scala has Hadoop/Spark which is a killer product that can earn a consultant a lot of money. The fact that C# is a better language that Java means there are more Java refugees going to Scala than there are C# refugees going to F#.
Thankfully Don Syme has made F# completely open source. Now with .Net being open source we no longer have to worry about Microsoft shooting itself in the foot again.
I make my money writing a product written in F# and can attest that F# is one of my big competitive advantages. I highly recommend it.
I'm 100% okay with criticism about things and think it's a good when people voice it, but this is not based in reality.
MSR (and Don) work together with different groups in Azure and and the developer division. It's not perfect - we are a large, multinational corporation with the same problems in communication across organizations as any large, multinational corporation - but calling the relationship hostile is misleading, harmful to the F# community, harmful to those of us who work on F#, and harmful to F# growth. I really don't appreciate spreading this sort of stuff.
They're eager to use the latest enhancements to C# that come from the functional world (e.g. lambdas, await/async), but it's impossible to get them to give up their mutable variables and null pointers.
See this link explaining the problem https://news.ycombinator.com/item?id=15026396
We wouldn't need to evangelize F#, if it just worked everywhere C# does with proper tooling support.
F# is way too entwined with .NET to be immediately useful to programmers familiar with Ocaml or Scala or Haskell, etc.
Some of the basic List., Array. stuff is simple enough, but then you start talking about interfaces and classes and members and all this stuff that doesn't really translate from C#->sanity very well.
I know people love it, but I've long considered C#/Java to be kludge languages on platforms they don't deserve. The JVM might be the most impressive software engineering marvel I can think of, and yet on top of that christmas tree stands a tall, stinky shit.
I'd love to see F# as a F#-first platform, and offer .NET interop as a secondary language feature.
I've also noticed a concerning trend in F# circles that """diversity""" should be celebrated. There is no merit in your skin color or your genitals. If you can hack well, you deserve to do so. End of story.
A counterpoint to the F# being poisoned by .NET is Clojure. Look how well Clojure did Java interop and how "not a big deal it is." Immutability everywhere, functional by default, and it feels painful to do things the Java way in Clojure. F# doesn't mind at all if you sink to .NET trickery and in some cases require it.
I could think of more, but off the top of my head, the .split instance method comes to mind. I have a Markov Chain kata that I spring on new hires to see how they do things, and .split is an instance method, which is counter-intuitive to how functional problems are solved because you don't just |> into it.
I'm waiting to see where Clojure goes with core.type before I abandon F#. I really WANT to keep riding the F# train but it causes quite a few headaches. Not more than it solves, but more than other solutions that I've considered.
To balance this out, I'll say something I really love about F#. Units of measure. Few languages have this, and the kind of numerical work we do means we really get our mileage out of UoM. I always see finance horror stories or like the excel rounding bug, it makes me wonder exactly how much money UoM has saved us from losing through some innocent type somewhere. Debugging money code is way easier when you can verify all the conversions separately from the logic. It feels like cheating, to be totally honest. I know there are big shops out there that don't have UoM in their tools, and I used to feel bad for them, but now that we have it, I can't help but NOT feel bad for them.
Anyways, yeah, F# isn't perfect. There's plenty to be said also for the amount of progress they make with language updates. It grows a crazy amount between major versions, and I haven't had too much problems yet taking old F#3.0 code and revitalizing it. Very pleasant experience in that regard as well.
Plus, if you're situated in a historical process (like we all are) then arbitrary things become important. Accidental weaknesses such as being perched on the CLR and tethered to the hopes, dreams and capabilities of common-garden C# enterprise developers invert strangely into advantages - in the case of F#, it makes for a very friendly, welcoming ecosystem.