5 ms·
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 deve
by zubspace 9y ago
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...
- lmm 9y agoAs 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.
- brianberns 9y agoIs your programming language unreasonable? or, why predictability is important http://fsharpforfunandprofit.com/posts/is-your-language-unreasonable/ http://fsharpforfunandprofit.com/posts/is-your-language-unre...
- AnthonyLloyd 9y agoThis is it 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.
- jasim 9y agoI'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.
- nurettin 9y agoIn 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)
- skybrian 9y agoPresumably 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?
- isaacabraham 9y agoI'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.
- yawaramin 9y ago> ... more than 10 million lines of code.... https://twitter.com/kot_2010/status/898941837372686336 https://twitter.com/kot_2010/status/898941837372686336