1. 11
  1.  

  2. 33

    This post is missing actual content. After reading “I make that claim based on three assumptions:” and then the assumptions, I expected to see some attempt at justifying why F# is strong in those areas. Based on the criteria given (which I’m not sure I align with), I would say a language like Kotlin would actually be a better contender:

    • The tooling for Kotlin is pretty good. It was initially started by JetBrains (so stellar IDE) and since Google made it the preferred language for Android, the situation has only improved.
    • Kotlin has all the “functional” features that F# has (type inference, pattern matching, first class functions). They both lack type classes or higher kinded types.
    • The developer community for Kotlin is quite a bit bigger than F#. I’m wasn’t able to find a programming language index where Kotlin didn’t outperform F# (and sometimes F# wouldn’t even show up on the index at all!).
    1. 10

      Kotlin doesn’t have a classic ML-style implementation of pattern matching.

      1. 5

        This is what I was trying to say. It felt like an introduction to an article … but the body of the article is missing.

        1. 3

          Yes, I agree. Just about any language could be a part of that article. And it’s not even qualifying any of t he assumptions.

          Practically a shit post. “F# is the best!” and then goes away without explaining.

        2. 10

          I expected the article to be a persuasive writing about why I should give F# a shot, maybe with stories about how it has helped you learn and grow as a developer. Perhaps a section on how to get started. I was surprised to find the article’s second half was reasons I might not want to use it!

          I was hoping for the persuasive article because I want to be persuaded!

          1. 1

            Thanks! I’m calling that a victory. The goal here was to qualify the discussion, not persuade anybody. There might be very good reasons for you not to use it.

            YMMV, but I found a big difference between intellectually understanding functional programming and actually being able to code well using it. The only way I got to coding was through pain; coding however I could and then “cleaning up”.

            This was not at all how I learned to code in other paradigms, so it was a painful process. If your path is like mine, you need to know that.

            The intellectual part is a hoot, and we can talk Category Theory and all sorts of cool stuff for a long time using various programming languages as props. Who’s not up for another monad tutorial? (joking!). It’s the dang minute-to-minute coding that’s where the action is.

            My plan is to do a “smells of functional coding” series. The problem is that unless you do some level-setting first, talk about why you’d want to code functionally and what you expect to get out of it, you can’t be sure you’re doing a good job with examples, tips, tricks, and so forth. Frankly, I’m seeing a lot of F# code that I’m not too happy with, but I like the training capabilities so much I’m doing my best to ignore it. An essay like this gives me a good stick in the ground to point back to later. (i.e., I see that this code is doing this cool FP stuff, but is is supporting my three assumptions or just indulging in theoretical self-stimulation. Lotta that going around.)

            1. 4

              There might be very good reasons for you not to use it.

              AH, I should clarify. None of them excluded me. But then I was left with mostly nothing beyond “maybe F# is a thing to look at somehow?” and nothing to whet my appetite or get me playing with it. I’d love to see that.

              1. 1

                In my second book I talk about using Code Budgets, Incremental Strong Typing, and Code Cognitive Load to grow a small application. The app I develop keeps track of what a programming team is doing and the current status of each task. Here we start with a problem everybody understand and grows our solution into a couple of F# programs. Makes for a nice contrast with the typical CRUD-ish OO Hello World stuff. IM me here and I’ll extract the chapter and send to you if you’d like.

                If you’re interested in all of those terms and more, they’re all in the book here: https://leanpub.com/info-ops2

                Note: I do not write essays to sell books. In fact, I make efforts to prevent people who are not serious from buying my books. Life’s short, and I’d rather have ten new readers a year that I can help than a thousand that email me telling me my fonts and margins are all wrong.

                1. 9

                  But the post is called “F# Is The Best Programming Language Today”. I think the post itself should try to make an argument for why F# is the best language today!

                  1. 1

                    There are three assumptions I believe to be true for F#. I state these assumptions without defense. If people accept the criteria, then I’m really interested in conversation. If they don’t feel these are reliable criteria for a conversation on good programming environments, there’s not much else to talk about.

                    I was quite interested in the Kotlin response above. It accepted my criteria yet drew a different conclusion. I thought that prompted a good discussion.

                    If F# doesn’t meet those criteria or there others that do better, let me know! In my opinion the criteria are much more important than my conclusion that F# fits the bill best. I didn’t bother justifying F# because frankly I felt that most commenters couldn’t even agree on acceptance criteria. Not much to talk about if that’s the case.

                    There were a lot of responses here and elsewhere that sought to muddy the waters, go into language features, debate “true” FP, and so forth. That kind of thing doesn’t even seem on-topic to me. I’d much rather agree on criteria and then disagree on which languages fit the best. To me that’s the better discussion.

                    1. 7

                      But then the piece is “the criteria for the best language”, not “why F# is the best language”. You could replace F# in the title with Python, C++, Elemist, or SML and still get the same essay. You also don’t justify much why those assumptions are interesting critera— you could replace them with other criteria without changing the essay much.

                      There were a lot of responses here and elsewhere that sought to muddy the waters, go into language features, debate “true” FP, and so forth. That kind of thing doesn’t even seem on-topic to me. I’d much rather agree on criteria and then disagree on which languages fit the best. To me that’s the better discussion.

                      Well that’s the internet for ya. Though I think people would have stayed a little more on topic if you wrote more about why those are important criteria, and why F# fits the bill.

                      1. 1

                        I don’t know. I kinda doubt it, to be honest. I’ve been through a lot of these discussions, and folks want to talk about everything but decision criteria. This is because talking about decision criteria takes a lot of smoke out. Wow, now somebody is calling this thing flamebait, folks going on about language semantics. Literally my first sentence was “Programming languages are much more than simply a list of features and rules.” We confuse the nature and construction of hammers with the job of roofing houses. This particular hammer may be one of the poorest hammers ever, but I prefer to use it because I can use it with new folks to train them faster. Going into the mechanics of hammer construction has nothing to do with anything. But we love talking about it anyway.

                        I’ll grant you that I should have been more clear about the meta nature of the article. This is meant as a generic reply to 1…x arguments about why one language might be better than another. These are the things I value and F# fits that bill. Other folks are welcome to value other things.

                        Also, there’s no underlying “Functional is better” argument here. In fact, assumption 2 is simply that most coders are interested in learning to code in a more purely functional way.

                        I shouldn’t have read the comments. They’re all over the place. You can argue that this indicates a poorly-worded essay. You can also argue that people have their pet ideas that they love defending. There’s no way of writing a better essay no matter how I went at it. I think the second is true or I wouldn’t have worded the essay the way I did. I’m perfectly happy with being wrong. Works for me. I’m much more interested in the conversation. So far it looks like about 10% are on-topic. That looks better than average.

                        1. 2

                          On hammers, there’s a classic PHP rant from 2012 that explains why we should care about hammers and other small details. Replace “F♯” with “PHP” and “Microsoft and OCaml” with “HTML and Perl”, and now Eevee speaks from the past to point out that poor hammers build poor houses. Remember, to a computer, only the details matter.

                          On incommensurability, there’s a classic paper from 2012 exploring the difference between programming languages and programming systems. You are clearly talking about programming systems, but because of a paradigm shift in the 90s, programming language research is now its own field, much closer to the hard mathematical core of computer science and to linguistics. The topics you discuss, like HCI, are properties of entire programming systems taken holistically.

                          On metadiscussion and the nature of programming languages, there’s a classic article from 2013 which discusses using abstractiveness and expressiveness to compare languages. For example, informally, F♯ has been less expressive than my pet language Monte on every example I’ve seen (including yours); the equivalent Monte code would be shorter than the presented F♯ program. This is because I want to write less code overall, and so I made it a design goal for Monte to not require too much ceremony, which led to a lighter syntax. (Formally, Monte is more abstractive than F♯ due to the presence of code audition, a fancy expensive feature which I would not expect any OCaml to include, aside from perhaps MetaOCaml.)

          2. 10

            I think that this article might be flamebait. These phrases:

            • “best programming language”
            • “teaching/coding language”
            • “purely functional manner”

            These are meaningless stubs meant to provoke discussion without reaching understanding.

            Most of the time, when we rank programming languages, we talk about rules, syntax, features, and whatnot. If that’s your thing, then my first assumption does not hold true and my entire argument falls apart.

            I encourage you to kibitz on programming language communities, like #proglangdesign or #esolangs on Libera IRC, and get a grasp of what language designers actually talk about. Syntax and semantics are bread and butter. You might even hear slogans like, “languages are not fast or slow; language implementations are fast or slow,” which reflect our understanding of software engineering.

            1. 9

              Man, if you want me to consider switching from the real OCaml to an OCaml without a real module system, at least stop writing it “OCAML”. ;)

              1. 4

                Man, if you want me to consider switching from the real OCaml to an OCaml without a real module system, at least stop writing it “OCAML”. ;)

                You mean the real OCaml with the global interpreter lock :)

                1. 2

                  Not if you install the multicore branch (recently rebased to the latest 4.12).

                  1. 2

                    Not an interpreter, if you want a bit of performance :p. And it’s being worked on!

                2. 5

                  What does “teaching/coding language” mean? What are you even trying to teach? I think the subtext is that all the things the IDE make convenient, things that you would need to learn if you were using Vim, they are not coding? Am I right?

                  Why not just write that then? I am not saying IDE’s are bad, but now that we are implicitly redefining words, why don’t you just let me redefine coding as “non-HTML based cross-platform GUI coding”? Now F# suddenly doesn’t seem like a good “coding language” ;)

                  Here comes my typical Haskeller retort: because F# doesn’t have a sufficiently strong type system (effectful code based on higher-ranked polymorphism is not popular in F#), you are not really doing FP. Without higher-rank polymorphism, you’re stuck in the pure part of FP, which is the easy one. F# jumps the shark on the hard parts, and it isn’t convenient to write modularized effectful code.

                  So is the implication here that “teaching language” means you don’t need to worry about effects? I think teaching should be about doing it right. If F# guides you down a wrong path, is it really a good teaching language?

                  I’d agree more to something like “beginner FP language”.

                  1. 5

                    because F# doesn’t have a sufficiently strong type system (effectful code based on higher-ranked polymorphism is not popular in F#), you are not really doing FP. Without higher-rank polymorphism, you’re stuck in the pure part of FP, which is the easy one. F# jumps the shark on the hard parts, and it isn’t convenient to write modularized effectful code.

                    For anyone unfamiliar with these terms: functional programming and higher-rank polymorphism are completely unrelated concepts.

                    Functional programming is about avoiding mutation and side effects, and higher-rank polymorphism is a type-checking feature. Functional code doesn’t stop being functional if you remove the type-checking step from your build, so it’s not possible for higher-rank polymorphism to be a requirement for doing FP.

                    1. 6

                      I don’t understand this FP gatekeeping.

                      Functional programming is about avoiding mutation and side effects, and higher-rank polymorphism is a type-checking feature. Functional code doesn’t stop being functional if you remove the type-checking step from your build

                      A couple years back, there were lots of posts about JavaScript being functional. At that time, I remember hearing arguments that closures and functions were what functional programming is really about.

                      Personally, I’m inclined to slightly agree with the grandparent comments: without higher ranked types it is tricky to represent IO in an immutable fashion. Since the alternative is to bail out and just use mutation (as F# does) then it isn’t as functional as it could be (by your own definition of “avoiding mutation and side effects”).

                      1. 1

                        without higher ranked types it is tricky to represent IO in an immutable fashion. Since the alternative is to bail out and just use mutation (as F# does) then it isn’t as functional as it could be (by your own definition of “avoiding mutation and side effects”).

                        Elm is a typed pure functional language without higher-ranked types, and Elm is even stricter about purity of effects than Haskell, which has unsafePerformIO. (Elm has no equivalent of that.)

                        I don’t think there’s anything particularly tricky about it!

                        1. 5

                          Elm chooses to limit pretty strongly what sort of IO the user does to the point that I’m not sure it counts as a general purpose programming language. For example: what is the type of a function that reads a file? How do you capture those effects? You can definitely avoid using monads for IO, but IMO it leads to less powerful or less usable systems. For example: Haskell initially modeled IO using infinite lists (and it wasn’t very usable).

                          unsafePerformIO is a compiler hack. It isn’t part of the Haskell specification, it is just used for FFI interop with other languages. EDIT: the legitimate use cases for unsafePerformIO are extremely rare and are (to my knowledge) always around some performance optimization that is made outside of Haskell 98.

                          1. 1

                            You can definitely avoid using monads for IO, but IMO it leads to less powerful or less usable systems.

                            Elm does use monads for I/O, it just doesn’t call them that.

                            Here is Elm’s equivalent of Haskell’s IO (except that it includes error handling, which Haskell’s IO doesn’t):

                            https://package.elm-lang.org/packages/elm/core/latest/Task#Task

                            Nothing tricky about it imo.

                            1. 2

                              Can I define my own Task to call a C function using FFI? Can’t find it.

                              1. 3

                                Only if your name starts with Evan. ;-)

                                1. 1

                                  Elm compiles to JavaScript, so no.

                                  Given that the thread was about whether functional programming has anything to do with higher-ranked types, I’ll assume this change of topic means that discussion is over.

                                2. 2

                                  Oh man, that link reminds me of Elm’s time handling approach that made me want to take Evan’s hand and tell him “Kid, it’s not as easy as you think it is. You think you had a really smart idea there, but really, you didn’t.”.

                                  1. 1

                                    Interesting - I had the opposite reaction. I’ve never used a time API I liked as much as Elm’s. What didn’t you like about it?

                                    1. 1

                                      From an ergonomic perspective, an example problem (of many!) is the choice to make time zones explicit. I suppose that this comes from the SPA tradition, where ad-hoc user input (“Choose your language”) or Web browser APIs are used to establish a preferred presentation language instead of content negotation. However, just like with the Unicode sandwich technique, we can have a UTC Sandwich design where times on Earth are handled uniformly, and only the outermost presentation layers need to worry about timezones.

                                      Worse, in my opinion, is the decision to make access to timers unprivileged. This invites timing side-channels. I know of no solutions besides making them explicit parameters instead of allowing them to be ambiently imported.

                                      In general, Elm’s libraries and designs are oriented towards SPAs but not towards backend services, ruining the hope of so-called “isomorphic” deployments where identical code runs in the Web browser and the backend.

                                      1. 1

                                        From an ergonomic perspective, an example problem (of many!) is the choice to make time zones explicit. […] However, just like with the Unicode sandwich technique, we can have a UTC Sandwich design where times on Earth are handled uniformly, and only the outermost presentation layers need to worry about timezones.

                                        I personally like this technique - explicit time zones are one of my favorite parts about the design of the elm/time package - but fair enough if your preferences differ from mine!

                                        Worse, in my opinion, is the decision to make access to timers unprivileged. This invites timing side-channels. I know of no solutions besides making them explicit parameters instead of allowing them to be ambiently imported.

                                        Hmm, how would a language (any language!) support even basic current-time use cases (like obtaining a current timestamp) without making timing attacks possible?

                                        1. 1

                                          Time-zone handling isn’t just a preference. We’ll necessarily incur an extra table lookup if we want to decode a historical timestamp which is relative to some time zone, so it’s slower. That extra table has to be from an old time-zone map, so we must store an ever-growing number of old time-zone maps, so it’s bigger. And it is another thing that programmers might get incorrect, so it’s got more potential for bugs.

                                          By “explicit parameters” for timers, I mean that the application’s entry points would accept timer values as parameters, and invoking those timer values would produce timestamps. Timing attacks are still possible, but they no longer have the potential to run rampant through a codebase. For a practical example, this Monte module implementing Python’s timeit algorithm is parameterized upon some timer object, but doesn’t have automatic access to any privileged clocks or timers. This module drawing some console graphics explicitly requests the system clock Timer and syntactically reveals where it is used.

                                          1. 1

                                            We’ll necessarily incur an extra table lookup if we want to decode a historical timestamp which is relative to some time zone, so it’s slower.

                                            I see - so, to check my understanding: for reasons beyond your control, it’s stored in the database (or you get it from an external data source) relative to a particular time zone, and you want to decode it (and then work with it?) while staying relative to that time zone?

                          2. 2

                            functional code doesn’t stop being functional if you remove the type-checking

                            Like the “Rust is NP-hard” post shows, the type system is also used for code generation. So I can’t even run my code after removing the type checker.

                            We don’t need to mince words about what FP is, because this is a discussion about what F# is missing. I should have said “pure typed FP”. Would you agree with me then?

                            1. 3

                              I should have said “pure typed FP”. Would you agree with me then?

                              Also no; a counterexample would be Elm, which is a typed pure functional language without higher-rank polymorphism!

                              1. 6

                                You can also have an effect system (like Koka) and still not have higher-kinded types. These are only necessary if you want to abstract over monads.

                        2. 3

                          I like F# a lot. It’s by far the most pragmatic ML-descendent to use. .NET Core became just regular .NET in .NET 5 (I think) so I’ve been using it solely in Linux projects like dbcore.org. The wealth of .NET libraries is fantastic.

                          1. 3

                            F# seems really cool and the .Net interop sounds like it would make a pragmatic language for production use… but one thing that bugs me is there seems to be no equivalent of type classes or protocols. I understand polymorphism can be achieved in a roundabout way (eg as explained here: https://withouttheloop.com/articles/2014-10-21-fsharp-adhoc-polymorphism/ ) but it doesn’t seem as natural as Haskell type classes, for example.

                            Is this a problem in practice? I’d love to hear a take on this from a F# programmer

                            1. 1

                              The first thing I garnered from this article is that F# doesn’t have string interpolation.

                              1. 4

                                That would be a weird claim, you can literally just do $“Hello {name}”. Is there something else called string interpolation?

                                1. 1

                                  The examples given do not use string interpolation, hence my takeaway.

                                  1. 3

                                    It looks like they didnt have it until last year:

                                    https://github.com/dotnet/fsharp/pull/8907

                              2. 1

                                I like the exploration of the assumptions, but that seems a long way away from “F# is the best…”

                                For the sake of discussion, let’s just grant the whole “functional is better” bias underlying them. Looking 1-by-1:

                                1. Tooling: Doesn’t F# limit you to visual studio on windows and the associated build tools? Unless “writing software for Windows” is the main objective, I find working in Visual Studio to be miserable. And “writing software for Windows” is something I do at the end after all the growth and learning and concept proving that might lead me to try a new language or new set of libraries. That’s the kind of job listing-style qualifier that OP said would make assumption #2 not apply… to then require it for assumption #1 puts the argument in a rough place.

                                2. Accommodating a wide range of styles is an interesting point, but it does hamper learning in some ways too. If it weren’t for the damned borrow checker in Rust making some bad habits unworkable, I don’t think the improvement occurs. Over-accommodation can lead you to stall out; how does F# prevent that?

                                3. I’d need some persuasion to see why F# isn’t dragging up the rear behind Kotlin, Rust and Clojure here, and the article offers none.

                                Maybe it’s just incomplete and needs a follow-up, as others have suggested.

                                1. 4

                                  Doesn’t F# limit you to visual studio on windows and the associated build tools?

                                  No it doesn’t.

                                  .NET Core runs well on other platforms these days. And the open-source F# tooling has very good support for Visual Studio Code, but there is a LSP server so other editors (vim, emacs, …) should be supported as well (I haven’t tried that recently though).

                                  1. 1

                                    Yep. The only lack of support I’ve bumped into is that dotnet doesn’t have an implementation for ppc64le yet. issue #13277

                                    1. 1

                                      Nice. I didn’t know the dev tooling had made it off windows yet. (I was aware it could be built and run, in some measure, but my impression from the last time I messed with it for C# was that most of the stuff people used in the software they wrote for distribution was still very much tied to windows.)