Page 30 - MSDN Magazine, October 2019
P. 30
The difference is that where C# has language features that encourage a style of program- ming that leads to stateful, object-oriented, imperative and statement-based programming, F# leads to writing stateless, func- tion-oriented, declarative and expression-based applications.
So while you can implement the exact same types of applications in both languages, such as line-of- business Web applications that use a SQL Server database, or highly scalable back-end services hosted in Azure, the way you “join the dots” and orchestrate calls and errors to .NET methods, NuGet packages and external services is different.
Figure 1 The Fable REPL
Indeed, C# already has some FP features, primarily those introduced in C#3 that revolve around LINQ—and that’s just the beginning. There’s a good reason FP-esque features are cropping up in many languages that have OO roots, because they lead to fewer bugs and easier-to-read code. So, if you enjoy using tuples, switch expressions and non-nullability, F# will almost certainly be a better fit—they’re the “default” way of writing code.
Enough about F#. Let's start by looking at the SAFE stack from a programming model point of view, looking at the three core libraries.
Saturn: Functional Programming on the Server
As you probably know, especially in today’s world of distributed applications, microservices and containers, writing stateless Web applications often leads to simpler, more scalable systems. In fact, if you think about Web programming fundamentals, you’ll see that the entire HTTP model is actually akin to a simple stateless func- tion definition: an input over HTTP—such as a GET request to a specific URI—which returns an HTTP response code and some payload—for example, 200 and some JSON data; there’s nothing intrinsically OO about it.
Of course, when you look at ASP.NET, you often think of things like controller classes, dependency injection and so on, but ulti- mately those are just layers wrapped on top of this simple model. Saturn takes a different approach to a standard ASP.NET appli- cation by creating a lightweight programming paradigm that allows you to directly tie HTTP endpoints into functions that return data—in other words, matching the HTTP input/output model.
Here’s a look at the “Hello, world!” for Saturn. I can fit the entire pro- gram into a single code snippet because Saturn wraps all the ASP.NET boilerplate for you and lets you focus on routing and business logic:
let myRoutes = router \{
get "/api/foo" (text "Hello, world!")
\}
let app = application \{ use_router myRoutes
\}
run app
As you can see, Saturn hooks up GET requests to the “/api/foo” endpointtoacodeblockthatsimplyreturnsthetext,“Hello,world!”. The nice thing about Saturn is that while it doesn’t force you to get involved with the whole ASP.NET pipeline, it doesn’t stop you from doing it, either. If fact, you can easily access the ASP.NET context object and seamlessly make use of all of the ASP.NET Core-compatible NuGet packages.
Saturn makes use of some F# smarts to create a succinct DSL for writing and composing routes such as get and post functions that map to the equivalent HTTP verbs, and uses simple helper functions like text, json and xml to quickly create results that set the appropriate values on the HTTP response.
Here’s a sample that illustrates how simple routing can be using several additional powerful features of Saturn and F#:
let loadOrderDetails (customerId:int) = \{| OrderCount = 10
CustomerName = "Fred Smith" |\} // loading real data elided...
let orderRoutes = router \{
getf "/customer/%i" (loadOrderDetails >> json) put "/customer" saveOrderDetails // etc.
\}
let topLevelRoutes = router \{
get "/api/foo" (text "Hello, world!") forward "/api/orders" orderRoutes
\}
Here are some of the points to note from this code snippet:
Route Composition: orderRoutes is “composed” into topLevel- Routes using the forward keyword, so that all routes in orderRoutes are automatically prefixed with /api/orders.
Type safe parameterization: Using F# string placeholder support, %i indicates a route should supply a number, such as api/orders/customer/10, to load orders for customer 10.
Seamless function composition: Using the >> (compose) operator, I “plug together” the loadOrderDetails and json func- tions to return a record with the order count and customer name, before converting it into JSON.
When you combine Saturn’s more powerful features, such as controllers, with the powerful F# type system and excellent data
26 msdn magazine
F#