Yeah, I blame it on version 5 (and dotnet) and the overall tooling improvements. It has really become an excellent and easy to pick up language, with not too many warts and a very good ecosystem.
Ha, I'd be very interested in seeing a sociology study on Java. There's nothing about the language itself says you need to seize on half a dozen design patterns and beat them into the ground, but that's what every Java project I've ever seen looks like.
I'm also a fairly recent migrant from .NET to Java.
My sense is that the root cause of a lot of it is missing features in the Java language itself. There are a lot of cases where in C# I'd have used an extension method, or been able to lean more heavily on generic programming or Expression<T>, but, since Java has no good equivalents of these things, instead I have to break out a design pattern to accomplish the same task.
And, once you've cracked that seal, it's sort of an, "In for a penny, in for a pound," type situation. Next thing I know, I'm neck deep in it, and I don't even know how I got there.
While I did just criticize Java for lacking some features, I genuinely don't agree with that Norvig quote.
It's true that, in a single dispatch language, I sometimes need to use the visitor pattern. And the visitor pattern is a little bit annoying to implement, it's true. But the implication of that Norvig quote is that every single language should implement multiple dispatch, and I simply cannot agree with that. And I suspect that a lot of people who like to invoke that sentiment would not prove to have the courage of their convictions when presented with a situation where Haskell needs a design pattern (sorry, "idiom") in order to work around the fact that it doesn't have dynamic dispatch at all, multiple or otherwise.
In general, it's OK for languages to be small and focused, or even to be large and multiparadigm but still draw the line at adding some features. That's not my criticism of Java. My criticism of Java is more along the lines of, Java's a kitchen sink language, and there's nothing wrong (aside from perhaps aesthetics) with being a kitchen sink language, but there are other kitchen sink languages that did a better job of cramming more random crap in more cleanly.
Tooling for sure. I would be shocked in the marketing budget increased just based on how many recent .NET changes recently exclude F# (e.g. source generators).
I have yet to learn it myself, but I get the idea that there is a huge amount of people who "shut up and get shit done" with it. Posts on HN tend to bring these people to the surface.
The interest has always been consistent, it's just not vocal.
There was a wave of popularity in 2017 as well. I used to work on it full time back then, and enjoyed it a lot. The SQLProvider [0] and other type providers like it are super impressive!
I've done a few little projects with the SQL provider. I worry about maintainability. You can't compile if the schema changes without you knowing about it.
I haven't run into any real problems as a result but it does make me nervous.
> You can't compile if the schema changes without you knowing about it.
There are those who would argue that making an incompatibility between the database schema and the code that interacts with it a compile-time error instead of a run-time error is a huge win for software quality.
I suppose there are situations where you simply can't have access to a database that mimics the structure of the production database during development. In which case, the SQL provider is definitely not the right tool for the job. But it might be worth looking into ways to change how you do things in order to make it the right tool for the job. A SQL database with an unpredictable schema makes me much more nervous.
> You can't compile if the schema changes without you knowing about it
That's why you don't normally compile against the production database. You version control the database schema and use it to create a local database to compile against. This way you can also test potential future migrations using branches.
Compiling against the production database should be done separately, as a kind of free integration test (usually in CI).
Maybe a combination of churn from Scala, growing popularity of .NET in general with .NET Core, lack of high-level stuff in Go, static typing coming to dynamic languages (especially JS), functional features getting everywhere. Lots of stuff is pointing at it in a way.
I’ve only played around with it on a few smaller projects, but it blew me away with the simplicity and expressiveness. I’ve used it a few times to make simple web based UIs for personal tooling and am looking forward to using it on something bigger.
Just one of the many reasons I find myself more and more excited by F#.
It's inspired heavily by Svelte itself, and borrows the Feliz DSL for building DOM. Feliz itself is a layer on top of React, Sutil is ground-up raw DOM building.
I like React and I like F#. But I never understand this kind of marketing. It's not that I want things optimized for pain. But I guess I'm just most interested in the technical choices and actual performance in the domain over my own happiness?
I mean that the marketing around "happiness" doesn't directly address any concerns that developers should have when evaluating a library. At best it's indirect if you assume that happiness means performance, but that's a big assumption.
So I'd rather marketing just say exactly what it's direct goals are rather than indirect ones like happiness.
> Since the F# transpiles into JS at runtime it's really no different than, say, TS in terms of performance concerns.
But as a particular point, this is hard to believe. TS tries to have no (or a minimal) runtime component whereas F# definitely must have a major runtime component.
I don't know a ton about Fable, but they recently wrapped Google's Lit to allow building functional templating and web components in it: https://fable.io/Fable.Lit/
It doesn't try to hide the fact that when you write a Fable program, you're fundamentally in the JS ecosystem. This can be a bit of a shift if you're used to doing stuff in .NET, but it means that everything will more or less work with your code.
It also comes with a few tools/utilities to ease the F#-JS bridge you're on.
I've never dealt much with transpiling to JS, but I would guess that it's a lot easier to do it from the AST or some other higher-level intermediate representation than it is from CIL instructions. That way you don't have to fuss with programmatically figuring out what stack manipulation instructions are trying to accomplish or any other target-specific noise like that.
Not necessarily. Aside from being more technically challenging, it's more of a "what are our goals" kind of question. Do you want to give F# developers who are afraid of javascript a way to avoid it altogether, or a way to bring those developers into the javascript ecosystem?
In practice you occasionally find something that doesn't quite work, for the most part things related to IO and the TPL but I occasionally run into some more niche functionality with Date & time for example.
You can use most of the core F# library and some non fable centric libraries (e.g. FSharp.UMX of FSToolkit.ErrorHandling) work just fine.
I find it mostly a non issue, as any IO you want to go through the browser API instead or it's not supported. It didn't take take long at all to easily slip into the "I'm in the browser now" mindset.
Maybe I'm biased, but I'm often skeptical of the benefits of projects like this that translate a language which normally targets runtime A, into another language that targets a wildly different runtime B (usually JS or WASM)
(Disclaimer that I'm not talking about compiling runtime-less languages for different platforms)
It just seems like it's going to be a very leaky abstraction, like there will be tons of corner cases where some behavior isn't exactly the same and other behavior isn't quite possible, which will cause libraries and in-house code alike to break in weird ways. I know that ClojureScript, for example, has several caveats relative to Clojure; and most of these translators are not nearly as mainstream (and so don't get nearly as much maintenance attention) as ClojureScript does. And then there's the messy question of interacting with the host system, dropping down into the host language for certain things, etc.
I haven't really worked with these kinds of systems so I may be way off, but from the outside it seems like a huge increase in complexity just so you get to stay in a familiar language
I'm only passingly familiar myself, so I might be off here-- but F# already has that sort of feel and experience, where it has some of its own ways of doing things despite integrating into the .NET runtime.
In other words, the language designers and users are already used to understanding corner cases and integrations into ecosystems that are different.
There are some caveats they outline for the usual async stuff[1], but at a cursory glance, it's not that bad. Thanks to F#'s computation expressions, .NET async code and Fable.Promise code look almost identical.[2]
My suggestion is to use async in F# code by default. Use task for certain heavy server code on .NET and promise for JS interaction, where required. Async mostly works everywhere.
Yeah, I'm just trying to avoid having to throw the adapters on each line. Just like fable supports any thenable, it'd be cool if async/task could be treated as a thenable.
F# is certainly a .NET language depending on the lens with which you view it. The object system and lots of its semantics are the way they are because that's how .NET works. However, a surprisingly large amount of this stuff is translatable and makes sense with a JS runtime instead. The fable authors have managed to thread the needle just right so that it more or less "feels right".
I've written quite a lot of F# code for the web over the last few years. Most of the time things just work as long as you don't use overly specific .NET BCL stuff.
We had issues with Dates. Implementing a custom 'ZonedDateTime' type with native implementations for both target platforms (JS and .NET) was straight forward.
Other than that there are a few gotchas when doing 'advanced' stuff:
- Fable does not support 'Array2D', 'Array3D', ...
- Fable allows the use of ints but you have to know that at runtime they all are floats.
- Fable does not support FSharp Quotations.
- Fable does not support the full reflection API.
ClojureScript is in a weird boat because of a constant background tension in goals of the Clojure community, where on the one hand Clojure proclaims that it is a "hosted language" and as such there shouldn't be an expectation of consistent semantics across different hosts, but on the other hand devs really like consistent semantics if the language doesn't change. So the Clojure community has kind of settled on cljc files as an indication of "works on all hosts in the way you would expect" even though that's not any part of the guarantee that a cljc file provides in any documentation anywhere (it gives you reader conditionals and that's it). Basically ClojureScript isn't really sure whether those "caveats" are bugs or features and neither is the Clojure community at large.
More broadly they aren't sure whether causing "libraries and in-house code alike to break in weird ways" on different hosts is a bad thing (some folks would probably take issue with the word "break").
On the whole however these projects can go very well. I've seen e.g. ScalaJS and Scala backend projects work very well. They're a lot less leaky than you would think. The vast majority of code just works. In particular, there are clean lines between what is supported by the abstraction and what isn't, and the latter doesn't affect the former, so in that sense the abstraction is pretty tight and not leaky. And the major win isn't so much the familiar language part as it is the ability to share code and definitions. It's really powerful to change a line somewhere deep in your backend and have all the places you need to make a change all the way out to the frontend UI automatically laid out for you.
That said the big misconception, and where the abstraction is incomplete if not leaky, is that these languages allow you to disregard the runtimes of secondary targets. No compile-to-JS language I know of has successfully done away with the need to understand the HTML/CSS/Javascript stack. And that indeed is extra complexity that should give pause to developers.
But RE "cause libraries and in-house code alike to break in weird ways" generally does not seem to be the case for these compile-to-JS languages.
The reason I'm bullish on Fable (and Feliz) is that in my experience the challenge in writing a modern web application is dealing with state, especially when API calls are involved. With Fable I save a lot of time (and bugs!) in that, so I'm okay with spending more time integrating with javascript libraries.
In my experience you can achieve the same functionalities with a lot less complexity in Fable + Feliz than the equivalent typescript stack.
Story on Wiz is since I’m a growth engineer I tend to think about usage on a more psychological level (optimizing for reduced friction and simpler onboarding). I found that most libs in F# have the .net APIs right in your face so users need to learn both. (E.g. I would define route handlers in the http library but if I want to change the server port I have to look at dotnet docs.)
If F# is to be successful, I think more attention needs to be made to build abstractions on .net APIs.
yeah wiz is still a work in progress. Its mainly just trying to be a simpler API wrapper for asp.net
Would challenge the notion of immutability or extensibility being a primary motive for using a library. Would like to see UX (DX more specifically) start to take more of a front seat in F#, since its one of the more neglected parameters of success in the ecosystem. Extensibility can be added, UX is more difficult to retroactively account for.
If Wiz only influences other libraries to become simpler for new users then I will consider it a success.
I just want more eng teams to use F# or Ocaml without having to justify why you aren't using python/node.js.
These things are bizarre when you think about the tech stack they lead to, but undeniably still really useful in our unfortunate real world. I think of F# as 'the pragmatist's functional programming language', and projects like this follow that mindset.
Debating F# for OSS project I'm about to start but the asp.net C# with blazor story(though I need support for loading blazor code at runtime for plugins and can't get a straight answer on that) is very compelling.
As to Fable.. I would want to use it on Vuejs perhaps if I can't do blazor, but also with something like mobx and NOT Elmish. I despise redux and it just looks gross. I want the Mobx "magic" for this boring view update stuff.
How close are the semantics to F#? For example OCaml has bucklescript which produces reasonable looking JavaScript but doesn’t always follow the language semantics, and js_of_ocaml which produces horrid JavaScript (it compiles from 32-bit OCaml bytecode to js, and fakes the OCaml memory layout of objects by using arrays with their first element being the tag of the object, so e.g. a float is represented in js as [<magic number>, <the actual float>]).
Similarly, Common Lisp has something called parenscript which compiles a lispy syntax to js but e.g. uses JavaScript arrays for its lists (so you don’t have a cons function).
Fable produces reasonable JavaScript and makes some semantic concessions towards JS. For example, ints are compiled to JS floats. You still get full compile-time type checking.
For shared code it can be wise to maintain tests that can run on both .NET and Node.js.
74 comments
[ 3.2 ms ] story [ 156 ms ] threadMy sense is that the root cause of a lot of it is missing features in the Java language itself. There are a lot of cases where in C# I'd have used an extension method, or been able to lean more heavily on generic programming or Expression<T>, but, since Java has no good equivalents of these things, instead I have to break out a design pattern to accomplish the same task.
And, once you've cracked that seal, it's sort of an, "In for a penny, in for a pound," type situation. Next thing I know, I'm neck deep in it, and I don't even know how I got there.
It's true that, in a single dispatch language, I sometimes need to use the visitor pattern. And the visitor pattern is a little bit annoying to implement, it's true. But the implication of that Norvig quote is that every single language should implement multiple dispatch, and I simply cannot agree with that. And I suspect that a lot of people who like to invoke that sentiment would not prove to have the courage of their convictions when presented with a situation where Haskell needs a design pattern (sorry, "idiom") in order to work around the fact that it doesn't have dynamic dispatch at all, multiple or otherwise.
In general, it's OK for languages to be small and focused, or even to be large and multiparadigm but still draw the line at adding some features. That's not my criticism of Java. My criticism of Java is more along the lines of, Java's a kitchen sink language, and there's nothing wrong (aside from perhaps aesthetics) with being a kitchen sink language, but there are other kitchen sink languages that did a better job of cramming more random crap in more cleanly.
The interest has always been consistent, it's just not vocal.
Looks like somewhat of an increase but a very spiky baseline.
https://hn-trends.eliot-jones.com/Home/Trend?id=f%23&allword...
[0] https://fsprojects.github.io/SQLProvider/
I haven't run into any real problems as a result but it does make me nervous.
There are those who would argue that making an incompatibility between the database schema and the code that interacts with it a compile-time error instead of a run-time error is a huge win for software quality.
I suppose there are situations where you simply can't have access to a database that mimics the structure of the production database during development. In which case, the SQL provider is definitely not the right tool for the job. But it might be worth looking into ways to change how you do things in order to make it the right tool for the job. A SQL database with an unpredictable schema makes me much more nervous.
That's why you don't normally compile against the production database. You version control the database schema and use it to create a local database to compile against. This way you can also test potential future migrations using branches.
Compiling against the production database should be done separately, as a kind of free integration test (usually in CI).
Just one of the many reasons I find myself more and more excited by F#.
https://zaid-ajaj.github.io/Feliz/
https://github.com/Zaid-Ajaj/Feliz
https://github.com/davedawkins/Sutil
I like React and I like F#. But I never understand this kind of marketing. It's not that I want things optimized for pain. But I guess I'm just most interested in the technical choices and actual performance in the domain over my own happiness?
Unless you meant something else there?
So I'd rather marketing just say exactly what it's direct goals are rather than indirect ones like happiness.
> Since the F# transpiles into JS at runtime it's really no different than, say, TS in terms of performance concerns.
But as a particular point, this is hard to believe. TS tries to have no (or a minimal) runtime component whereas F# definitely must have a major runtime component.
Besides his numerous repos, the Elmish book [1] is really awesome.
[1] https://zaid-ajaj.github.io/the-elmish-book/#/
Seems like a neat project.
It doesn't try to hide the fact that when you write a Fable program, you're fundamentally in the JS ecosystem. This can be a bit of a shift if you're used to doing stuff in .NET, but it means that everything will more or less work with your code.
It also comes with a few tools/utilities to ease the F#-JS bridge you're on.
So you can call any .NET library?
In practice you occasionally find something that doesn't quite work, for the most part things related to IO and the TPL but I occasionally run into some more niche functionality with Date & time for example.
You can use most of the core F# library and some non fable centric libraries (e.g. FSharp.UMX of FSToolkit.ErrorHandling) work just fine.
I find it mostly a non issue, as any IO you want to go through the browser API instead or it's not supported. It didn't take take long at all to easily slip into the "I'm in the browser now" mindset.
(Disclaimer that I'm not talking about compiling runtime-less languages for different platforms)
It just seems like it's going to be a very leaky abstraction, like there will be tons of corner cases where some behavior isn't exactly the same and other behavior isn't quite possible, which will cause libraries and in-house code alike to break in weird ways. I know that ClojureScript, for example, has several caveats relative to Clojure; and most of these translators are not nearly as mainstream (and so don't get nearly as much maintenance attention) as ClojureScript does. And then there's the messy question of interacting with the host system, dropping down into the host language for certain things, etc.
I haven't really worked with these kinds of systems so I may be way off, but from the outside it seems like a huge increase in complexity just so you get to stay in a familiar language
In other words, the language designers and users are already used to understanding corner cases and integrations into ecosystems that are different.
Still- I have to wonder about things like concurrency, which is perhaps the most obvious limitation of targeting the JS runtime
1: https://fable.io/docs/dotnet/compatibility.html#Caveats-II
2: https://stackoverflow.com/a/54451205/4099287
There’s a function that Fable provides to turn a promise into an async, so mix and match isn’t a huge challenge anyway.
- F# has some significant advantages over JavaScript (and TypeScript).
We had issues with Dates. Implementing a custom 'ZonedDateTime' type with native implementations for both target platforms (JS and .NET) was straight forward.
Other than that there are a few gotchas when doing 'advanced' stuff:
- Fable does not support 'Array2D', 'Array3D', ... - Fable allows the use of ints but you have to know that at runtime they all are floats. - Fable does not support FSharp Quotations. - Fable does not support the full reflection API.
More here: https://fable.io/docs/dotnet/compatibility.html
Would definitely recommend F# for both backend and frontend. It's been a joy.
More broadly they aren't sure whether causing "libraries and in-house code alike to break in weird ways" on different hosts is a bad thing (some folks would probably take issue with the word "break").
On the whole however these projects can go very well. I've seen e.g. ScalaJS and Scala backend projects work very well. They're a lot less leaky than you would think. The vast majority of code just works. In particular, there are clean lines between what is supported by the abstraction and what isn't, and the latter doesn't affect the former, so in that sense the abstraction is pretty tight and not leaky. And the major win isn't so much the familiar language part as it is the ability to share code and definitions. It's really powerful to change a line somewhere deep in your backend and have all the places you need to make a change all the way out to the frontend UI automatically laid out for you.
That said the big misconception, and where the abstraction is incomplete if not leaky, is that these languages allow you to disregard the runtimes of secondary targets. No compile-to-JS language I know of has successfully done away with the need to understand the HTML/CSS/Javascript stack. And that indeed is extra complexity that should give pause to developers.
But RE "cause libraries and in-house code alike to break in weird ways" generally does not seem to be the case for these compile-to-JS languages.
In my experience you can achieve the same functionalities with a lot less complexity in Fable + Feliz than the equivalent typescript stack.
If F# is to be successful, I think more attention needs to be made to build abstractions on .net APIs.
If for whatever reason Suave is not fast enough, Giraffe can be fine tuned. However, it also exposes much of the underlying ASP.NET stuff.
I love the website for Wiz though!
Would challenge the notion of immutability or extensibility being a primary motive for using a library. Would like to see UX (DX more specifically) start to take more of a front seat in F#, since its one of the more neglected parameters of success in the ecosystem. Extensibility can be added, UX is more difficult to retroactively account for.
If Wiz only influences other libraries to become simpler for new users then I will consider it a success.
I just want more eng teams to use F# or Ocaml without having to justify why you aren't using python/node.js.
However, if you translated the Wiz examples to Suave, would they be any more difficult to read?
I think the biggest issue is Suave’s docs etc.
These things are bizarre when you think about the tech stack they lead to, but undeniably still really useful in our unfortunate real world. I think of F# as 'the pragmatist's functional programming language', and projects like this follow that mindset.
[1] https://babeljs.io/
As to Fable.. I would want to use it on Vuejs perhaps if I can't do blazor, but also with something like mobx and NOT Elmish. I despise redux and it just looks gross. I want the Mobx "magic" for this boring view update stuff.
Similarly, Common Lisp has something called parenscript which compiles a lispy syntax to js but e.g. uses JavaScript arrays for its lists (so you don’t have a cons function).
For shared code it can be wise to maintain tests that can run on both .NET and Node.js.