The combined power of F# and C#
steven-giesel.com
steven-giesel.com
Writing tests is much easier and requires much less code, and bugs are much easier to track down (since you have less surface area of "what might have modified this object I now hold?").
Since a functional approach also goes well with single-responsibility-principle and composability, you can end up with multiple times more actual functions. They're all (ideally) very conceptually simple, but the larger number of more specialized functions means you have to work a lot harder on choosing meaningful names. And to write meaningful names of specific functions, you can end up with some quite long names. This is actually quite ok as it makes things much more readable, but some people have some strong negative reactions to seeing 4-6 word function names. But at least in this case (Java being the most famous counter scenario) each function probably has more real value as it's not just OO layer artefacts.
You can find this pattern in a lot of places if you are a little bit flexible with your definition of "functional".
For example, why would we not consider some combination like SQL and PHP to be hinting at this exact same kind of archetype? You've got a crusty outer shell of yucky hackarounds (PHP) that talks to this (ideally) well-normalized & clean data store (SQL). Assuming you "stuff as much as possible" into core part of the solution, you could swap the outer shell for anything you desire without much headache.
With actual SQL, most people don’t get beyond JOIN, which they mainly need in order to deserialize nested or pointer structures from normal form. I learned aggregation, grouping, and windowing working with Spark and Hive on warehouse replicas of my DBs/topics to troubleshoot and analyze my stuff. Never used one in an actual request handler.
From the outside it's stateful, because well you are interacting with a DB.
From within the PHP codebase, it's imperative and requires discipline and extended knowledge of any API you call to understand whether something mutates.
The only time you can view it as actually stateless and somewhat functional is when you test or debug an integration from idempotent requests to responses without interleaving mutations.
This holds for a Haskell webapp too.
For example, I find in TypeScript, most libraries and the language itself are not written with immutability in mind so there's constant gotchas. So while I strongly prefer immutable data structures, going against the grain to use immutability everywhere usually isn't worth the frustration. That for me doesn't mean immutability is a bad idea though, it's more that the libraries and languages support is lacking.
That way you can use pure functions on the inside. Pure functions don't allow for side effects.
This is why there's now more focus on algebraic effects as an alternative for keeping a functional core, imperative shell. Monads/transformers are just an ugly solution that even hardcore functional programmers don't want to use.
For a functional shell, your program's entry point would be composed of pure functions describing effectful actions to take. The necessary computations get bubbled up to the entry point during evaluation, at which point the runtime / compiled program executes them.
(At least, this is the abstracted perspective you should view the purely functional source code from. The final program itself in practice is, of course, effectful throughout).
Fundamentally the user isn't functional - if you ask them what they want to do they'll say different things at different times. You can have a 100% purely functional programming language that works like a calculator (with no memory) - the user can put in expressions and the language will evaluate them - but generally users want their language to be able to do things that are non-idempotent and non-time-symmetric and so on. You can, and should, push that part to the edge, but it needs to be there somewhere.
2) Not all frontends have functional APIs, you may be stuck writing a lot of FFI glue code.
3) Front end code often gets pushed to devs with different experience and skill sets, in my experience they’re probably less likely to have functional or hard CS backgrounds.
4) Harder recruiting if all the devs need to be proficient with FP.
I personally don’t find any of these reasons compelling enough to negate all of the benefits of FP.
Answers saying FP isn’t suited for IO or for interaction with users are rhetorically interesting but contradict my experience.
I wouldn't be qualified to argue the merits of FP on frontend, but on the backend it really seems to have value.
In .net, most UI stuff is easy on C# and semi-painfull on F# because of limited tooling and examples. I don’t see any theoretical reason why a usable functional language could not be used everywhere. The ecosystem maturity and tooling are the limiting factors.
This is most clearly seen with conversation functions between types defined in different libraries. A collection class might have a method like "from_Array".
I recently wondered why Casey Muratori (RAD Game tools, Handmade Hero etc.) seems to write C++ in a very C like fashion plus a few select features.
Here's a discussion: https://hero.handmade.network/forums/code-discussion/t/453-w...
One of the takeaways is that he prefers the simplicity of C but uses operator overloading for similar reasons as you describe. One example includes addition of vectors, which is certainly a thing you do often in game and graphics related code.
Eg: https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQAUEEsC2UECeAwgP...
But in any case I really love this addition to the language but the inability to have multi-line or block expression arms is a constant annoyance for me.
You can even combine these with the new one line record syntax to create a poor man’s discriminated union.
The workaround for that is the same as the workaround for the really lame one line limitation: you need to call a (preferably (static) local) function in the handler portion and then return something like `true` assigned to a discard. Hacks all around!
Eg
_ = foo switch a when … => CaseA(foo), _ => CaseB(foo);
With CaseA and CaseB returning bool in order to call a function depending on the value of foo rather than assign a value.
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...
Probably one of my favorite recent-ish additions to the language.
I think they would add it by C# 14 or 15.
Anyone that cares so deeply about them can do the work on a F# assembly.
C# has done great in the industry for the past 23 years without them.
Languages are not used in isolation, great IDE experience, and having mature libraries for every use I can think of, is more valuable than grammar and semantics.
Also a reason why I would rather do FP in C++23 than Haskell, even with all the warts and paper cuts it entails, ecosystem.
Additionally, they add friction to a development stack, now everyone needs to be confortable with two language stacks, and most of the time it isn't really worth it.
The hope and the expectation is that the CLR will gain support for first-class representations of F# concepts to allow for greater interop with C# scenarios, but the CLR's development has always been tied to C#, with other CLR languages like VB.NET and C++/CLI only exerting minor influence on the CLR's design with most of their language-specific idiosyncrasies being handled by library-code and compile-time tricks instead (e.g. VB.NET's "On Error Resume Next" statement is implemented by having the compiler wrap each individual statement in a try/catch instead of having the CLR specifically support it (though in this specific case that's probably a good idea as OnErrorResumeNext is a horrible idea I'm sure we all agree).
Which is kind of ironic given how they usually leave C++/CLI out of the picture, including the cross platform story.
[1] https://devblogs.microsoft.com/dotnet/new-csharp-12-preview-... [2] https://devblogs.microsoft.com/dotnet/new-csharp-12-preview-...
I can manage without DU, have used plenty of languages without them since Caml Light.
I believe I am overlooking something (probably obvious even to me) since i know:
https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...
is perhaps a misuse of the term in this context.
(I'm guessing you mean more like in Rust, but am not sure.)
(There are multiple proposals tracking the idea. This seems the most comprehensive and "central": https://github.com/dotnet/csharplang/issues/7016)
https://github.com/linkdotnet/Blog.Discussions/discussions/7...
Is is kind of a pain particularly when working across Typescript projects. OneOf is cool, but it DOES NOT work well with null and thus optional parameters.
For now, you can get a reasonable DU via an [external library](https://github.com/mcintyre321/OneOf).
[Nick Chapsas Video on Usage](https://www.youtube.com/watch?v=7z-xjijYfcI).
F# / C# interop ... cool thing, just to clarify.
let a: int = 1 // Assignment
a = 3 // Testing equality, will give you an error
a <- 4 // Assignment, valid
There's an article here with more things:https://alexyakunin.medium.com/crying-out-loud-what-i-like-d...
I want to like F# since I love C#, but I just want something closer to Scala, but for .Net
But I agree with some ad hoc words criticism
shadowing would be a second 'let a = 2'
That confused me, as I assumed that code there is compiling. But the only way for it compile is shadowing, so yeah, I kind of assumed that to shadow is to use this syntax. I'm not working with F# for some time, so should've checked before posting.
Recently I did some F# conversion to Python, and the Python code really did seem ugly and ungainly in comparison.
a <- 4 // Assignment, valid
This is a compiler error because you haven't made your variable mutable.Also, I actually like that initial assignment and equality testing both use =. I think of `let a = 1` to be less like assignment and more like `Assume that a = 1 is true`, so using the same operator (e.g. `a = 3`) makes sense for comparisons.
And `<-` as a separate operator is good because mutability should be exceptional for many (most?) codebases.
Assume that a = 1 is true
I just don't read code as I would a mathematical proof. I think in terms of what memory locations are equal to what in the stack, or the heap, and what is the lifecycle of that data in that memory address.When I read "int a = 1" in C#, I implicitly translate that to "take a 4 byte piece of memory on the stack, and set it equal to 1". I don't think in the abstract sense of a formula.
When I see a class like:
class Foo {
int x = 2;
string xyz = "Hello";
}
var foo = new Foo();
I read this as "allocate a chunk of memory big enough in the heap to insert a 4 byte integer and an 8 byte pointer. Set that 8 byte pointer equal to a static chunk of memory where the "Hello" string is pooled.I think that's overspecifying a bit. It could be kept in a register rather than the stack. And due to to the Single Static Assignment transformation that modern optimising compilers do, variables don't correspond exsctly to registers anymore; each time you modify the variable, it becomew a new variable, and then the compiler removes or changes extraneous modifications and dead code. It only keeps track of the values that move through the code. You could really only count on variables corresponding exactly to stack space registers before the SSA form existed.
IF you have grown up doing objects, or C#, and thinking with variables. Then 'a=1' means a memory for variable a has a 1, and you should be able to change that. But it is really a like a function where the function returns a 1.
'let a = 1' is not assigning the value 1 to variable a.
'a' is a function that returns a 1.
I think this is biggest reason why people trying to learn functional programming in languages that don't enforce immutability, have a harder time than with languages that do enforce it.
Like moving to another country, and the people around you purposely don't speak English so you have to learn the language. If the did speak English to help you, then you wouldn't learn the language.
Enforcing immutability is like this.
I can imagine that trying to translate C# to F# would be horrible. That is going against the grain.
I'd encourage you to have a look at the SAFE stack it's really nice to use. https://safe-stack.github.io/
That said I don't think there are any features that I'm missing out on being at .Net 6.
I think there is a limited number of maintainers of the SAFE template so they might not be able to keep on the bleeding edge. But it's not that hard to update the components to the latest versions. As can be seen on this waiting pull request https://github.com/SAFE-Stack/SAFE-template/pull/564
I should also mention that when I've hit issues with packages not working on the latest .Net version I've asked in the F# slack and twice had the maintainers fix things within a day so it would run at the latest version.
I'm still curious about F#. It has a lot of neat looking features (units of measure, computation expressions, active patterns) and has some of the easiest to read code I've seen. I just don't know if my experience was representative or if it was abnormal. :/
https://zaid-ajaj.github.io/the-elmish-book/#/
https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUME...
https://bulma.io/documentation/
https://medium.com/@zaid.naom/introducing-fable-remoting-aut...
I've actually gone through most of the Dojo tonight except for the reset button.
In any case. I get it. If you have done ASP.NET, and C#, then suddenly this F# way of building web pages by programming through combining functions, it is hard to get over the hump. Like brain has to re-change how to think through the whole flow. Really, F# on the web is like ELM.
I like https://websharper.com/
but other use this https://suave.io/
Giraffe is nice because it is itself built "just" as ASP.NET Core Middleware so it plays a bit more nicely than Suave with a mixed stack of C#-defined Middleware.
It's more likely you accidentally fall back into just translating C# patterns to non-idiomatic F# with Giraffe, but it's also nicer when in that case of needing to live in both worlds and use a mixture of libraries built for C# ASP.NET projects.
Mutable variables are better explained as slots or cells, I think. Both OCaml and Rust have a concept of ref cells, for example (and they entirely replace mutable local variables in OCaml).
1: https://en.m.wikipedia.org/wiki/Variable_(mathematics) 2: https://en.m.wikipedia.org/wiki/Variable_(computer_science)
That's fine, although I'd say that FP is probably just not for you. It's very much a style of programming that lends itself more towards "programming is akin to proofs" than "programming is about manipulating things on a von neumann architecture". Neither is an incorrect view of the world, but they do represent different ways of reasoning about things and it's better to use a language more suited towards one way of thinking.
I think immutability is more about making it easier to reason about the code than mathematical proofs specifically.
That's just gatekeeping.
That's what I have been working toward with my language-ext library [1]. Including a ZIO like effects system [2], Haskell-like Pipes [3], Clojure-like concurrency primitives [4], the fastest immutable data-structures in .NET [5], and lots of other common FP bits.
Obviously more support for expression based programming would be welcome (and higher kinds), but you can do a lot with LINQ and a good integrated library surface.
[1] https://github.com/louthy/language-ext
[2] https://louthy.github.io/language-ext/LanguageExt.Core/Effec...
[3] https://louthy.github.io/language-ext/LanguageExt.Core/Effec...
[4] https://louthy.github.io/language-ext/LanguageExt.Core/Concu...
[5] https://louthy.github.io/language-ext/LanguageExt.Core/Immut...
"This is a C# doc-gen tool. It was primarily built to support my Language-Ext project. Which is non-idiomatic in its approach. I couldn't find documentation generators that did it justice. I also really liked the Hackage documentation style from Haskell, so have taken a styling approach from there (even if it looks a little dated now, it was always the documentation I felt most comfortable reading)."
language-ext isn't idiomatic C# and so idiomatic C# doc-gens have quite a bad time with this library. One of the major benefits over other doc-gens is it grabs the README.md from each folder and prepends it to the relevant section - allowing me to write some contextual documentation outside of the auto-generated API documentation. This makes it a much more approachable reference.
It also supports markdown in the XML documentation, avoiding a lot of the need for lots of ugly XML special characters [in code] and extending the layout options compared to the common C# doc-gens.
* Linearly ordered files. Several times in F# I have tracked down an issue simply by bisecting the codebase, which is impossible in C# because there's no meaningful way to halve the code. I think I've only once ever had a problem with the linear ordering that wasn't solved simply by reordering some files.
* Explicit type conversions. I've only ever found this awkward when interfacing with C# code that is designed around C#'s extreme laxity. Explicit is better than implicit!
* "No struct tuples" is false - that's what the `struct` keyword is for. (It may have been true when the article was written.)
* Dot notation for indexing is now no longer necessary.
It's certainly true that OO idioms are often clunky and feel strangely like they were constructed by Frankenstein, but then I almost never find myself trying to use them anyway. You don't notice oddities in features which you never think of using! Similarly, the number of times I use `<-` is so low that I don't think of it as being incongruously odd - why shouldn't there be a baroque syntax to indicate the place in your code that's likely to have a 50% higher chance of bugs?
Quite probably. The underlying CLR ValueTuple type is a relatively recent .NET addition (and partly only exists because C# asked for it). (It was added in Fx 4.7 / Core 1.0, whereas the reference type Tuple was added way back in Fx 4.0.)
Alternatively you can take the more functional approach and rewrite your function logic to use `if x then (value) else` throughout instead of `return (value)` or `yield (value)`. Early return isn't really a thing in f# but if-else is.
[0] https://fsharpforfunandprofit.com/posts/computation-expressi...
What about it is lacking?
I'm asking because at one time I was weighing moving to Scala, but the F# algebraic types and compiler seemed more complete.
Is it syntax? that does take some getting used to.
let a: int = 1
This isn't assignment; it's a binding that permanently associates the identifier `a` with the value `1`.> That’s it for now. Hopefully, some of these issues will be addressed in future versions of F#, though as I said, most of this is simply annoying / inconsistent. It shouldn’t stop anyone from using this awesome language
and as other have pointed out, some of this is complaining about features or not working with the language. File order matters should probably exist in more languages (the amount of debugging/code reading time it saves is insane) and complaining about explicit typing because it's slowing down your contest code is just eye rollingly silly to me.
The only problem with it is that you can't write things like:
let x^2 + y^2: float = 1
In early versions of F# you had to stick `#light` at the top of a file to use lightweight syntax, which is now the default. The verbose syntax, which is closer to ML was originally the default.
> 1. You have to manually order your files in F# projects in order they must be processed by the compiler.
This is one of my favourite features of F# because it forces you to design your code in a way that minimizes mutually recursive types, since all mutually recursive types must live in the same code file. You end up with cleaner code which is easier to test, maintain and reason about.
> 2. No break and continue for loops. 3. No do-while loops.
I've never seen as a problem because I rarely write loops. It's so rare that I use them I end up having to double-check the syntax. I always reach to .map, .fold, .filter, etc first, and I can't remember a time I couldn't express what I wanted using these.
> 6. Awful type constructor syntax
The primary constructor syntax makes sure all other constructors must call it. Prevents you from not instantiating a class incorrectly and is a better fit for immutable-first types. Perhaps could have some improvements but the common case becomes more terse to write.
> 7a. a) array type can be declared as “int[]” or “int array”; same for list and seq; moreover, it’s the same for any other generic type with one type argument.
Comes from ML, where multi-parameter generics are also written `(x * y) type`. I think it was a good idea to make F# closer to .NET and use `Type<X, Y>`, but they should have done the same for single-parameter generics too to make it consistent. I never use `X array` syntax and prefer `array<x>`, though I would prefer if they also made these case consistent and I could write `Array<x>`, but Array is a module. Not sure how to fix this.
> 8. “fun x y z -> …” syntax for lambda expressions
Again from ML. I imagine there are syntactic ambiguities if you drop the `fun`, as `->` is also used in pattern matching and other places.
> 9. a) <expression>[<expression>] = <indexingExpression> ... Please, please implement this.
I dislike the idea of spaces being significant, and there are already gotchas in F# where this is the case. `x().y` and `x ().y` are not always the same thing.
> 12. “<-” for assignment. Another personal thing, maybe, but “:=” seems easier to type.
`:=` was used for ref cell assignment, but is now deprecated.
Long ago I integrated IronJS (written in F#) into a project written in C#. IronJS at the time was a very primitive JS compiler though it was a real compiler using the runtime machinery to emit Expression objects that were JIT'd into native code.
I was very excited about switching to V8 for the vastly improved performance. To my shock and horror once I got it working the performance was worse. It turned out most scripts called our API a lot (the equivalent of the DOM in a browser). Bridging between CLR types and V8 (C++) types completely erased V8's superior performance. No amount of lazy bridging and zero-copy abstractions were enough and I eventually abandoned that branch.
It was a humbling experience that I learned a great deal from.
Btw, work of Miguel de Icaza via the Xamarin acquisition.
Fortunately, almost anything you can do in C# can be done better in F# anyway, so there's really very little need to use C# at all once you take the plunge into functional programming.
In regards to the C# vs F# issues. Have you tried using WPF or any desktop GUI frameworks from F#? If so how was the experience?
It's been a few years, but yes, I've implemented WPF apps in F# and found it quite doable. FsXaml was very helpful: https://fsprojects.github.io/FsXaml/
I'm quite interested in making a GUI from a functional language. This could be a good way to kick the tires.
I’d love if someone could point the way around (because until today, I’ve to combine both languages for desktop appt)
1. Your top-level project should be a csproj where all your "web" stuff is. Controller routes, DTOs, startup code, etc... This project is responsible for converting data into nice objects that are passed to the F# code.
2. A mid-tier project written in F#, an fsproj. This is where you can be nice, pure, functional, live out your best life pretending C# doesn't exist. However you will eventually run into issues interfacing with external libraries, EF Core, lack of standard library support for whatever you're doing. That's ok! The final layer will solve it.
3. A foundational project written in C#. This project's job is to hide the ugly and make your F# project into the functional programming nirvana you know it can be. Wrap those dependencies into pure functions, write your entity framework junk, use all the latest C# language and runtime features and expose them to your F# project.
So something like:
C# -> F# -> C#
Ultimately, I decided that C# really is good enough and I didn't see enough benefits to continue using F#.
I’ve heard this story repeated so many times over the years. The responsiveness of the C# language team in particular (as opposed to the runtime or ASP teams, etc) is remarkable and the thoughtful inclusion of functional-inspired features over the years has really dramatically improved QOL for C# devs. (With the exception of some annoying carve outs like going too far with trying to please JS/node refugees with not only introducing support for top-level statements but also pushing it as the default in the various templates, the botched delivery of global includes, and other such changes —- but to be fair, many of these were implemented correctly and without any complaints on my end in the language itself but my quibbles are more with the delivery, integration, and polish in the IDE and downstream by teams like the ASP.NET Core one.)
After a long pause during the denouement days of the .NET Framework, the C# of today would be unrecognizable to C# devs of yesteryear, and the changes keep coming.
(All this is from the POV of a developer that’s been using C# since 2002; back when J# was a thing and I was slowly adapting to writing code in a case-sensitive manner after years of VB.)
That's what I meant by "OO services" in original comment: a class (or composition of classes) that maintains some state e.g. a connection, maybe some cached data or whatever, and wraps some third party services or DB calls, etc, and optionally create an interface it implements, for IoC and/or unit testing. This works quite well in F#. F#'s anonymous classes are a great conciseness aid there too (C#'s aren't sufficient; they're really just anonymous records).
It's just all the class inheritance hierarchy stuff that is uglier in F#.
Yes, and that makes it harder for F# code to effectively re-use existing C# libraries. I know it is _possible_, fsc can reference DLLs no problem but unless that code was written to be idiomatic F# then I will have to awkwardly write some classes with a worse syntax than C#.
And each time, it turned out to be a mistake.
Things went way more smoothly by just using an F#-native web framework like Falco or Suave or Giraffe or …
It's just too piecemeal right now.
In the JVM world, mixing languages works a lot better, because you can compile .class files instead of whole assemblies. So mixing clojure and Java, for example, is very easy to do in any order.
https://dotnetfiddle.net/F5fPZy
The class implementations add a bit of upfront verbosity but the calculation at the end is arguably nicer to read: that method is concerned with applying the discounts, but in the F# case it has to know how to apply each type of discount, while the C# encapsulates that in the Discount implementation.
decimal subtotal = OrderLines.Sum(ol => ol.Product.Price \* ol.Quantity);
return Discount?.Apply(subtotal) ?? subtotal;
vs let subtotal = List.sumBy (fun ol -> ol.Product.Price \* (decimal ol.Quantity)) orderLines
let totalDiscount =
discount
|> Option.map (fun d ->
match d with
| Percentage p -> subtotal \* (decimal p / 100M)
| FixedAmount f -> f)
|> Option.defaultValue 0M
subtotal - totalDiscount
Though I'm also pretty sure F# could do the same thing with the classes if you wanted. And someone else pointed out you can write some C# that looks the same as the F# if you use a switch statement. And I'm pretty sure you could make it look even more F# if you used more Linq.I guess the F# matching is pretty much like interface/class checking as follows? I don't see the functional difference:
if (discount is IPercetage percentage) { .. subtotal \* percentage.value / 100 ...
else if (discount is IFixed fixed) { .. subtotal - fixed.value ...
And you could do even more with some newer C# features like anonymous classes etc.I'm not left feeling like I'm missing out on anything without discriminated unions in particular. I'd be interested to see some cases where the functional nature of F# does make a large improvement over the equivalent C# though.
It's so comforting to know up front that you've done everything you need to do!
let calcDiscount =
function
| Percentage p -> subtotal * (decimal p / 100M)
| FixedAmount f -> f
And then: let totalDiscount =
discount
|> Option.map calcDiscount
|> Option.defaultValue 0M
F# is missing some of the convenience operators, but you could easily add these yourself if you wanted: let (?) x f = Option.map f x
let (|?) x y = Option.defaultValue y x
and so totalDiscount becomes: let totalDiscount = discount ? calcDiscount |? subtotal
The benefit of DU's over inheritance for me is that you can't tie yourself to any additional details that get stuck onto Discount (since you can't extend DU's). If you have loosely coupled data you won't have sort-of-relevant methods possibly mutating shared state. The discount calc only takes a simple thing hidden away by the Discount DU, and so there are fewer plates you're trying to keep spinning in your head (all of "what could be"). (ItemA, ItemB) = (ItemB, ItemA)
~50% of our methods don't even have block bodies anymore... Switch expressions are our bread & butter now. Once you learn how to combine expressions + switch + LINQ, you have the triforce of poor-man's functional programming inside the perfect imperative realm by which to protect it.then move to a so called startup in France and CTO laughed when I was speaking about F#... this is why people dont use it more because they never learned FP.... so sad
public record Product(string Name, decimal Price);
public record OrderLine(Product Product, int Quantity);
public abstract record Discount
{
public record Percentage(decimal Value) : Discount();
public record FixedAmount(decimal Value) : Discount();
}
public record Order(List<OrderLine> OrderLines, Discount? Discount)
{
public decimal CalculateTotalPrice()
{
var subtotal = OrderLines.Sum(x => x.Product.Price * x.Quantity);
return subtotal - Discount switch
{
Discount.Percentage { } percentage => subtotal * percentage.Value / 100M,
Discount.FixedAmount { } amount => amount.Value,
_ => 0M
};
}
}I thought about switching to F# a few months back, but as I started working with it, it wasn't enough of a benefit over C#.
Ideally you would want to write `abstract sealed` for `Discount` too, but C# does not allow it. CIL does support it however, and it is how F# implements sum types. There's no reason I see for C# to continue enforcing this restriction, and lifting it would allow us to write proper DUs.
But I think this is a myth. F# the language is better than C# at most things and certainly better overall. If you are going to use F#, you may as well go all in and get all of the benefits.
For web services, Giraffe or Suave combinators are much easier to reason about than ASP.NET MVC patterns.
Please note that seq is not a data structure but an iterator.
https://stackoverflow.com/questions/4232130/an-implementatio...
If you've got a C interface, LibraryImport in C# is sophisticated enough to get the job done on its own.
.NET 1.x brought Managed C++, which was replaced by C++/CLI in .NET 2.0.
However Office and WinDev were never big fans of it, so its main purpose has been as easier interop for C++ libraries indeed.
Also COM and C, as for anyone that is comfortable in C++, is is easier than getting all marshaling annotations correct.
However it isn't an option for code that has to be portable, as it relies on Visual C++ toolchain.
dotnet new webapp -lang F# -o MyFSharpWebApp -f net8.0
was the command I used. This was in Windows 10. I'll probably attempt it again in WSL2 when I have time, though I'm not expecting better results.Shows all the available templates - you could give mvc or web a try?
You'll note that in the list of default templates:
ASP.NET Core Empty web [C#],F# Web/Empty
ASP.NET Core gR... grpc [C#] Web/gRPC
ASP.NET Core We... webapi [C#],F# Web/WebAPI
ASP.NET Core We... webapp,razor [C#] Web/MVC/Razor Pages
ASP.NET Core We... mvc [C#],F# Web/MVC
ASP.NET Core wi... angular [C#] Web/MVC/SPA
ASP.NET Core wi... react [C#] Web/MVC/SPA
"webapp" is actually a flavor of MVC with Razor Pages to define the UI, which is itself a flavor of UI that's different from the typical HTML/JS/react setup most people use.
I think the cleanest way to do web development with .NET is to leave .NET-isms out of the UI (.NET UI is, ironically, its weakest link now) and keep it on the server.
I do look forward to having opportunities to mix the two. C# + a gui framework and F# for the non-gui logic works really well in my experience.
I have a ~300kloc project with zero cyclic dependencies between any types or functions, and its delightful to maintain and test. If I had written this in C# I would've taken the easy way out many times and just made cyclic dependencies between types, which become non unit-testable without creating mock types just to test.
I could implement this with a super simple prototype where you list dependnat files as comments at the top of each file. I'd honestly rather do that than reoder items in XML by hand.
Binary floating-point numbers are less than ideal for keeping discount percentage because a value like 12.3% can’t be represented exactly.
So, a power chord?
Why would we want to live in the ".NET world"? I say adopt a programming language, or languages-ecosystem, that's not useful only in the "[some niche] world", but more-or-less everywhere. Whether it's older and well-trodden or newer and up-and-coming, I don't see the benefit of limiting yourself that way.
The ".NET world" is comparable to the "JVM world" or even the "LLVM world": a virtual machine target with a varied language ecosystem and a lot of real machines supported at runtime (in a cross-platform, open source way today).
".NET world" is defining a complex language ecosystem, and it probably isn't as "limited" an ecosystem as you seem to think.
Unity is used by a huge number of mobile games and uses C#. The Godot engine also supports C#.
On desktops not as much, but it is sometimes used for cross platform development, and plenty of games on Steam are written in Unity and run cross platform.
Linux desktop could have had a better story when MonoDevelop was gaining popularity, but Linux/desktop support basically got sidelined when Xamarin shifted focus to mobile development, and the acquisition by MS killed mono.
Since MS went fully open source on .NET, the situation for Linux is improving, but it's not yet a choice for most developers on Linux and may likely never be, because everyone wants to write web apps.
Many of the Linux desktop apps written with Mono have been abandoned or are barely maintained.
There's a few recent apps written with Avalonia which run cross-plaform. Wasabi Wallet (bitcoin privacy focused wallet) is a good example. See more here: https://github.com/AvaloniaCommunity/awesome-avalonia#sample...
In practice, most of the RPFs that we get for UNIX deployments, rather want us to provide solutions in ecosystems born out of the UNIX world and aren't keen in hearing about .NET, even if it supports UNIX platforms nowadays.
Back in the .NET Core 3.1 days, I had a migration project from .NET Framework to Java, as the customer didn't want to stay in the .NET ecosystem, and given the amount of code they had to rewrite to be fully functional in .NET Core, they decided to move elsewhere.
They aren't alone, Sitecore once the the lighthouse of enterprise .NET CMS, is now a polyglot platform, where most of the new products are written in a mix of Java and JS/TS.
Given the recent efforts in WCF Core compatibility and System.Web wrappers, it is quite clear that the decision to create a Python 2 / 3 schism in the .NET world is taking its toll.
We've had very different experiences. I've never had a problem replacing dependencies on WCF or System.Web, have yet to ever see a use case for WCF Core, and have yet to find System.Web dependent middleware that wasn't trivial to rewrite as modern ASP.NET (Core) Middleware. (ETA: Or toss. Some of what I find done in System.Web has only that one destination, because it is obsolete or was a bad idea in the first place or there's an easier way to do it.)
“Trade offs”