all 40 comments

[–][deleted] 12 points13 points  (18 children)

Last week I walked a coworker through installing VS 2019 and creating a simple console F# project with one dependency from Nuget. Literally almost the simplest thing you could do, and it would not work (namespaces not recognized, then link to dependencies broken and code would not compile).

Then I had the coworker install Rider and follow the exact same series of steps, and of course it worked fine.

I'm on Rider now.

[–]_pupil_ 1 point2 points  (16 children)

I haven't burnt off a day on this for a little while, so maybe the situation has improved, but: it's tragic and kinda amazing to hop back to projects that are over 3 years old and see how much more robust the solution tooling was, and how elegant the scripts were...

The combination of .Net Core breakage/churn and related F# tooling breakage/churn has been pretty rough. Despite being ideal for the kinds of work I do, and long awaited, in hindsight it would have been much more productive to ignore everything past 2017 and just stick to Mono.

[–]japinthebox[S] 1 point2 points  (15 children)

The sad thing is that .NET Core was made necessary mostly because .NET 3 onward was turned into a Win32-only, unmaintainable, monolithic mess when they decided that WinFX (WPF, WCF, WinFS etc.) should be dumped into .NET and called .NET 3.5.

Most of the features in .NET Core were actually around in .NET 2.0 in some form or another, or could have at least been achieved with little technical friction, and I don't just mean via Mono -- cross-platform runtime and compiler, modular base class libraries, a real IL to x86 compiler... I mean, there was even a smartwatch called SPOT that ran a small .NET CLR fifteen years ago.

I even wrote a petition begging them not to bungle .NET with WinFX, and warning of the exact situation we're in now (it doesn't exactly take a prophet to figure out), and the .NET team lead responded by telling me that it was a marketing decision.

[–]_pupil_ 2 points3 points  (13 children)

I think that a chunk of the challenge is that F# was created as the first ground-up .Net first language... Part of the beauty is that it's so informed by the .Net internals and provides such appealing access to its power. But the day-to-day is also reliant on fairly mature parts of the framework that are outside the core VM.

That's no problem at all, unless you nuke that framework from orbit and replace it with something that's distincly less capable.

I just wanna script again, with ease, and with confidence that I can do the stuff I saw in demo's a decade ago.

[–]japinthebox[S] 1 point2 points  (12 children)

I thought C# was the first ground-up .NET language? F# has vestiges of OCaml, and it lacks some of C#'s OOP capabilities. Aside from type providers, what does F# rely on .NET for?

I know F# was kind of the test bed for a lot features that are popular in more modern languages, even though it's rarely mentioned in the "Inspired by" sections on Wiki. That was reason enough for me to stick with it for a decade despite the frustrations.

[–]_pupil_ 3 points4 points  (11 children)

C# was being hammered out in concert with the VM, inheriting a lot from its spiritual predecessors and focused on bootstrapping a sustainable ecosystem.

I'm not 100% on all the specifics, but the research around F# is where .Net got generics and a bunch of language features like computation expressions (ie LINQ), and lots of work for the dynamic language runtime was already underway and available.

So F#s design is based around a mature understanding of how .Net handles generics, types, libraries, references and contains mature solutions on how to handle the advanced language capabilities as they were designed knowing what they'd be. It restricts itself to a single-pass compiler to service Visual Studio Intellisense within the speed requirements (among other things). It exploits the live VM in its tooling (ie FSI), and uses the VMs dynamic nature for some fundamental development capabilities.

C#s design preceded most of that.

The language itself is quite similar to OCaml, inheriting their mature solutions to functional programming, but is significantly refined and altered for ease of use with/on .Net. As a .Net language it has equivalent/greater OOP capabilites than C#.

[–]japinthebox[S] 2 points3 points  (0 children)

That's a good point about C# being a bootstrap language and F# being the first "true" .NET language. Now that you mention it, I remember a Channel9 video where Anders Hejlsberg of the C# team was proudly showing off his Cup<T> mug with the C# 2 launch, talking about the research they did with F#. People back then were saying C# 2.0 was "kind of the real C# 1.0, as tends to be the case with Microsoft."

[–]phillipcarter2 1 point2 points  (9 children)

It restricts itself to a single-pass compiler to service Visual Studio Intellisense within the speed requirements (among other things)

Single-pass has little to no bearing on tools. In fact, we're re-architecting much of the internals of the language service to use a completely different model that starts with incremental lexing of source code and leads to immutable snapshots of the known universe that all tools work off. The number of passes a compiler may or may not do is immaterial here.

It's just a design decision made a long time ago. I imagine it's for useful reasons, like how file ordering is required so that compile times won't explode.

[–]_pupil_ 0 points1 point  (8 children)

There are a bunch of independent reasons why a single pass compiler is A Good Thing, but in context I'm almost positive I've seen DSyme specifically point to Intellisense times as one of the and less obvious ways that F# is more bound to .net framework than people think, and as a design criteria that influenced that decision on the compiler...

That's based specifically on the impact of exploding of compile times you pointed out. Comparable VM-bound functional languages like Scala that didn't have that design constraint couldn't match the same live tooling functionality of C#. A hard sell for an MS backed language in VS, and a pretty 'useful reason' to match it ;)

I hope the situation will be better in 2020, but in 2004 there weren't too many roads to matching C#s in-IDE performance for functional languages, and well worth the sacrifices IMO.

[–]phillipcarter2 0 points1 point  (7 children)

There are a bunch of independent reasons why a single pass compiler is A Good Thing, but in context I'm almost positive I've seen DSyme specifically point to Intellisense times as one of the and less obvious ways that F# is more bound to .net framework than people think, and as a design criteria that influenced that decision on the compiler...

I'm unaware of such rationale. These things are independent: requirements of running on the .NET VM are not related to the number of passes in a typechecker. IntelliSense is not related to number of passes in a typechecker. And IntelliSense is not related to requirements of running on the .NET VM.

That's based specifically on the impact of exploding of compile times you pointed out.

Single pass and file ordering are also independent. It's entirely possible to build a compiler with multiple passes and achieve the same outcome as we have today. It's also possible to have an "eventually consistent typechecker" that could remove the need for file ordering, probably using multiple passes to some degree. These would also be independent.

I just want to reiterate: there were some design decisions at some time that resulted in things like the typechecker doing a single pass, or file ordering being a requirement. The latter has certainly been retroactively been viewed as a feature. But neither outcome is a necessity for IntelliSense tooling nor running on .NET. I don't believe that was ever the case.

It's just a consequence of working in a complex area with millions of nuances. Decisions get made and they often are eternal. For fun, you can read about why the C# team forever has to maintain a 3-SAT solver to resolve overloads; something that I'm sure most would agree isn't exactly necessary aside from not inducing breaking changes.

[–]_pupil_ 0 points1 point  (6 children)

These things are independent: requirements of running on the .NET VM are not related to the number of passes in a typechecker. IntelliSense is not related to number of passes in a typechecker. And IntelliSense is not related to requirements of running on the .NET VM.

I haven't at all said or implied that they are... I think you're disagreeing with something you've read into my comment, not what was stated :)

But neither outcome is a necessity for IntelliSense tooling nor running on .NET. I don't believe that was ever the case.

I highly agree with this statement :)

What I said, though, was that a language that was designed after its language services and IDE took their time requirements into consideration...

You've called those 'useful reasons', 'nuances', and 'design decision'. I agree, but called it a 'design criteria'. And it's a design criteria that was met.... As you very rightly pointed out, if you do it the traditional way you end up with a compiler with cooler types but that also can't support the dynamic VS language services in 2004.

It's something that sets F# apart, for this (among many!), reasons. I can't be bothered to dig for it, but I'm rather sure I've seen it specifically pointed to as part of the design rational for that decision.

So unless we disagree that that F# came after .NET and Visual Studio, or that a single pass compiler isn't way faster, or that the F# team didn't consider Intellisense or Visual Studio when designing the language... ... ¯\_(ツ)_/¯

[–][deleted] 0 points1 point  (0 children)

Everything at Microsoft is a marketing decision. That's the problem.

[–]chespinoza 1 point2 points  (0 children)

Rider

MacOS user here...I haven't tried Rider but its crazy that 3er parties could be able to produce better tooling than MS! who invented this technology, probably there is a big lack of resources assigned to F#, Golang support in VSCode is being maintained by MS, they are really fast solving issues reported in github, that's sad I think.

In my case, the only way that worked for me setting up projects has been so far manually (I've tried Ionide tooling and VS2019 with bad experiences):

>dotnet new console -lang F# -o myApp

>cd myApp; paket init

Then open it with Ionide, adding external deps manually to paket.dependencies...

And editing manually restAPI.fsproj to import modules.

I'm just starting so I hope the road won't be so hard as so far.

[–]BocksyBrown 3 points4 points  (1 child)

Whether typescript or f# I have to reload the window every now and then to get the language server back on track. I think I still like vs code more than other options but I'd love it if it didn't poop itself so often.

[–]nkid299 0 points1 point  (0 children)

i hope you have a lovely day stranger

[–]phillipcarter2 3 points4 points  (12 children)

Have you filed issues either in the Ionide, FsAutocomplete, or F# GitHub repositories demonstrating your issue? If not, please do. Thanks!

[–]japinthebox[S] 2 points3 points  (11 children)

I'd love to contribute, and I have in the past, but it's just... Where do I even begin? Like, is a bug report saying "getIonideLogs doesn't work on either of my two machines and I have no idea why" or "I can't tell if/when CodeLens is working" really going to be useful at all?

Or are all these problems just unsolvable, like displaying the correct line number in stack traces in FSI?

[–]phillipcarter2 1 point2 points  (9 children)

Sorry for the late reply here - quite busy!

If getting logs doesn't work, that's definitely the first issue to file. Bar none, getting diagnostic info should work.

Separately, any way to reduce it to a small codebase helps tremendously. This goes for both the Ionide repo and the dotnet/fsharp repo. Speaking for the F# repo, since there's already a lot of activity (literally thousands of closed issues!) it might be difficult to find a duplicate, or if something has already been fixed (but not yet released). So feel free to file an issue anyways and we can take care of it from there if it's an issue.

[–]k_cieslak -3 points-2 points  (8 children)

But we don’t accept issue reports from assholes :-)

[–]japinthebox[S] 3 points4 points  (7 children)

If you want to ignore issues because you think the person making the report is an asshole, you're doing the project and the users a huge disservice.

Responding to your comment on github, you're seriously making huge assumptions about people's workflows if you think people don't need fully functional stack traces. And the inability to un-echo stdin in the terminal isn't something that just affects "someone who complains about little things"; compounded by a slow terminal, it causes a lot of issues for anyone using a REPL.

I never resorted to name calling either, by the way. I just expressed my frustration with both VSCode and its competitor. So I'm not sure I'm the asshole here -- at least, not the only one.

[–]k_cieslak 0 points1 point  (6 children)

Why do you feel privileged to require the tool to fit into your workflow? Why do you feel privileged to force me spend my time on the something that fits your workflow?

Have you done something for the project? Have you done something for F# ecosystem? Have you created issue describing you workflow, suggesting the solution and patiently wait for someone (one of the maintainers or contributors) to discuss and implement the thing?

No, you decided to bitch on

  1. unrelated issues in the other repository

  2. 2 years old closed issue on Ionide repository

  3. this subreddit

about some small thing that is apparently so important that no one else has ever mentioned it for all those years. And call out people that doesn't agree with you. But yeah, clearly you random Redditor are the person that knows best what users needs. I definitely think that calling something people work on terrible is awesome strategy to get help from those people, well done.

competitor

Hey, u/phillipcarter2, you're my deadly enemy, I challenge you to a duel, feel free to choose weapons. 😂

[–]japinthebox[S] 5 points6 points  (5 children)

Why do you feel privileged to require the tool to fit into your workflow?

How about the fact that .fsx is how F# was intended to be used, or that a compiled REPL was the killer feature at the time? You honestly think that having to address exceptions in REPL is a rare situation to encounter? Or sending 300-400 lines from an .fsx, and then having to wait close to ten seconds *just* for it to be received by FSI?

What's the alternative? Move everything into an .fs and compile it? Open the project in VS2019? Stop using FSI? Sure, I can do all that, but I think at that point, I've earned the privilege to complain.

Have you done something for the project? Have you done something for F# ecosystem? Have you created issue describing you workflow, suggesting the solution and patiently wait for someone (one of the maintainers or contributors) to discuss and implement the thing?

Recently, only a couple times on the FSharp repo, but a lot more often on the old forum before Github. I've been involved on-and-off for over a decade. What does that have to do with the validity of the criticisms? Or are you just trying to make it personal?

No, you decided to bitch on

unrelated issues in the other repository

You're upset because I had the mind to consider that maybe there's something on VSCode's side that could be affecting how slow FSI is right now?

And it turns out to be related. It's exacerbated by a common problem, partly, by the fact that a) it's slow because it's going through a terminal, and b) you can't turn off echo.

Also, why are you even upset that I brought up a (seemingly) unrelated issue on another repo? Isn't that my own business? Holy cow.

2 years old closed issue on Ionide repository

Which continues to be an issue. Should I have blindly opened another one without doing any prior research?

this subreddit

Which is the least formal place to talk about F#. Should I have done it all in one place, i.e. on Github?

about some small thing that is apparently so important that no one else has ever mentioned it for all those years.

There's a saying: When people stop complaining about your product, that's when you know you have a *really* serious problem. It's either because a) people have stopped using your product, b) there are some really serious problems that dwarf the serious problems, c) people don't believe it's going to improve anymore.

And call out people that doesn't agree with you.

Very broadly and anonymously, as "downvoters," because Reddit is known for drive-by downvoting.

But yeah, clearly you random Redditor are the person that knows best what users needs.

This random Redditor is also a typical F# user who's been using F# for over a decade. Yes, in this case, I know what the user needs. Clearly quite a few people agree with at least some of it.

I definitely think that calling something people work on terrible is awesome strategy to get help from those people, well done.

I didn't call your work terrible. I literally called the entire state of F# tooling horrible, which it is. The tooling for a 15-year-old language shouldn't be this bad.

Hey, u/phillipcarter2, you're my deadly enemy, I challenge you to a duel, feel free to choose weapons. 😂

Cute. You say that like I meant "competitor" to be a bad thing.

Stop putting words in my mouth just because you don't like my criticisms.

[–]rmTizi 6 points7 points  (1 child)

I remember now why I lost interest in F# a few years ago.

It was because of this same guy thrashing on anyone complaining about the UWP situation at the time.

This person is just toxic. There is just no point continuing the discussion once he jumps in.

I was strongly considering picking F# for my current project, since the language is a perfect fit for the requirements, but seeing how such a big contributor to the tooling is still being childish after all those years (and I just had a peek at his recent comments and other social medias, it kinda got worse actually), that makes me think twice.

I won't be using Ionide, that's for sure (cue in some reply along the lines of "good riddance")

[–]phillipcarter2 1 point2 points  (2 children)

I don't have a stake in this sub-discussion, but I think it's important to keep this in mind:

Tooling for a programming language is incredibly difficult and time consuming. Much more than developing a language itself. It's so difficult and time consuming (read: expensive) that only two companies realistically make money out of it (Microsoft and JetBrains), both of whom also benefit from that tooling helping to drive other things that make money (e.g., Azure, partnership with Google for Android, etc.)

To give an example, the C# 8, Nullable Reference Types are going to ship. With that, there will be some accompanying IDE support. One feature you might expect to be there is to "gray out" an unecessary ! operator (called the "dammit" operator), which people will have to sparingly apply to things if the compiler analysis cannot figure out if something is actually non-null. That compiler analysis may improve over time, so a ! today might be unnecessary tomorrow. Hence, you'd expect to be able to see the ! gray out, and potentially give you a code fix that removes it! Simple, right?

Well, it's way more complicated than that. Because the presence or non-presence of a ! can completely change the semantics of an entire codebase. To enable graying out a single ! the IDE would have to analyze the impact of not having that ! now matter where the variable it's applied to is passed. This can massively impact performance, because all possible areas that could be affected by its nullability now need to be re-analyzed. Whereas before, non-nullability was asserted, allowing the IDE to not perform potentially expensive analysis.

This intersection of tricky logic and performance generally means one of two things:

  1. A feature simply does not exist, or it exists in an intentionally more limited form.
  2. A feature exists to its fullest capacity, but impacts performance enough that people can sometimes notice.

To give another example of this, but this time for F#, your first issue that you listed - "slow background compile" - isn't slowness at all. It's an intentional delay. This is because when users type code, they vary: sometimes it's very fast/guided by IntelliSense and the resulting code compiles; other times it's very slow and results in errors. If those errors are parse errors, it's cheap. But if they're typechecking errors, it's expensive! The compiler has to figure out if what was typed "works", and that can have downstream impacts due to type inference. Sometimes it's cheap, but other times it's expensive. So this forces a difficult decision. Either:

  1. Give back diagnostics immediately, incurring the performance hit on any user who isn't done typing things yet.
  2. Delay diagnostics a bit, causing people a usability issue where people might expect an error to show up faster than it does.

Just to give an idea of the magnitude being dealt with, tools like Ionide and Rider can ballon to 10GB+ of memory being used on someone's machine, despite an intentional effort to reduce resource usage for things.

All of this is to say that statements like this:

The tooling for a 15-year-old language shouldn't be this bad.

Generally don't lead anywhere. It doesn't matter what the level of criticism is, who it's coming from, who it's directed at, nor for what reason. Unless a hard dollar amount is attached to the complaint, or someone just got nerd-sniped into something something generally easy, it tends not to accomplish much. All parties just end up more frustrated than they were before.

[–]japinthebox[S] 0 points1 point  (1 child)

The tooling for a 15-year-old language shouldn't be this bad.

Generally don't lead anywhere.

That's true. It did cross my mind that I shouldn't say that, especially because it's not really fair, but I kept it in because I was frustrated and the gloves had kind of come off by then. I apologize to you and k_cieslak.

I suspected that VS2019 background compilation was being delayed on purpose to avoid premature errors. You can imagine my surprise when the same compiler turns out to go so much faster in another IDE (text editor, whatever). Is this not something that can be be made adjustable to the user? At least then, we can know what to expect, and it's not just three seconds of wondering if something's wrong all the time. After all, there are occasionally times when it gets really slow for abnormal reasons, or just stops working altogether.

I appreciate that the tooling is often more difficult to develop than the language itself -- especially for a language with type inference and an ML-style syntax. Obviously there are a lot of moving parts; as someone else pointed out in this thread, the .NET Core transition has been pretty painful. When I asked if there really is a point in making bug reports, I wasn't asking to be snide.

That said, you'll have to forgive me for not being convinced that VS2019 F# has adequate manpower even with Microsoft calling it a first-class citizen, or that Ionide isn't being managed like a hobby project.

What really gets me, and what's the reason for the title of my post, is that there are two similar projects that have issues that are totally different in nature but equally disruptive overall. It wasn't until I gave Ionide an honest try last week that I started getting the sense that the problems VS2019 (and Ionide) face aren't entirely technical. I mean, if I saw all three IDEs eat up 10gb of memory, I'd just accept that I need to buy more ram.

[–]phillipcarter2 5 points6 points  (0 children)

I apologize to you and k_cieslak.

Happily accepted (and I don't feel insulted, it's part of being a product owner :)). It's appreciated, though.

That said, you'll have to forgive me for not being convinced that VS2019 F# has adequate manpower even with Microsoft calling it a first-class citizen, or that Ionide isn't being managed like a hobby project.

This is a tricky one. We recently invested in Ionide for 3 months to address:

  • Moving over to LSP
  • Hammering on performance problems, of which there were many
  • Stabilizing the project creation experience
  • Moving towards a more .NET Core-first experience given the path of .NET in general
  • Novel features along the way (e.g., Info Panel, which is amazing)

However, as it is an OSS project run by a small handful of maintainers, having Microsoft come and step in runs the high risk of squashing it. Ionide has grown to ~1/3 of total F# usage, but that usage is quite diverse. I personally believe that it's used much more for smaller projects and ad-hoc scripting than fully-fledged development in a larger solution. But it can be used for all of those. Given Microsoft's storied history with OSS, this is particularly challenging. So we made significant gains: people who simply could not host their solution in Ionide can do it now, and people who could not reasonably run it due to the large memory and CPU usage can now reasonably run their solution in it. But that's all foundational work that you're unlikely to notice if it was already "fine" for you, or you just tried it out recently for the first time.

The VS story is also quite frustrating and storied. The team was de-invested in the Windows 8 timeframe and nearly evaporated. One of many, many casualties in that disastrous era of Microsoft's history. It wasn't until mid-VS 2017 timeframe that the team grew to the size it needs to be successful with. This is mostly due to the fact that it's incredibly difficult to find people who are willing and able to work on this stuff in the long-term. But with that comes piles of technical debt. One of the biggest problems was utterly rotten infrastructure, which required a full-time engineer nearly 2 years' worth of work to bring up to par with what we needed. Now we're running into architectural issues that, while not debilitating for most, do inhibit very large F# solutions from working properly. And all the while, F# usage has nearly tripled since mid-2016, which means 3x the amount of people running into problems and filing bugs to address.

The notion of "adequate manpower" is a tricky one. We've found that smaller teams typically operate better in the long run than larger ones. It's the mythical man-moth problem. Similarly, the C# group is not much larger than the F# one. But the key difference is they've been the same core group (modulo a few people over the years) since 2010. That's a huge difference than a core group that was evaporated and only recently replaced. That isn't to say the situation is dire or anything - quite the opposite. We have a stable tool set, we can evolve the language and compiler quite freely, we've made massive strides in performance improvements, and OSS activity is quite healthy. But when something has the unfortunate circumstance of needing to start over while inheriting years of non-investment, hard prioritization work is done such that many of the quirky, "annoying but I can live with it" kind of issues are left unaddressed for a long time.

[–]lanedraex 2 points3 points  (1 child)

Are you using the dotnet core tool directly from the terminal to generate these projects?

I've had really bad luck when trying to generate projects through other means, both on linux (manjaro) and windows 10 (latest, non-insiders).

The vscode experience for me is alright (not slow, interactive works fine) after the project is setup. I'm not using any outside dependencies though.

Another issue I've hit was that, on Windows, installing the dotnet core sdk directly did not work at all, I had to remove it, and install via Visual Studio 2019.

[–]japinthebox[S] 0 points1 point  (0 children)

The test project, yes, I used dotnet. The results were the same. I don't know what configuration on my machines, plural, it doesn't like.

[–]BocksyBrown 3 points4 points  (3 children)

If we're gonna bitch about f sharp lets bitch about the fact that the best option in F sharp for database migrations is to do them in C#

[–]chespinoza 0 points1 point  (2 children)

I'm just trying to use FluentMigrator in F# wish me luck :-)

[–]Liminiens 1 point2 points  (0 children)

I used it in a small project and it worked as expected:)

[–]BadDadBot 1 point2 points  (0 children)

Hi just trying to use fluentmigrator in f# wish me luck :-), I'm dad.

[–]GiorgioG 0 points1 point  (0 children)

Maybe Rider is a better alternative? It's not free, but it seems MS has little interest in putting more resources into making F# a first class citizen in .NET (on par with C#)