I'm the lone F# developer in a shop full of experienced and talented C# developers. I have clout, but not enough to convince any of them that F# is a better choice for many of our purposes. It's frustrating, but I try to understand that people are just naturally resistant to big changes like this.
Having made the analogous switch from Java to Scala, I think it's the good devs that benefit the most. Functional languages give you more scope for factoring out commonality, avoiding repetition, reusing existing library functionality - exactly the things that make good devs better than bad devs.
I do, because many of our requirements are purely functional. E.g. Turn a bunch of database queries into a user-friendly report.
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.
Good article. F# would find much better adoption if there were more FP style libraries out there. I have tried to move to F# but it's a real uphill battle when pretty much all sample code and most libraries are in C#. I would do it if I was alone but it's hard to convince a team of the benefits.
I'm currently a C# developer, but have tried functional languages before. As mentioned in the article, I think a lot of people are satisfied with the functional capabilities C# already provides. I don't need to write functional code all the time, but if I can model a problem more elegantly using higher-order functions I will gladly do that.
I'd be interested to read a more in-depth description of the recent project which the OP chose to implement with F#.
The best way to get someone to tune out when you are trying to convince them to get out their . bubble is to casually introduce words that they don't know.
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".
It's unfortunate tho, because for example what word do I have for the generalization that we call "profunctor" other than a profunctor. Even the words we use to describe a profunctor are "contravariant and covariant" and these words OUGHT to be more applicable to programmers but are not.
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.
Good points but I'm curious: once you "emerge from the other side of it", where are you? In a nuts-and-bolts programming world where you now write better code in a language like C# that has limited functional features or in a more idealistic environment where you write Haskell or OCaml? The latter always seems to be a niche available to few.
You become very good at identifying ways in which to avoid writing error-prone loops. You also get a sense for how to use simple functions of various types rather than more abstract and purpose-built abstractions.
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.
We can sell the vision without sound like a Pythagorean-cult of mathematics (functional programming). Hell, once I showed some of Python programming brethren pure functions they were hooked, HOOKED I tell you!
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 think a disagreement about didactic methods and ordering is not what I was addressing. I just think at some point calling it a "Pythagorean cult" (and what, I wonder, are we murdering people over uttering?) is counter-productive.
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).
This is my biggest problem when trying to work in ecmascript and when people ask me if I know things like "mapreduce."
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.
I think Peyton and Co. were right to stick with the mathematical terms when they introduced various concepts to Haskell over the years.
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.
I think F#'s larger problem is that it doesn't dip into many of the wells that even OCaml (its original inspiration) has dipped into. C# developers are wrong that C#'s functional facilities are just as good, but it's a big struggle to point to a place in F# and say, "THERE! THAT IS THE BEST BIT!" The closest I can get is type providers (which are cool and I wish for them elsewhere) but they're kinda specialist.
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.
Sure but Ocaml and Haskell don't have .Net. F# is .Net for people who feel a bit gross in C#, it's not (specifically) to make people jump ship from the JVM ecosystem.
Neither are making "big strides". They both are barely able to exist.
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.
Scala is not even remotely stagnant. Scala programmers are becoming stagnant, but scalac hasnt experienced any major growth slowdowns, Scala native is progressing quite a bit, ScalaJS is still a thing, and Dotty seems to at least be implementing some of that new DOT stuff.
I'm not defending how obnoxious it CAN be to write Scala, but say it's stagnant is just dishonest or misinformed. Or both.
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.
This is where I'm at. When people ask me why I don't write Scala anymore, the best answer I can give is "it is not a language that deserves a The Good Parts book, but desperately needs one."
The fallacy there is that Clojure development is drastically slowing in favor cljs. Because there's jobs doesn't mean anything with regards to dev status.
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.
Clojure.spec has spread like wildfire and is offering tools to combat some of what you say. Goes further than type systems on some things. Is also amazing for generative testing.
Why do you say Clojure is dying quickly? That seems at odds with its community, which is continuing to grow and the language keeps evolving. I've seen several new companies and start ups in my area adopt it for their main work, just in the last year.
Tooling wise, tried Rider IDE from Jetbrains on Ubuntu, and out of the box it works (if you have mono + F# installed).
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).
I just don't see a lot of point to Mono in my domain. Even with the code inputs from .NET I've never gotten anything but sub-par performance from it for network services.
Very nice list of very practical problems functional languages like F# are meant to address. After a certain amount of experience, pretty much every OO developer will have experienced these problems, so makes for a good entry point to describe the benefits of a functional language.
I've experienced sort of a diagonal problem to this.
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.
But there's dismerit in monocultures - maybe those F# folks are pleased to be avoiding that.
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.
I actually work on F# at Microsoft. I've had success and some failures when it comes to evangelizing F# through Microsoft (but mostly success if my measures are accurate). I strongly believe that it can be boiled down to one major thing:
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.
It's often very difficult to come up with one, because there isn't really a silver bullet here.
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.
I can't give you any examples in F# from the top of my head, haven't touched it for a few months but I did ship software with it. I can give an example in Swift:
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.
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.
It's fair to say that the boundaries of "functional programming" are pretty amorphous and I certainly didn't mean untyped functional programming. In any case, if you want to prevent null/None errors in a way that gives the programmer his/her own access to create similar new abstractions then you need
* 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?
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.
At what point does big company politics become a drag rather than a lift ? F# is not Microsoft's important story in a way that Java was for Sun.
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.
Replacing the contents of my previous post regarding VS 15.3 support of F#.
"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"
This is for the .NET Core tooling support part of F#, and as we've stated in the blog post, we prioritized the quality of cross-platform usage here. You're free to disagree with this prioritization, but please don't craft a narrative about the relationship between F# and Microsoft that can mislead people as you did before.
Perhaps it's worth taking a different viewpoint. With VS 2017, no single release updates every part of the product to the latest things. For example, UWP does not support .NET Standard 2.0 yet. That did not put the VS 2017 15.3 release on hold. Similarly, although F# fully supports .NET Core and .NET Standard 2.0 with this release, the tooling quality wasn't high enough for us to declare the associated tooling support in VS as fully supported. That also did not put the VS 2017 15.3 release on hold. This because we're already three updates into Visual Studio, and it's only been 6 months since its release. Visual Studio updates faster and teams can release things easier and more rapidly without needing to wait for everything else. This is a really good thing.
> People, especially while learning, associate programming with specific constructs and idioms of C-style languages. It's incredibly difficult to break that barrier.
I'd say this is the main problem with the programming community at large, in 2017.
My previous job was working for a .NET shop that powered tons of automotive search engines etc. across the web. We moved to more and more functional concepts using C# as the team learned about them and had a lot of success with them. At a certain point, we explored using F#, but the lack of type classes & HKTs meant we kept having to repeat ourselves.
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.
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 `_`).
How can you help someone see the beauty and benefit of Typed FP without asking them to just trust you and spend a few months with it?
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?
I'm not in the Typed FP world, but I found the concept of pervasive use of Sum Types making invalid states (according to the project domain) of composite structures _unrepresentable_ to be a pretty compelling nugget to chew on.
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.
I honestly don't think there much wrong or a problem with F#
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
I will just make one criticism ... it is sadly somewhat awkward to install F# even on windows
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
This article retreads common complaints about the F# experience for outsiders that I've seen numerous time. I'm going to go a different direction.
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".
I disagree with this, you can sell F# to C# developers - things like type providers for SQL checking their query strings in compile time and no extra build step to produce SQL type definitions vs monstrosities such as Entity Framework... if they've been burned by ORMs to drop to stuff like Dapper they'll love F# type providers.
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.
C# developers in my shop loathe the SQL type provider because it requires them to have a compatible SQL Server database schema at build-time. The extra level of organization required is more than they can accept.
Does the F# SQL type provider integrate with the .sqlproj database projects that are in the solution, or do you have to have an actual running SQL database?
Why? Compilers have to read source files anyway. What's wrong with reading other stuff as well? For example, there are type providers for XAML, JSON, app.config, etc. Do they also seem wrong to you?
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.
Actually, for the providers from FSharp.Data.TypeProviders, it's only necessary when you need to pull a schema change.
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.
> Functional programming is also seeping in but again tons of boiler plate.
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.
Actually, higher kinds could make you write more composable and reusable GUIs. I don't see why "prettier" is the only metric of interest.
Immutable records also makes it easier to share data safely, thus making scaling across cores easier in some cases, without worrying about plan interference.
I'm interested to know why you describe EF as a monstrosity? Genuine interest, I just started using EF for the first time a few weeks ago for a work project so I've only scratched the surface but it didn't seem terrible, although I did find that only being able to update all tables after a change to the database as opposed to a single table to be a bit of a limitation.
If you use code first it's very easy to get in a corrupted state where it doesn't know how to update the database. Otherwise it speeds up the basic tasks then it gets in your way to write more sophisticated queries.
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 all the EF projects I have been on it caused weird issues after a while. Code first projects don't upgrade anymore or the order of saving is wrong or performance is bad.
In pretty much all of them we ended up writing a simple wrapper around the database that was much easier to control.
EF is great when things are small and simple. It is when things get big and complex that it can be a hindrance both to understanding what is happening (when does my code actually hit the database?) and how it happens (what query is being generated here?)
I feel the same way about all ORMs I've seen (minus micro ORMs which I feel are just inferior to F# type providers but work in same spirit) they just try to map two semantically incompatible data models and try to make it look transparent with a bunch of needles complexity - when it ends up failing you're stuck with something that's a hell to debug and tweak.
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.
> Immutable data is also becoming a common pattern even in C# but it's tedious as hell to write.
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.
In C# 6.0 you can also make them properties instead of fields, though I have no idea if this works with your "Record" class.
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;
}
}
> In C# 6.0 you can also make them properties instead of fields
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.
C# 7 does allow one to shorten that class a bit further. I saw this trick in the MEAP[0] of C# in Depth.
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.
> Encapsulation, allowing to change the internal representation without requiring clients to update or recompile their code
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.
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.
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.
>If you need `With` functionality, then add the following member:
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.
I take this approach in my language-ext project [1]. I wrote it because as well as wanting to use F#, I had a decade old C# project to maintain, and I wanted to bring functional techniques into. We're (my team) finding that the pull to to F# is diminishing quite significantly; this is partly due to the functional framework features I've added to lang-ext; partly the new features of C# (expression bodied members, tuples, etc.), and majorly the relatively poor tooling.
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).
I don't think that's correct. I think the main strength of F# lies in its interoperability with C#.
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...
#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.
> #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
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.
It's the "Go to Definition" and "Find all References" pieces that are so annoying to me, and slow me down tremendously. It means that (practically) you have to do your whole solution in one language for it to feel like a unified solution, and that makes it hard to start by bolting an F# project onto an existing solution.
And FWIW, I remember complaining about this to Don Syme something like 5+ years ago, and while he gave lip service to the idea that F# needed better tooling (and better tooling interop with C# specifically), I don't think he was really convinced.
You get intellisense alright. But using C#, I never need to #open the namespaces myself for it to find the types I am using or the extension methods I need. (Maybe this is because I use resharper, but still, it really slows me down).
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.
> You get intellisense alright. But using C#, I never need to #open the namespaces myself for it to find the types I am using or the extension methods I need. (Maybe this is because I use resharper, but still, it really slows me down).
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 hardly think the lack of a cross language usage reference means that you lose all the benefits of static typing.
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.
This is how my C# developers feel as well. Personally, I've concluded that F# is a more reasonable language than C#, so I see these "annoyances" as challenges that are not a big problem.
Miguel de Icaza commented on a trend they've seen with the C# devs on Mono/Xamarian that matches well with my experience: the C# devs who start using F# simply do not come back.
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.
This is what I agree with. I would love to write F#, I write Haskell on Windows, I love functional programming, but jesus the tooling around F# is broken, at least for someone not familiar with it. I can write, compile and run a small example program in C# or C++ in visual studio in minutes, but last I tried, even after spending half a day looking through how to get it done, I couldn't get the F# tooling to work for me. It just didn't seem worth exploring further to me at that point.
Could you clarify what you had problems with? F# comes with Visual Studio (and VS for Mac), has templates, and builds/runs/debugs/etc out of the box. No need to configure beyond checking "F# language support" in the installer for VS 2017 (and in some cases, the Workloads in that installer come with F# already-checked).
Thank you, that was a really helpful comment! That seems to have been the problem. I had Visual Studio installed already and I just reran an installer and that gave me the option to install F#. That I needed to run the installer for visual studio again, that I couldn't just install F# support some other way, that just wasn't clear to me.
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.
Good post. I really hoped for some f# code after listing all those scenarios where c# fails.
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...
As someone on the JVM side: I find the biggest advantage of functional programming is being able to replace "magic" - reflection, bytecode manipulation, AOP - with plain old code. If you can get everything you need to get done in plain old C#, you should probably stick with that. But if the most error-prone parts of your program are the cross-cutting concerns and the not-quite-C# you use to handle them - things like error handling, transaction boundaries, audit records, access control, that tend to be done with a "magic" API that doesn't follow the normal rules of the language - then maybe F# could help you by letting you replace those things with normal code. Slightly fiddly code, perhaps, but I'll take fiddly normal code over AOP-style magic any day.
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.
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'll tell you my experience. I never liked the arguments about reduced bugs or concise code. As a programmer the reason I would use F# (or OCaml, or Haskell, or Elm, or PureScript) is because it increases by day-to-day well-being.
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.
In Delphi, I hide this kind of complexity behind methods which check everything for nulls and types at runtime. I imagine I'd do the same thing in C#, too. So if I have json = TJSON.parse('{}'); json.Node['something'].Nodes[0].each(procedure(pNode: TJSONNode) begin; list.add(pNode.GetValue<int>()); end); { Nodes and Node properties actually create empty nodes and keep a debug string to show you where exactly your code failed so you can go back and take a look at which assumption was wrong. I then cleanup everything when the main node is freed.
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)
Your response raises some intersting points and sounds reasonable. But also kind of too dreamlike.
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?
Presumably it wouldn't be with your current project (since it sounds well-maintained and doesn't need a rewrite) but when starting on some other, entirely unrelated project?
I'm seeing lot of "I guess" or unknowns / assumptions here. All I'd suggest is actually try looking at it in bit of detail out speak to someone who's using it now - maybe that would dispel some of the FUD you're feeling.
Your preconceptions are certainly not unusual coming from the C# world, but they're generally just that - conceptions rather than actual reality.
I lost faith in F# the moment Microsoft decided it is not worthwhile to delay Visual Studio 15.3 to properly support it pushing devs to VSCode, and the lack of roadmap for .NET Native/UWP.
So first make F# a first class citzen on .NET and maybe many C# devs will look again to it.
I usually don't try to sell F# as compared to C# as noted in many other responses here. Since most of my friends work in other languages I usually just use python as my reference.
#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!
F# isn't generally solving problems... well, it can but rarely at the same level as it introduces problems.
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#.
Except for the people using Windows 10, developing with HoloLens, gaming on XBox ONE (after the latest firmware updates), or using Surface meeting boards.
That's because F# 1.0 was bascially a more powerful language than C# 10.0 (C# still doesn't have proper pattern matching for example, or discriminated unions)
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
You can gripe about your fav UI stack not being supported just the way you want (you don't seem to have any other complaints), but your claim the language is not improving is wrong.
I’m a C# developer, love the language and how easy it is to express what’s in my head as code.
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#.
Microsoft promoted this idea that F# is for mathematicians and engineers, so its no wonder that people pick this up. And I'm not sure who is pushing this idea that F# is not fine for OO work. It has all those features. So starting with F# as a "better C#" -- there's nothing wrong with that. Over time I think people will come to view OO as suboptimal, but in the mean time F# is just a better language to use than C#.
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.
No one who knew what they were talking about at Microsoft said F# was for mathematicians and engineers. It's a myth pushed by C# programmers and idiots. It's nonsense and you shouldn't repeat it.
People at Microsoft repeatedly said "Simple Code for Simple Problems". C# programmers don't like repeating that.
Doesn't matter if they didn't know what they're talking about, that was what was written on the Microsoft sites when comparing C#, VB, F#. F# was always pigeonholed into scientific and financial categories, never aimed at general code.
I'm only repeating it to point out that MS itself did poor marketing for F#, clearly favouring C#.
For me, I only got into .NET because of F#, and even then I pretend C# doesn't exist. I tried learning OCaml, it has cool features, like parametric modules and GADTs. But I always ran into the issue of irreproducible builds. This didn't happened in F#.
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
The difference being that Apple is quite vocal that Swift is the future of app development on their platforms, while Microsoft is trying to push UWP as the future of Windows apps (supported languages VB, C#, C++ and JavaScript).
UWP supports JavaScript? Cool, in that case a bunch of transpilers should work (once bindings are written) ... people can just use their preferred languages: OCaml/BuckleScript, F#/Fable, TypeScript, PureScript, et al.
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'll go a step further. There's a problem with language evangelism.
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.
I built Tsunami IDE. It is a F# IDE. I built it before VSCode or VS Community came out. I built it as a test to see if cost of Visual Studio was blocking adoption of F# and the ideas that F# codifies. The experiment was successful, I got my answer, and the answer is no. Even with free tools people have a hard time adopting 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.
> There is is a not invented here culture at Microsoft Redmond which includes being hostile to Microsoft Research.
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.
The problem is that people who are drawn to F# tend to be a certain kind of people: generally stronger in CS, but also more prone to think that changing programming language is a solution to a wide variety of problems.
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.
Businesses and developers generally won't switch languages. Evangelism makes people curious which means new businesses and new projects some times picks a new language.
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.
A great take on this topic is a talk by Evan Czaplicki, creator of Elm, another functional language: Let's be mainstream! [1] The conclusions are more or less the same than the article, but Evan goes into a lot more depth. And in my opinion, as a result, Elm does very good in this regard.
The problem with F# evangelism is that it doesn't exist. No one is evangelizing F#; everyone is evangelizing functional programming. F# is the Python of .NET - it's multi-paradigmatic and you can do whatever you want. Sell it to people that way, and let them get into functional concepts later, or never.
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.
Asking businesses to rewrite apps in a different language is a non-starter. It adds no value. The app is already there, written in c#. Why rewrite it in f#?
There has to be some reason to write something in F#, usually the reason is functional programming, which excels in manipulating data. However most programs rarely manipulate data. They show a UI, get some input from a user or server, save, retrieve, re-display in a different form. These kinds of programs work very well when written in imperative languages compared to functional ones.
> 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#.
Domain modelling in F# is lightyears beyond C#s. Parallel and concurrent development, not to mention actor systems (F#s built-in or Akka.net), are also leaps and bounds beyond the C# story. These are Enterprise issues. Identity management, default immutability, distate for null, and exhaustive pattern matching are all major Enteprise features that large, complex, and critical systems see measurable benefit from...
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.
I'm not a C# programmer, but it seems to me that the productivity drop argument is weird. Your engineers might be in a local maximum, and it might be worth the temporary productivity drop, if in the long run it would allow them to reach new
(and better) heights. This, of course, is an outsider's supposition, and I understand that you're just conveying their point of view.
How it is weird having a productivity drop when everything that is done graphical by tools with a simple mouse click, must be done manually in plain code without any kind of visual assistance, beyond "compile and see how it looks like"?
No, I meant that bringing this fact as an argument is weird: I'm not arguing that there's a productivity drop, I'm saying that it's only temporary.
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.
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.
reply