IntelliFactory

From enterprise to next-generation web: celebrating 11 years with WebSharper

websharper
By Adam Granicz on Monday, December 10, 2018 — 5 comments

From enterprise to next-generation web: celebrating 11 years with WebSharper

(This article is my contribution to F# Advent - a huge thanks to Sergey Tihon for organizing and running this initiative! - and it is the first part of a string of F# + WebAssembly articles lined up for you in coming days and weeks.)

F# web development is teeming with ideas and OSS projects, and it's often hard to figure out what to pay attention to with all the social media noise. With WebSharper being 11 years old soon, I wanted to take this opportunity to briefly outline the historical circumstances around WebSharper (just some notes in no particular order) that position it for a bright future outlook, and give a quick glimple of what we are working on with it to advance the current state of F# web programming. If you'd rather just skip the history part, feel free to skip to the whats-coming part and see a full WebSharper application run on WebAssembly.

Not your typical community project - a historical perspective

We started WebSharper in 2007 at IntelliFactory, a small Budapest-based startup, to enable ourselves to develop ASP.NET applications entirely in F#. We were not only interested in writing code behind files in F# (see the work of Tomas Petricek to implement an F# CodeDOM provider for ASP.NET) , but also client-side code that interacts with the server, transpiled into JavaScript. Unlike in Tomas's F# Web Tools project that came up around the same time, calls crossing tiers were marked by .NET attributes ([<Remote>] vs [<JavaScript>]) and nearly the entire F# language and its standard libraries were supported in the translation. This effort culminated in the first public WebSharper release in 2007, with support for remote and service calls, and the ability to write client-server apps in F# to facilitate full-stack development.

WebSharper's goal has always been removing the need to work with JavaScript and to provide a more robust, type-safe, and functional way to develop web applications - without having to worry about the underlying execution engine. Various WebSharper concepts that were evolved (sitelets, pagelets, formlets, piglets, etc.) all aimed at this single goal and quickly shifted our attention from just writing an F# to JavaScript transpiler to working on fundamental building blocks for web applications. I often refer to this as "WebSharper is 80% about web abstractions and 20% about translating F# to JavaScript" in talks I give at conferences, but it's difficult to appreciate this until you actually work with WebSharper.

Some of these web abstractions that WebSharper pioneered:

  • Sitelets - a server-side abstraction for building microservices and server-sides to web clients.
  • Typed endpoints, safe URLs - unique to WebSharper and part of the sitelet abstraction, these enable representing all entry points to an application's client and/or server-side in the type system.
  • Type-safe remoting - asyncronous client-server communication and message passing, with the ability to supply server-side functionality via multiple server sides, using an attribute-based notation or interfaces to mark/describe server-side functionality.
  • Formlets - declarative user interfaces, also available as reactive formlets built on WebSharper UI. Formlets can be arbitrarily composed and can be used to build highly complex UIs, albeit without much control for render customization.
  • Piglets and reactive Forms - providing the same consice, declarative notation as formlets, but with explicit control of the output structure.
  • WebSharper UI (previously called UI.Next) - an alternative to React and other reactive libraries, UI provides a reactive application foundation with dynamic data flow.

Back around 2007, open source in the .NET community was not widespread, and we quickly found a solid business opportunity to sell WebSharper as a commercial tech, keeping a free/shareware option (under what we coined as WebSharper Professional and Community.) Customers included Fortune 500 organizations and startups alike. In parallel, we also developed commercial WebSharper applications, for instance, we presented the first line-of-business application written entirely in F# (later followed by an entire umbrella of related services and applications) in CUFP 2009, the commercial users workshop organized under ICFP, one of the most prestigious international FP research/academic conferences. These applications were already in the 30-50k LOC range, proving WebSharper to be a worthy, mature, enterprise-ready technology.

As a proprietary code base at the time, WebSharper met with a fair amount of resistance from the early F# community. Misconceptions like "WebSharper is trying to do too much", "WebSharper is hard to understand", and "WebSharper is too monolithic, frameworks are evil" all came from this prejudice. We listened to this criticism. To juggle our ongoing commercial contracts and further developing WebSharper in parallel, we released WebSharper on a dual license model (GNU GPL for open source projects and commercial license for closed source use), which ensured the viability for the time being, but failed to attract indy developers due to the "copyleft" issues associated with GPL. So ultimately, we released the entire code base on Apache 2.0 (under WebSharper 3 in late 2014).

From the start, the WebSharper ecosystem also encompassed various extensions, providing proxies to popular JavaScript libraries so they can be used in F# code directly. There is a resurgence of this now with Fable, binding JS libraries - and it's great to see these match those existing extensions available for WebSharper. Be sure to check out Andrei Degtiarev's community extensions, Mariusz Wasak's PowerBI extensions, and the WebSharper organization on GitHub, containing another 70+ extensions, all of which are also available on NuGet for easy reference in your projects. Most of these extensions use WIG underneath - the WebSharper Interface Generator: an F# DSL to describe and generate proxies. WIG is considerably expressive, catering to all sorts of JavaScript weirdness, yet WIG definitions remain short and easy to maintain. We also developed a TypeScript bridge to create WebSharper extensions from TypeScript declaration files, and enabled WebSharper to output the same type of declarations for WebSharper libraries. These tools and some larger extensions have been made available in the WebSharper Insider Program, and will likely be open source at some not so distant point similar to the 70+ of their counterparts already on GitHub.

All in all, WebSharper sits in a slightly unusal corner of open source community projects: with an F# consulting company driving it and supporting it commercially for over 10 years, it caters to significantly more advanced scenarios that one usually encounters in larger, enterprise applications. It also represents a highly contraversial stance that seeks to minimize the use of JavaScript. This often paints a false image: due to our team constantly being busy and overlooking the importance of remaining active on social media (most notably the F# Slack channel, Twitter, Gitter, etc. - sorry, we are working on that...), and our enterprise devs getting support through our dedicated support channels instead of the public WebSharper forums (start with the "Request support" button on the top right corner on forum tickets), it's easy to assume you should be looking at other options. The constant pressure to infiltrate F# with JavaScript artifacts (proposals like adding npm dependencies to Paket, or adding features to the language that facilitate JavaScript translation, etc.) to widen F#'s appeal to include millions of JavaScript developers also doesn't help. It's time to realize that JavaScript might just soon be much less important.

While I urge everyone to always explore all options to formulate an educated opinion (after all, anything that raises awareness to any F# web development tool helps WebSharper become more visible as well), do consider that popular topics nowadays with other F# web tech (Giraffe, Fable, SAFE, Saturn, etc.), such as creating microservices, calling remote functions, server and client-side debugging, source maps, composable and reactive UI forms, IDE integration and various project templates (just have a look at the last few editions of F# Weekly) have been available for years in the WebSharper ecosystem. Instead, you will find us most active with multi-server RPCs; reactive, native and highly performant UI abstractions; advanced templating for client-server apps and SPAs; client-server reactive models; enhanced performance with web workers; shared, type-safe routing on the client and the server; and other not-so-obvious themes. But all in all, WebAssembly sits very high on our list and we are fully committed to it.

What about JavaScript?

WebSharper has always taken a low-ceremony stance with respect to generating JavaScript: it's done in a "translate-it-and-forget-about-it" fashion. The main objective here is to provide a semantically correct translation, and not pretty, F#-looking code that you will spend time hand-editing. In most cases, developers will never have to look at the generated code and can simply assume things work as expected, and for the demanding cases (that involve a heavy use of external JS libraries, etc.) employ source maps to help with debugging.

WebSharper can emit minimized or verbose code (driven by a configuration setting or compiler option), and dead-code eliminated single-file includes for SPAs, among others. The generated JS code can be consumed from other JavaScript or TypeScript projects easily, and is also embedded into the target .NET assemblies to avoid recomputation in dependent projects. In addition, sitelet-based apps (client-server and HTML applications) perform resource tracking for each page served, outputting the minimal set of external references/dependencies for the generated JS functionality - both in the sitelet serving runtime and in statically generated pages. This is incredibly useful with reusable components that depend on each other or require JS libs to function (extensions, for instance.)

These features ensure that your WebSharper applications scale properly as their complexity increases. Some of the largest WebSharper apps we worked on included 25-30 library projects, compiled incrementally on demand.

You can therefore safely conclude that working with WebSharper will drastically reduce your exposure to JavaScript, often to nothing. Like I summarized earlier, this is one of the fundamental goals of WebSharper. This also applies to the typical development setup: while you can use JavaScript tools to bring dependencies into your project via npm and friends, the correct approach is to let this happen automatically by using proper WebSharper extensions that declare and embed/reference their dependencies.

That said, we made a conscious effort to avoid external dependencies for the core WebSharper libraries. jQuery was one such dependency initially, used for dynamic DOM manipulation, but it was later removed in the 4.x releases. The main reactive UI core library (sources on GitHub), for instance, that powers most of the reactive WebSharper applications, implements the foundation for reactive web applications entirely in F# without any JavaScript dependencies (no React.js, etc.), and is then bootstrapped into all generated client-side code in consuming applications. This has the benefit of the potential for further dead-code elimination, fewer external dependencies, and above all, a fully .NET-based reactive UI machinery/application backbone.

Teaser: Bolero - taking WebSharper and F# to WebAssembly

Armed with the above, one of the upcoming things we have been working on is to enable adapting WebSharper applications to run on WebAssembly via Blazor. This is a project we codenamed Bolero that we will be announcing next.

WASM is a portable assembly representation that can be efficiently executed in all major web browsers, giving an alternative to JavaScript to run web applications. The impact here is tremendeous as WebAssembly can eventually be made to run on any device, giving a universal assembly representation for any programming language compiler. While there are still some rough edges, this concept has already been applied to a wide range of compilers, giving WASM compilation for Rust, Go, C, C++, and many others. Furthermore, WASM is expected to significantly reduce the use of JavaScript in future web applications, even though at this time it is only meant to provide extra safety and performance and work side-by-side with JavaScript (obviously).

Blazor has a similarly ambitious goal: it aims to run any .NET code on WebAssembly by implementing/compiling a .NET runtime in WASM. This will ultimately enable running .NET applications anywhere where WASM can run. Blazor also aims to implement an IL to WASM path, allowing all .NET languages to compile to WASM directly.

Built on top of these, Bolero enables you to use WebSharper's key features/abstractions to develop Blazor applications. In its ultimate form, it will allow to adapt most WebSharper applications with minimal changes to run on WebAssembly via Blazor. In the first cut we are shipping next, sitelet endpoint types, a slightly modified version of UI's HTML templating type provider as an alternative to Razor, and RPC client-server communication is enabled. Bolero also integrates the Elm Architecture natively via Elmish, making it easy to develop Model-View-Update (MVU) apps that you may have seen with Fable+React, but this time without the JS dependencies and running entirely in WASM.

Consider the TodoMVC challenge:

TodoMVC with WebSharper

There are several WebSharper (F# to JavaScript) implementations for this app. The older WebSharper.UI-based one showcases using UI's list models, client side routing, and templating features. I have an alternative, more recent implementation that uses UI's reactive composite models along with the use of lenses and the V notation, feel free to reach out to get a glimple (I should have blogged this ages ago...) Then there is an WebSharper.MVU-based implementation, that very much resembles the Fable+React implementation, except it uses a UI-specific render function that operates on read-only views of the underlying composite model instead of the model value itself, and doesn't depend on React.js as noted above. It is also considerably shorter due to the use of lenses, UI's V notation and templating.

The matching Bolero implementation (F# to Blazor/WASM) is almost identical, except that the render function here uses model values as in Elmish, exactly like in the Fable+React version mentioned above. Minor changes were also applied in the app's HTML template to accommodate the change in attributes that the Bolero templating type provider works with, to be more in line with Blazor's data binding notation.

You can see the app running in WASM live. Note the short delay initially as you load the page: this is the time it takes to initialize the Blazor runtime in WASM and download the required .NET framework assemblies.

We are very excited about moving more WebSharper features under the Bolero umbrella, providing an a solid framework to adapt WebSharper applications to run fully in WASM. But even in its current form, the key basic WebSharper features and Elmish/MVU integration are available, and you can develop Blazor SPAs and client-server applications entirely in F# in much of the same way as you are used to with WebSharper.

And one last bit: you may have also noticed in the commits in the past 12 months to the core WebSharper repo, that we have been working on enabling WebSharper to produce TypeScript output as well. Here, we considered tailoring this output to play well with technologies that can translate a subset of TypeScript to WASM. But overall, this approach would be inferior to what Bolero already offers.

Final words

11 years is a very long time for any project. WebSharper might just be the oldest F# tech around with a tremendeous track record and impact (at the time of writing, there are 147 NuGet packages for WebSharper) . This would not have been possible without a great deal of perseverance, hard work, and overcoming a great number of challenges.

I would like to thank our WebSharper users, customers, and contributors who continue to appreciate the kind of brevity, abstraction, and productivity that WebSharper brings to web development, our IntelliFactory WebSharper team members, who over the years have worked on WebSharper and its tooling, in no particular order: Loic Denuziere, Andras Janko, Jozsef Uri, Anton Tayanovskyy, Joel Bjornson, Simon Fowler, Ernesto Rodriguez, Diego Echeverri, Istvan Gansperger, Sandor Rakonczai, Sandor Szaloki, Adam Abonyi-Toth, Gergely Fabian, Ramon Snir, Maciej Lopatka, Gyula Kiss, Csaba Hruska, Joel Huang, and our other F# developers, interns, contractors, and staff for the engaging technical and strategic discussions.

I would also like to thank the multitude of people in the community who have contributed to applying F# in web programming. It would be impossible to list everyone, but a very special thanks goes to Dustin Moris Gorski, Ryan Riley, Andrew Cherry, Alfonso Garcia-Caro, ncave, Zaid Ajaj, Krzysztof Cieslak, Maxime Mangel, Eugene Tolmachev, Steffen Forkmann, Henrik Feldt, Robert Pickering, Justin Greene, Zach Bray, Vladimir Matveev, Don Syme, Tomas Petricek, the past and current Microsoft F# team, Colin Gravill, Catalin Bocirnea, Kimserey Lam, Youenn Bouglouan, Taha Hachana, Flechner Romain, Jonathan Nolis, Mariusz Wasak, Andrei Degtiarev, Abelardo Mieres, among many-many others for running inspiring initiatives and building F# web communities. You guys rock!

And last, but not least, I would like to thank Art Scott for making me curious about WebAssembly back in 2015, ahead of everyone else in the community, whose clear vision was spot on, and the Blazor team for bringing .NET to WebAssembly.

Happy coding and Merry Christmas soon!