5 ms·
We are currently rewriting Buckaroo in F# (https://github.com/LoopPerfect/buckaroo/tree/buckaroo-redux https://github.com/LoopPerfect/buckaroo/tree/buckaroo-red
by adgasf 8y ago
We are currently rewriting Buckaroo in F# (https://github.com/LoopPerfect/buckaroo/tree/buckaroo-redux https://github.com/LoopPerfect/buckaroo/tree/buckaroo-redux ); previous versions were in modern Java. The productivity gains are tremendous... FParsec, async, asyncSeq, records, match expressions are all a huge leap over what most OOP languages offer. Deployments are also much easier thanks to Core RT.
F# feels like a secret weapon.
- dmead 8y agoI want to ask an annoying question. Why F# and not haskell?
- adgasf 8y agoThe main reason was that team had more experience with F#. I think it is also more approachable than Haskell for OOP devs (C# to F# comparisons are quite common on the internet). I'm sure Haskell would also have been an excellent choice though :)
- BeetleB 8y agoNot OP, but some reasons usually thrown around: F# has the power of .NET and its libraries behind it. Haskell's insistence on purity and laziness does increase the time it takes to design things well. F# makes it easier to violate purity in controlled ways, and is not lazy be default. People who've used both say it's a lot easier to get lost in abstractions with Haskell, and there doesn't appear to be a clear "stopping" point. A common statement by people who move from Haskell to OCaml/F# is "I spent N years on Haskell but didn't produce much. In 6 months of (OCaml|F#) I've produced more than in those N years with Haskell." The bottom line: The .NET libraries and slight relaxing of purity/laziness allows for more productivity. (I'm not an expert on either - just repeating what you normally find in the threads).
- nikofeyn 8y agof# is a multi-paradigm language. it allows you choose what paradigm fits the problem best and allows you to easily mix paradigms, and it does so in a clean way. haskell does not have this flexibility. this is f#'s power.
- Kip9000 8y agoC++ package manager written in F#. This should be the tagline :P
- adgasf 8y agoHaha indeed! My position is this: different programming languages have different strengths and weaknesses. C++ is crazy fast, but it lacks many high-level features (most notably do-notation). Efficiency and performance are its niche. Conversely, F# (or any ML) is very good at expressing complex control flow, but performance is a secondary concern. Package management is an IO-bound SAT problem, so we are using the best tools for the job.
- ernst_klim 8y agoML could be as fast as C++ and even have manual memory management. Take a look at mlton [1] and manual memory management [2]. Most ml compilers just value compile time more. [1] http://mlton.org/Performance http://mlton.org/Performance [2] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.75.9105&rep=rep1&type=pdf http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.75....
- e12e 8y agoMaybe the world needs a language that is like clasp is to lisp and c++, but for c++ and ml? Curious about your deployment happiness - are you bundling the runtime with your program? Last I checked, such a bundle was large (compared to a static c++ binary), and didn't have a nice single/few file(s) "container"?
- adgasf 8y agoPureScript has a C++ back-end, which is an interesting prospect: https://github.com/andyarvanitis/purescript-native https://github.com/andyarvanitis/purescript-native Our development process is very pleasant. We use VS Code, .NET Core and Ionide as an IDE. I find an IDE essential for languages with global type-inference. For bundling, we use Core RT to create a self-contained application. This is then bundled using Warp (https://github.com/dgiagio/warp https://github.com/dgiagio/warp) to generate a single binary. The script is here https://github.com/LoopPerfect/buckaroo/blob/866ae97ffc82ab8908fbb6a43a208cc0cd131cd7/warp-bundle-linux.sh https://github.com/LoopPerfect/buckaroo/blob/866ae97ffc82ab8... The final binary size is quite reasonable: - Linux 66.1 mb - macOS 63.9 mb - Windows 35.4 mb I haven't investigated why Windows is so much smaller!
- lmm 8y agoWhy not Scala? That's a very similar language to F#, and JVM interop would let you do a gradual migration.
- adgasf 8y agoWe wanted to make significant changes, so a gradual migration would not have been possible regardless of language. Regarding Scala vs F#, I do not know enough Scala to comment. Perhaps someone else can weigh in?
- bunderbunder 8y agoI've spent a fair bit of time with both. Personally, I don't find the two to be all that comparable -- they're fairly distant cousins. F# tends to stick much more closely to its ML roots, and fairly scrupulously retains a more ML flavor except when necessary to achieve good interop with the .NET ecosystem. Scala, on the other hand feels less to me like a dialect of ML and more like an ML-flavored object-oriented language, because it mixes in OOP-style features much more freely. Its algebraic data types, for example, are explicitly implemented as class clusters. A lot of people like this, others see it as an anti-feature. Where you fall probably depends on how you feel about the relative merits of OOP and FP. It's also, for better or worse, less concerned with maintaining good interop with the rest of the ecosystem. Consuming F# modules from C# isn't always pretty, but it's always possible. There will be C#-friendly wrappers, but creating them is a matter of aesthetics rather than necessity. Consuming Scala modules from Java, though, frequently requires creating wrapper libraries. On the upside, this does mean that Scala gets to have things like typeclasses, which the F# team has kept out of the language due to concern about how they would impact interoperability with the rest of .NET. Last but not least: F# has type providers. Scala has implicits. F# has quotations. Scala has more stuff that's also called implicits.
- saberience 8y agoThe scala toolset is incredibly crap though. Ask the engineers on my team about the huge pains of transitioning to using IntelliJ from Visual Studio, or the joy of trying to make SBT do what we want it to do, then trying to use Maven instead. Any new engineer we get started with Scala has to spend about a week making their local setup work. It's a total pain in the arse.
- Verdex 8y agoI think the reason why is two fold. 1) The type system is focused on mathematical types as opposed to compiler tags. Mathematical types are about ensuring that a function will not fail. Compiler tags are about ensuring that you generate the correct asm op codes. Mathematical type systems allow you to have an easier time knowing the code works. 2) Algebraic data types provide both an AND component and an OR component. That is, you can do type X = A of int * int * int; AND you can do type X = A of int | B of char;. C# style languages provide you with the AND component (class A { public int B => ...; public int C => ...; }). However, they don't provide the OR component. You can create it by constructing weird type hierarchies, but this isn't idiomatic AND it also isn't closed. With an algebraic data type you can know that all of the cases are handled in a match. This isn't something you can ensure in C# (at least not naturally or easily). Ultimately, I don't think functional programming provides anything that has to be better than object oriented programming. It's just that there are two facilities that tend to go with functional programming that allows a better way to model the problems that we encounter.
- willtim 8y agoMissing from your list is arguably the most important difference: programming with mathematical (immutable) values. This allows one to build very high-level abstractions and easily reason about correctness. OOP languages simply were not designed for this style of programming. There are also many other significant differences. I estimate I am about ten times more productive in Haskell than Java.
- Verdex 8y agoI don't think mutability is problematic. The problem with mutability is that you can create hidden communication channels between different sections of your code and it can be very difficult to determine what is actually going on without carefully analyzing the entire code base at compile time and at run time for every possible input. So if you only have mutability inside of a function and it cannot escape, then you have no problems. Or if you have a way to prevent it from crossing module boundaries. Rust for example, gives you some pretty good tools for controlling mutability and for tracking it. I think this is actually superior to a fully immutable system as with full immutability you end up with other weird problems (like needing monads and monad transformers to do things that would otherwise be simple).