GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — `Discussion`: `FSharp.Core` breaking/incompatible changes · dotnet/fsharp · Discussion #16231
|
Would like to start this discussion for how should/shouldn't we be introducing breaking changes to core library.Why: Things like the issue below, are reasonable and benefitial for average codebase but can be/are breaking runtime behaviour: Overall rationale is: this probably should be done eventually if we don't want core library to stagnate and lag behind in features. One of the solutions is to use shadowing:
Approach has some pros and cons: Pros:
Cons:
Question is do we think it's worth it? I personally do, since otherwise core library will be stagnating and lagging behind in features. fyi @T-Gro @Happypig375 @dsyme @cartermp Request for comments: additional approaches, flaws in current approach. Update 1: For those with edit permissions, feel free to add to OP's pros/cons and additional approaches. Update 2: We've had an idea of "layered" core library, which essentially may cover this approach as well, but ids probably a bit orthogonal to this specific discussion (thought related to it). |
There was an error while loading. Please reload this page.
|
A few thoughts:
|
There was an error while loading. Please reload this page.
|
For the runtime breaking scenarios, I think the "project template" solution might be a solution. |
There was an error while loading. Please reload this page.
How would it be distributed? We don't really want a new NuGet, or more "special" assemblies/projects at this point, it might become a maintenance nightmare very soon (since Idea of "layered" core library is a good one, but probably a bit orthogonal to this one (thought related to it).
This might work, however, we should keep in mind that it will require bringing in some msbuild magic to when we invoke compiler, to let it know which libraries/modules/namespaces to autoload. Logic of "default" behaviour will be spread thin between project file, compiler and core library (parts). This will also become very polluted very soon, if we will chose this approach.
It's also good to keep in mind that it's not how it currently works. Right now, one opts-in implicitly into new language features every time SDK is updated for the project (i.e. we always use |
There was an error while loading. Please reload this page.
|
I think for so-called "hypothetical" breaking changes this seems to a reasonable way forward. It just needs to be very well documented and clear in the release notes for the language / FSharp.Core release version. |
There was an error while loading. Please reload this page.
|
Problem is nobody reads those, and if they do, it shows that people don't pay enough attention. |
There was an error while loading. Please reload this page.
|
Understanding how release notes interacts with your own code base can be hard. I might read it and think Could still be the reasonable way forward. F# is rather small language and has a pretty up to date community so could still be worth it in a risk/reward scenario. |
There was an error while loading. Please reload this page.
|
Can an Intellisense refactor save us? |
There was an error while loading. Please reload this page.
You mean like showing potential braking changes to people? It sure can, but I am not certain how to make them predictably detectable. |
There was an error while loading. Please reload this page.
|
Ah - that makes sense. I echoed Mark Seeman's missive about linters earlier today and I kinda felt sheepish about making this suggestion as a cheap way out. |
There was an error while loading. Please reload this page.
|
Just 2 cents, please always keep an eye on Fable compatibility. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
That was a point in recent discussion I had as well. Doesn't Fable have its own core library? Or do they have FSharp.Core fork and use it as a source? I'd imagine they'll need to have a per-language mapping from I imagine if we choose to do shadowing, no "binary" breaking changes will take place. |
There was an error while loading. Please reload this page.
|
Yes, Fable reimplements the core library for all languages. Any breaking change should eventually be a breaking change in Fable as well |
There was an error while loading. Please reload this page.
But only binary breaking changes, not runtime ones, correct? So, shadowing wouldn't affect anything. |
There was an error while loading. Please reload this page.
I know there's the equality issue, but what else? Note that there's no guarantee of forwards-compatibility, and so for example, if FSharp.Core were made |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Vectorized collections operations, for example. Or if we'll want to migrate async to state machines, there might be some breaking changes. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I remember a huge PR a few years ago by @manofstick to replace |
There was an error while loading. Please reload this page.
|
The Option type being a ref type when it should be a value type is a breaking change needed by the high-perf, real-time devs. Having to use OptionValue is a PITA. Many libraries only accept/return Option<'T>, which generates a lot of GC activity in a hot loop. |
There was an error while loading. Please reload this page.
|
Good direction, a specific point I would like to see changed is that |
There was an error while loading. Please reload this page.
|
Yeah, versions are up for discussion, not sure if it's the prettiest solution or not. In regards of syntax changes - also need to think more about it, what would it imply and how can we communicate this. |
There was an error while loading. Please reload this page.
|
I think it could get pretty unwieldy to maintain differing versions here :/ |
There was an error while loading. Please reload this page.
That's my concern as well. Also opens up potential rabbit hole of "try to figure out what implementation is used based on sdk, language version and project flags" |
There was an error while loading. Please reload this page.
|
My understanding is that if breaking change is actually fixing the long-standing buggy (or ineffective) behavior then it's normal. Relying on "incorrect" or undocumented behavior is a mistake anyway, so fixing it to to be "correct" only makes invisible bugs to become visible. |
There was an error while loading. Please reload this page.
It won't, just an old fsharp.core will be used, depending on language version.
That's the whole point, introduce a breaking change, with ability to quickly opt out (either via language version or some property). Alternatively it can fully be opt in via the same mechanism. Can't just make a breaking change and not let people to chose to fallback to the old behaviour. |
There was an error while loading. Please reload this page.
This doesn't have any importance to me. Let's say I have a silent bug sitting in my code which relies on buggy NaN comparison. It's 99% chance that I don't know about it (otherwise I would fix it myself). As I don't know about it - I upgrade FSharp.Core to get new features and/or performance (why do I upgrade otherwise). And it appears that upgrade breaks in production. At the moment when I investigate and issue, it would be clear that the issue is that I relied on NaN in the first place, so I'll rather fix it than enable compat.
If people are that afraid of doing upgrade that after upgrade they want to enable compat, then they shouldn't upgrade at all. Because the unintentional breaking changes can still happen and nobody is immune of those. |
There was an error while loading. Please reload this page.
Exactly, you're only describing your experience, which is not necessary mapping to others'. I talked to some of F# folks who use it in bioinformatics or telecoms, and they will appreciate if we don't break much of runtime. |
There was an error while loading. Please reload this page.
I'll definitely appreciate this as well! :) But whenever I upgrade major version of any library or framework, I do acknowledge that runtime can be broken. Also, if I really care about the minimizing the impact of upgrade, I'll very carefully read release note and possible breaking changes of major version change. |
There was an error while loading. Please reload this page.
|
I agree with @Lanayx . Any runtime bugfix is "breaking" since when fixed it is a runtime change in behaviour and people may be relying on the bug. We should not be scared about bugfixes! Any approach that unblocks F# progress is good, especially in an area like the canonical example here which both fixes a bug and improves performance. That includes the proposal here. However the proposal adds additional complexity in communication, and may add additional complexity in the compiler, which will then result in making it harder to make improvements in future. So I believe the best approach would be to do nothing except fix the bug. If anyone for historical reasons wants to preserve runtime behaviour then they can specify an FSharp.Core version and a dotnet sdk. |
There was an error while loading. Please reload this page.
|
Another solution that comes to mind is to have a new Core lib "layer", something like A reference to (Yeah, this approach has a bunch of downsides, and for now I'm proposing it for brainstorming purposes only.) |
There was an error while loading. Please reload this page.
|
That sounds no different to just a nuget with some shadowing modules. |
There was an error while loading. Please reload this page.
|
Yep, that is. |
There was an error while loading. Please reload this page.
|
Ok, done, what should I include next? https://www.nuget.org/packages/FSharp.Core.Faster We can test things on this package version and iterate faster than normal FSharp.Core. |
There was an error while loading. Please reload this page.
|
Nice! When shadowing you might run into #13535 though, as we created such a library internally as well |
There was an error while loading. Please reload this page.
Just to be clear, I unironically intend to use it as platform for experiments and benchmarks. I also might try and use it in our VS extension (since it's more or less independent) and maybe in future LSP layer, since it'll be a new project with small to no risks. |
There was an error while loading. Please reload this page.
|
Is it possible to make options, choices, etc structs and automatically insert glue code for conversion to reference types on interop with old libraries? |
There was an error while loading. Please reload this page.
|
Been thinking about it as well. Will experiment with it. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Don't forget tuples, basically they're wrappers, should've been ValueType |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
If such a breaking change is introduced in Fsharp.Core 9 would that mean any nuget package referencing Fsharp.Core 8 could misbehave in my app using Fsharp.Core 9 ? |
There was an error while loading. Please reload this page.
|
If newer core is loaded, then yes (given its runtime change and nothing was inlined). |
There was an error while loading. Please reload this page.
|
DESTROY ALL NON-VALUE OPTIONS |
There was an error while loading. Please reload this page.
|
That is a non-behavioural breaking change. It will result in huge compile-time and runtime breaking changes, and will likely never happen in such form. I can suggest shadowing for all functions used to work with value options. |
There was an error while loading. Please reload this page.
|
Shadowing is a great idea that solves the problem. Throw in a nice compiler warning too and we'd be good to go! |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
If we don't break things other people will, it's part of growing up, (none ValueType wrappers) needs byebye |
There was an error while loading. Please reload this page.
|
Shadowing doesn't guard against code that uses reflection. |
There was an error while loading. Please reload this page.
Yeah, but reflection should then depend not only on the structure but also on the types of entities (structs vs ref types, etc), which is surely a "deep" implementation detail, and people should not do that (I know they will anyway). |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Leave behind people who wants to use the old version, by introducing change inside next version of F# / .NET and telling people if upgraded to newest version there is breaking change, start documenting the behavior of old version and the new version of the language is different and how, and making these changes a moduler part of the language, so people who wants to use the old version of a specific part of the language can specify so (some how), other parts of the language can be using latest F# features But first introduce breaking change in a way that both the new version and the old version is equally obvious, so McGriddle w/peanut and McGriddle without peanut both are on the menu, now 1~2 yrs has passed we can have the older version available in non obvious way and eventually be making the decision whether to keep the old version around (.NET Framework is around still) Meaning in visual studio have the option of { use ValueType for the fsharp core library } available in .NET 9 and turned on by default Now if we are starting to have a bunch of flags in Visual Studio project property and one single { use latest } flags to hide them all that's off by default for the brave, the fsharp language can be agile enough, similar to Haskell If fsharp is performant and elegant, that's a result everyone wants seeing Sometimes you simply wanted to write quantum entanglement in classical computer in fsharp because it feels closer to doing math |
There was an error while loading. Please reload this page.
Yeah, this isn't happening any time soon. Especially if the change is "silent" or runtime. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
People building their project in .NET 6 is still going to use the old version, you can achieve the adaptive compatibility because people has to change their project to use the newer version of Fsharp, as we are moving on to .NET 9 if they use new version of course they get new features and breaking changes and what comes with it |
There was an error while loading. Please reload this page.
|
This has nothing to do breaking changes in core library. |
There was an error while loading. Please reload this page.
|
This, again, has nothing to do breaking changes in core library. |
There was an error while loading. Please reload this page.
|
I love planned, intentional breaking changes. It means that things are progressing, and the truth is: people complain about it, but most of the time it takes an average mid-level programmer less than a day to make whatever changes need to be made for new versions of stuff. (I'm not as big of a fan of unintentional breaking changes, i.e. bugs.) We're in a new world with LLM's and Copilot. If you imagine a compatibility helper process of some sort (one-time migration tool? integration with IDE's? a target in MSBuild? just thinking out loud...) that submits code to an LLM and allows us to automatically evaluate it to find things that could be breaking with a new version, it allows us to move much faster than we ever have before in terms of breaking changes. I'm a huge fan of the expansion in the last many years of warnings and code fixes (💡), but those rely on the compiler to find issues. LLM's can do more and different things than pure compilation. Yes, as of January, 2024, this seems like a less reliable way than compilation, but I think of Wayne Gretzky's famous quote: "I skate to where the puck is going, not where it has been." Looking ahead to 2025-2026, with improved, much less expensive GPT-4-level models, and then more advanced GPT-5-level models, incorporating LLM's into our compilation processes seems like a no-brainer. This - breaking changes in a new language version - is exactly the kind of thing that's a perfect use case for starting down that road. Taking advantage of these new tools lets us accelerate the changes we can make with our languages. I'm going to be building LLM usage into the source control layer with Grace in as many ways as I can think of. Using them with large context windows is expensive today but will be much less expensive in a couple of years, and using them for this - a one-time(-ish) migration between language versions - is so much less expensive than loaded-cost-per-hour for a programmer. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I think I would favor the simplest break possible: release a new major version of FSharp.Core, referenced by default by the new SDK. If someone has pinned an older version of FSharp.Core, use that version; otherwise, use the one that came with the SDK—as is currently done. When the new SDK compiled an existing project, we could use target framework as a heuristic and emit some kind of warning if it was lower than the framework version in the SDK at release and the project did not have FSharp.Core pinned. The warning would mention the incompatible runtime behavior in the new FSharp.Core version. The user could dismiss the warning for such a project in two ways: if targeting an older framework version, say ProsWhen compiled with the new SDK:
ConsWhen compiled with the new SDK:
|
There was an error while loading. Please reload this page.
|
(epistemic status: personal non-canonical prediction of my employer's preferences) We are currently stuck with FSharp.Core v5 because of an unfortunate choice many years ago to standardise on pickling instead of serialising. Any upgrade of FSharp.Core is currently breaking for us due to the resulting inability to read in output that was pickled from older versions of FSharp.Core. I've been putting forth some effort over the last few years to unblock an upgrade by moving parts of the world to more stable output formats, but it's a very slow job. So it would make things much easier for us if we could opt into new behaviour purely from the SDK or language version rather than FSharp.Core. Given warning and the ability to opt out on a case-by-case basis, I expect we are generally happy to accept breaking changes as long as they are very clearly indicated in the release notes. Enough of us do read the release notes each cycle that we'd learn about it immediately. We already view SDK upgrades with deep suspicion (because NuGet is typically unusable until at least the .20x release due to performance regressions in the .10x releases), so we are already on high alert when we move to a new SDK and I'd expect us to catch issues pretty quickly. Our development model is to keep versions of critical libraries in tight lock-step across all our in-house dependencies, so "silent breakage when a library was built and tested with a lower FSharp.Core and was consumed in a binary with a higher FSharp.Core" wouldn't arise for us as often as one might expect. Developers aiming for maximum compatibility already have to test against many different versions of the runtime, and this is easy with |
There was an error while loading. Please reload this page.
|
Interesting problem. Seems manageable to migrate by defining a serializer that adds a new serialization method in addition to pickler and a deserializer that deserializes either, then deserialize and reserialize the data, then delete the pickler, then update fsharp.core. We are doing well with cbor serialization, we find that manual serialization is great for making changes needing backwards compatibility including occasional tricky sequential changes like the one above, and that github copilot is great at writing serializer code so you might find that the process goes faster now than it did a few years ago. |
There was an error while loading. Please reload this page.
|
Yeah, it's all solvable in principle, but the correct choice of serialisation format is different on a case-by-case basis - it's not the kind of thing one can trivially automate, or else you end up trying to reserialise a petabyte of floating point data as JSON :) |
There was an error while loading. Please reload this page.
|
That may be, but 1. it's possible to choose something which dominates pickler in terms of speed and size, and 2. JSON is heavily dominated in both dimensions so never the correct choice. |
There was an error while loading. Please reload this page.
|
(By the way, another thing that appears to break FsPickler is DictImpl between 8.0.100 and 9.0.100; I just reverted an FSharp.Core upgrade that was intended to work around #18298. Again, we are performing a completely unsupported operation; just wanted to record somewhere for posterity because I sometimes need to recall these things months later.) |
There was an error while loading. Please reload this page.
|
@Smaug123: Could you please point me to a break in DictImpl? I was not aware of that one. |
There was an error while loading. Please reload this page.
|
I honestly think it's worth it. Dealing with different versions of things is already a challenge, but so often that challenge seems to come with little real world benefit for the hassle cost. A breaking change moving forward is.. Well actually moving forward. It can suck, but as long as there is a bunch of support for the change, something like making analyzers that check for known breaking changes, and supply suggestions, or diagnostics or some sort of clue about whether or not it's going to happen or what to do about it, I think it's ultimately healthy to cull the crufty, to prune the dead branches, etc... It's a cost, but the reverse is a higher one, I think. |
There was an error while loading. Please reload this page.
|
This a good approach. Those are the good breaking changes - if their breakiness can be detected by analyzing the source code. Perfect example is the generic equality and how it works with floating point |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Which changes are actually proposed @vzarytovskii ? Only avoiding boxing on equality ? Is there a list with reasons , impact and current workarounds? |
There was an error while loading. Please reload this page.
|
This is not proposal for any specific changes. It's is a discussion of possible approaches if library breaking changes make sense and we want to introduce them, how it should be done. |
There was an error while loading. Please reload this page.
|
Don't do breaking changes! Here is why: The news about braking changes is scary and confusing for new F# adopters. So bad for F# adoption Or did I miss other braking changes that are not about performance ? Or can't be fixed with shadowing or additional code? |
There was an error while loading. Please reload this page.
|
Any arguments made against breaking compatibility are pretty much defeated by the fact that we've already successfully navigated possibly the biggest transition in the .NET Framework -> .NET Core move. If we could break compatibility with complete disregard for legacy code, imagine what we could do:
I would venture to suggest, that while keeping compatibility is a noble goal, in the sufficiently long term it's highly detrimental to the adoption of F#. Trying to explain to new developers how/why all this stuff exists that we shouldn't use and have to work around is really annoying. if we do not actually do this, F# will continue to languish in its warty state, filled with thing that seemed like a good idea at the time. Is it really is the intention of Microsoft to literally keep all of these problems around forever? that seems like an engineering approach that I and many others would fundamentally disagree with. I really don't want to have to be dealing with these legacy elements still in 10 years time - we might as well try to get rid of them sooner rather than later. |
There was an error while loading. Please reload this page.
What you're suggesting is pretty much "Hey, let's not think of users with legacy code bases, who just update SDK and want their code just work, and instead do all sorts of breaking changes, for reasons.". It's not right approach here. There are a lot of F# projects out there which can't account such breaking changes, we for sure need to think about those.
We can't "just" make it a struct, it will break all existing libraries which are using options and not rebuilt with latest
They are not the interchangeable, they have different approach, cold vs hot, implicit vs explicit cancellation, composable vs not, etc.
Not sure what that means, but probably less breaking than others.
Also not sure what that means.
That was discussed many times, we wanted to hide a bunch of mlcompat under separate lexer/parser.
It's not, we're not literally keeping all of these problems around forever. This is exactly why this discussion is started.
There are people with different projects and use-cases, you described yours - when you're fine if every major version language does breaking changes, both compile-time and runtime, and you're dealing with those. The other side of the coin is people who can't and won't deal with them, as it simply doesn't work for them. Is is important to find a middle ground (which this discussion aims to do), and not just say "hey, let's just change things" or "hey, let's keep this issue foreve", as it's not productive. |
There was an error while loading. Please reload this page.
Just to note that the large majority of .NET Core work was in maintaining compatibility with most of .NET Framework, and especially in ensuring most libraries can get ported over to .NET Standard fairly painlessly. Of course there were some big breaks, but the reason why most people have been able to move their code over is explicitly because of legacy compatibility. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Can we incorporate some features of FSharpPlus e.g. NonEmptySeq? Maybe a NonEmptySeq can even be defined as a seq with const generic length constraint > 0. These can help ensure domain constraints. They are related to breaking FSharp.Core because of fsprojects/FSharpPlus#480: |
There was an error while loading. Please reload this page.
|
It might be useful to consult the changelogs for ocaml. I know next to nothing about ocaml, but notice that every release seems to have a decent number of 'breaking changes'. I wonder how they prepare developers for breaking changes coming down the pipe (if at all?). I'd imagine others in the F# community might be more aware of the culture in ocaml, and could weigh in. I think adding 'Compatibility' modules/ns in core is more confusing. New users have to do software archaeology to gauge when to use it, F# dev team has to test both versions, veteran users have to alter a bunch of files either way. You sort of double the space of possible problems. We have a pretty savvy and active developer community, and I would guess most F# codebases are on average not terribly large (~1-20KLOC?). If something would give people concern around a breaking change, they could opt to not immediately upgrade their .net version. Rigidly maintaining legacy functionality is a problem that very popular languages must contend with (C, C++, Java, etc). I think F# isn't quite at the stage where we have to be so wary of this yet. Most of the time when I mention F# to developers they either don't know what I'm talking about or assume it's some kind of Fortran. So, I guess my stance on breaking changes can be best summarized by an old Rob Zombie song, "Dig through the ditches and burn through the witches and slam in the back of my compiler." |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
F#'s commitment to backward-compatibility is key to its credibility in my opinion, but at the same time it is really unfortunate when key features and/or undesirable behaviours are not addressed for the sake of keeping everything backward compatible. My2cents are that as an end-user, I'd be totally happy to have a few breaking changes with major releases as long as I can see that the team remains conservative with this type of changes, and that the overall culture remains one of stability, and that F# does not become some sort of toy project where the team breaks things left and right because it doesn't care about old versions. |
There was an error while loading. Please reload this page.
|
Is F# growing or slowly dying? I love F#, but We all know these things need to be fixed:
People who NEED this stuff are waiting for it. The existing codebases...breaking changes are painful, but those maintainers love F#, want it to grow, and will meet the challenge. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Here I meant the hot path which shouldn't be allocating on the heap at all to avoid any GC pauses (probably used the wrong term) |
There was an error while loading. Please reload this page.
|
The first three points of that list would be quite easy to implement in a fork-of-fsharp. |
There was an error while loading. Please reload this page.
|
@EricM81, F# is only slowly dying from having passed the threshold of immortality, the slowness corresponds to that of the heat death of the universe and beyond, as it has been inscribed in the source of conscience itself. For all your low level needs, you don't have to rely on FSharp.Core library, and you can contribute to the practices that makes F# suitable. You can take the compiler frontend, and make a C backend for Fable, with all the things you mention, or look at the existing Rust backend. For dotnet users, F# users enjoy productivity parties in high value codebases, that are all slowly dying at a slower rate than code which is not null safe, full of OO hierarchies, bloated with virtual dispatch, etc. The community is welcoming the non breaking suggestions, and looking forward for your PRs 🙂. I'd like a whole program optimization that elides option and allocations for closures, etc., as much as you do, but however this may or may not exist, the mighty CLR team is doing the ground breaking work so it is less of a concern, to 99.99% of F# users (I know, this is not so many of them). For most existing F# users, I think compiler & tooling performance is above priority than beating rust at lower level code (C# isn't there, why expect F# to be?). |
There was an error while loading. Please reload this page.
|
For what it's worth, recent improvements in the JIT can already make option allocations disappear altogether in some situations. For example: open BenchmarkDotNet.Attributes open BenchmarkDotNet.Configs open BenchmarkDotNet.Environments open BenchmarkDotNet.Jobs open BenchmarkDotNet.Running [<MemoryDiagnoser(displayGenColumns=false)>] [<HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")>] type Benchmarks () = let x = Some 3 [<Benchmark>] member _.DoSomeStuffWithOptions () = x |> Option.map ((+) 1) |> Option.map ((+) -1) |> Option.defaultValue 0 ignore <| BenchmarkRunner.Run<Benchmarks> (DefaultConfig.Instance .AddJob(Job.Default.AsBaseline().WithRuntime CoreRuntime.Core80) .AddJob(Job.Default.WithRuntime CoreRuntime.Core90)) It looks likely that we'll get more improvements along those lines in .NET 10. (Of course the JIT cannot solve everything, but it's not nothing.) |
There was an error while loading. Please reload this page.
|
This is great, but then if you are storing objects in objects or passing them to non-trivial functions this goodness disappears, and then you end up with a mix of Personally I would recommend to just use Like most of the time it just does not matter. Machines are amazingly fast. If you do care about performance though, maybe don't write your code in fsharp (but really we're talking about the 1% cases here - most performance is going to be algorithmically driven, not low level details) |
There was an error while loading. Please reload this page.
|
There are many reasons why
Have you tried not using
Have you written something that outperforms a function in
C# is there. This is a basic thing.
For me, it's real-time updates to geometry as a user clicks and drags control points. You avoid allocating anything because GC causes framerate stutters. You do the same thing you would do in C, and it's as fast as C. Use static arrays full of tiny, optimized structs to make the CPU cache happy, and you do as little branching as possible to make the CPU prefetch and branch predictor happy. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I'd be very surprised if collection module functions are slower than Linq esp for a type like FSharpList. Linq adds I believe two layers of overhead. It uses reflection doesn't it? Plus it doesn't know about FSharpList and will need to access it via seq and then convert back for something like That said, if you care about performance you don't want to be using FSharpList in particular except in rare situations. Out of hundreds of thousands of places in our codebase where collections are created I believe there is only one place where a linked list is useful for performance.
Yes. We use it everywhere except optional arguments and ValueOption optional arguments are in preview. F# has made a lot of steady progress towards having ValueOption usable everywhere and allowing struct representations of things in general, so these comments are out of date. Also you created 2 new threads about |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
The most obvious places are where LINQ uses SIMD but FSharp.Core can't (because it's netstandard). I tried this years ago and failed for that reason. Orders of magnitude speedups are possible for things like Array.sum, but not in FSharp.Core. |
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I'm curious about this work @Smaug123 . My understanding from playing around with |
There was an error while loading. Please reload this page.
Be surprised then, because they are. System.Linq wraps functions like Select (Seq.map) and Where ((Seq.filter) with an internal type so that when you have a combination you just combine the two function calls, rather than accessing through the MoveNext(), Current mechanisms. It also carries things like the Count of the collection through calls to Select so that the size is available for allocations to calls like ToArray, hence requiring no temporary buffers. Also When temporary buffers are needed, the first few elements are stored in a stack allocated object, which also removes the need for temporary buffers. Now, if you're talking about List.map then yes, it would be faster than Select() |> List.ofSeq, but you are kind of talking apples an oranges here, as all the List functions are eager, and will allocate the complete list before moving to the next step in the pipeline as opposed to the lazy evaluation of Seq/System.Linq.
It does not. Unless you are using IQueryable, in which case you are doing somethingn completely different. Linq-to-objects is the Seq equivalent. Linq-to-database uses runtime reflection/tree manipulation to do things such as converting to SQL.
Go for your life. I have done many in the past, you can probably hunt some of them down in other github comments that I have written. Although the changes in platform's code generation has changed results.
Meh, the reason to use FSharp is FSharpList. Really. The joy in FSharp is pattern matching with lists. Just don't let performance concern you. I mean for the vast majority of cases it just doesn't matter. And if it does matter, I would highly recommend not using FSharp. You just end up fighting the language. So go ahead an embrace list, along with all the garbage it creates.
You don't use linked-lists for performance - or if you do, you don't use an immutable linked-list for performance... (OK, I lie, there can be some edge cases - if you are leaning heavily into into sharing roots of the lists.) Look, FSharp is great. I love the language, It taught me heaps, and my c# is better for it, and I'm a better mentor to other developers because of it. I highly recommend that people spend 6 months coding it exclusively so that you really get to appreciate the beauty. BUT, I would not recommend it for anything that I care about performance for. Yes you can carve that bits for performance out and do them in another language, or bastardize the usage of fsharp to make it work (I know, I have), but its just harder to then maintain. c# is till missing discriminated unions, but does a faily good job, and presumably will be easy to refactor into whatever final form that they will eventually choose. (c# pattern matching via switch statement also work fine, although ending each block with |
There was an error while loading. Please reload this page.
LINQ just does it all behind the scenes, doing whatever type tests are required to ensure the dispatch is safe. You can use the low level Vector APIs, but you're expected just to let LINQ work out when it can speed things up. |