Checking the state of the system and performing a requested action only if certain conditions met, is an indispensable requirement in most of the software we write. Though it is a straightforward thing to do, things will get complex/messier when we need to check multiple conditions.

Recently I encountered such a situation while developing a sample application for my fsharp book. Initially, I solved it with a typical nested if else and then I improved it by applying fsharp’s Partial Active Patterns.

In this blog post , I will be sharing what exactly I did and how it can help you to write an elegant code in fsharp.

Business Domain

Let’s start with a description of the problem domain that we are going to solve.

When an individual visits a restaurant, he can place an order, specifying the list of foods he wants. The foods he ordered will be prepared by the chef and then it will be served.

Preparing a food should satisfy the following conditions

  • The Food should be a part of the order
  • It should not be prepared already
  • It should not be served already

Serving a food should satisfy the following conditions

  • The Food should be a part of the order
  • It should be prepared already
  • It should not be served already

If serving a food completes the order, we should tell that the order is served otherwise we should tell that there are some other foods to be served.

Starting with the types

Let’s start with the types that are needed to solve our problem. If you would like to know why we need to start with the types do check out this wonderful blog post by Tomas Petricek.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Food = {
  MenuNumber : int
  Name : string
}

type Order = {
  Foods : Food list
}

type InProgressOrder = {
  PlacedOrder : Order
  PreparedFoods : Food list
  ServedFoods : Food list
}

The names of the types and its properties clearly describe what they represent, so let’s get to the crux of the problem straight

Prepare Food

1
2
3
4
5
6
7
8
9
10
11
let prepareFood food ipo =
  if List.contains food ipo.PlacedOrder.Foods  then
    if not (List.contains food ipo.PreparedFoods) then
      if not (List.contains food ipo.ServedFoods) then
          printfn "Prepare Food"
      else
        printfn "Can not prepare already served food"
    else
      printfn "Can not prepare already prepared food"
  else
    printfn "Can not prepare non ordered food"

The if else conditions represent the criteria mentioned in the business domain and for the sake of simplicity we are just writing the action in the console.

Serve Food

Let’s move on to serving food

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let isServingFoodCompletesOrder food ipo =
  let nonServedFoods =
    List.except ipo.ServedFoods ipo.PlacedOrder.Foods
  nonServedFoods = [food]

let serveFood food ipo =
  if List.contains food ipo.PlacedOrder.Foods  then
    if List.contains food ipo.PreparedFoods then
      if not (List.contains food ipo.ServedFoods) then
        if isServingFoodCompletesOrder food ipo then
          printfn "Order Served"
        else
          printfn "Still some food(s) to serve"
      else
        printfn "Can not serve already served food"
    else
      printfn "Can not serve unprepared food"
  else
    printfn "Can not serve non ordered food"

This is called Arrow Anti Pattern and it is obvious that this code is hard to understand and maintain.

It can be improved by using the techniques mentioned in this StackExchange’s answers and also by using the specification pattern from the OO World.

Though the specification pattern solves the problem, it has a lot of code! No offense here but it can be done in a better way.

F# Active Patterns

Active Patterns allow programmers to wrap arbitrary values in a union-like data structure for easy pattern matching. For example, its possible wrap objects with an active pattern, so that you can use objects in pattern matching as easily as any other union type. - F# Programming Wiki

Let’s begin by defining a partial active pattern for NonOrderedFood and UnPreparedFood

1
2
3
4
5
6
7
8
9
let (|NonOrderedFood|_|) order food =
  match List.contains food order.Foods with
  | false -> Some food
  | true -> None

let (|UnPreparedFood|_|) ipo food =
  match List.contains food ipo.PreparedFoods with
  | false -> Some food
  | true -> None

Then for AlreadyPreparedFood and AlreadyServedFood

1
2
3
4
5
6
7
8
9
let (|AlreadyPreparedFood|_|) ipo food =
  match List.contains food ipo.PreparedFoods with
  | true -> Some food
  | false -> None

let (|AlreadyServedFood|_|) ipo food =
  match List.contains food ipo.ServedFoods with
  | true -> Some food
  | false -> None

Finally,

1
2
3
4
5
6
let (|CompletesOrder|_|) ipo food =
  let nonServedFoods =
    List.except ipo.ServedFoods ipo.PlacedOrder.Foods
  match nonServedFoods = [food] with
  | true -> Some food
  | false -> None

With this in place we can rewrite serveFood function as

1
2
3
4
5
6
7
8
9
10
11
let serveFood food ipo =
  match food with
  | NonOrderedFood ipo.PlacedOrder _ ->
    printfn "Can not serve non ordered food"
  | UnPreparedFood ipo _ ->
    printfn "Can not serve unprepared food"
  | AlreadyServedFood ipo _ ->
    printfn "Can not serve already served food"
  | CompletesOrder ipo _ ->
    printfn "Order Served"
  | _ -> printfn "Still some food(s) to serve"

The goal is to have code that scrolls vertically a lot… but not so much horizontally. - Jeff Atwood

Now the code expressing the business logic more clearly

To refactor prepareFood to use the parial active patterns we need one more. So, let’s define it

1
2
3
4
let (|PreparedFood|_|) ipo food =
  match List.contains food ipo.PreparedFoods with
  | true -> Some food
  | false -> None

Now we all set to refactor prepareFood and here we go

1
2
3
4
5
6
7
8
9
let prepareFood food ipo =
  match food with
  | NonOrderedFood ipo.PlacedOrder _ ->
    printfn "Can not prepare non ordered food"
  | PreparedFood ipo _ ->
    printfn "Can not prepare already prepared food"
  | AlreadyServedFood ipo _ ->
    printfn "Can not prepare already served food"
  | _ -> printfn "Prepare Food"

Awesome!

You can get the complete code in my github repository

Summary

F# is excellent at concisely expressing business and domain logic - ThoughtWorks Tech Radar 2012

Refactoring Composition Root

Comments

Last week, I was working on a task which involves populating some PostgreSQL database tables from CSV files. Though this is a straight forward work, I’ve encountered an interesting approach while manipulating a CSV file to adhere some changes in the database schema. In this blog post, I will be sharing what it is and how it made me think better.

Unlike my other blog posts, this post is more on the thought process behind my approach than the code.

The Story

The CSV file that we suppose to import to a database table has the following Structure with 1000 rows

1
2
3
4
5
6
code,value
XAS,0.23
SDR,3.21
WFZ,1.87
........
........

The column name code and value are just for illustration.

The Proposed schema change is introduce a new column called year with a fixed value 2015 for all the rows. So, the expected CSV file would like

1
2
3
4
5
6
year,code,value
2015,XAS,0.23
2015,SDR,3.21
2015,WFZ,1.87
........
........

A Very Simple Task

Now, How can we solve it?

Approach 1 - Don’t try this at your office/home

Let me start with an approach number one

Edit all the rows manually

It is something that you never wanted to see/do. So, let me move on to the next approach without wasting any time here

Approach 2 - Leverage your programming skill

Pick your favorite programming language and whack the CSV file

I believe this would be the approach that came to your mind when you read the problem statement. A typical programmer’s instinct!

So, you picked your programming language ‘X’, what would be the next steps

  • Open the IDE/Editor - Wait for it to load!
  • Create a Project - if required and wait for it also to load!
  • Start typing - and your phone rings
  • Refer library documentation - or Refer Stackoverflow
  • Compile - and wait for it - Ignore this step and wait for a run-time error if your programming language is dynamic
  • Execute - A bug -> Go to Google -> Stack Overflow -> Change the code -> Start again
  • You are done!

I’ve exaggerated some things here. If you don’t agree with any steps, just ignore that step

After 2 to 15 minutes (based on your ability & the ceremony of your programming language) you have solved the problem.

Is it really worth? Well, It depends on the context.

If there are lot of CSV files which require the same change or also few more like this will about to come in the near future, then this effort really makes sense

But, here it is only one file which requires the change. So, I feel it is a complex response to a simple problem

Approach 3 - Use a tool

Use a shiny CSV management tool to make the changes

It is slightly better approach than the previous one but it also has its own downsides

  • Googling to find the tool
  • Download it - Your office firewall is blocking that site! / Registration on the tool site / Requires Java x.x version
  • Open the CSV file.
  • Refer the tool’s documentation
  • Get the job done

You can also Microsoft Excel to achieve this. But again a heavy solution to solve a simple problem!

There should be a simple approach! That’s what we are going to see next.

Approach 4 - Find And Replace

Just use Find and Replace in an advanced editor

This approach involves just three steps

  • Open the CSV file in an advanced text editor (notepad++, atom, sublime text, etc.,)
  • Select all the text and press tab
  • Open Find and Replace. Find for tab and replace it with 2015,

That’s it!

Summary

Comments

One of the impressive aspects of functional programming is that it will enable you to write simple and expressive code to solve complex problems. If you are wondering, how is it possible? Well, It is because, Functional Programming provides a lot of higher level abstractions to solve the problem in hand and avoids the need of writing lot of boilerplate & plumbing code. As a developer, you will be more productive and deliver the solution faster.

In this blog post, we are going to see the application of the above statements by implementing an API Gateway Pattern in F# using Rx and Suave.

This blog post is my contribution towards fsharp advent calendar 2015. Do check out other great fsharp posts there

API Gateway Pattern

Let’s assume that you are asked to build the backend for the below GitHub user profile screen.

Github Profile

This profile screen has 3 components

  1. User - Username and Avatar

  2. Popular Repos - Top 3 Repos of the Given User (determined by the number of stars)

  3. Languages being used in the corresponding repos

Now as a developer, you have two options

  • Build the Profile completely in client side by calling their corresponding GitHub APIs Profile with URLs

    This approach makes the client side complex as it involves five HTTP requests to create the complete profile screen. It has a lot of drawbacks as mentioned in this blog post by Netflix.

  • Create a Facade API which takes care of the heavy lifting and hides the complexity of calling multiple services by exposing a single endpoint which returns all the data required to create the profile screen in one API call API Gateway

    This approach is called API Gateway. It is one of the commonly used patterns in the microservices world.

Rx

API Gateway is normally implemented using Rx. Rx, an implementation of Functional Reactive Programming, makes it easy to implement this kind of problems by enable us to think each asynchronous operation as streams (aka Observables). These streams offer a declarative API to work with the asynchronous operations.

In the implementation that we are going to see, we will be using FSharp.Control.Reactive, an extension and wrapper for using the Reactive Extensions (Rx) with F#

Project Structure

Create a new F# Console Application Project with the name RxFsharp and then create the files mentioned below in the given order

1
2
3
4
5
6
7
8
9
10
11
RxFsharp
|--Http.fs --> Abstraction to make http requests
|--GitHub.fs --> Define data types, transforming & parsing of responses from GitHub
|--ObservableExtensions.fs --> Some Util methods over Rx Observable
|--Profile.fs --> Implemention of API Gateway using Rx
|--ApiGateway.fs --> Suave API
|--Program.fs --> Entry Point
|--user.json --> Sample response of GitHub user API
|--repos.json --> Sample response of GitHub repos API
|--App.config
|--packages.config

Then install the following NuGet packages

Modeling Http Request & Response as Stream

In Functional Reactive Programming(FRP) every action is treated as stream. So, first step in implementing an API Gateway is modeling each HTTP request&response as stream.

Open Http.fs file and update it as below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module Http

open HttpClient
open FSharp.Control.Reactive

type HttpResponse =
| Ok of string
| Error of int

let getResponseAsync url =
  async {
    let! response =
      createRequest Get url
      |> withHeader (UserAgent "FsharpRx")
      |> HttpClient.getResponseAsync
    let httpResponse =
      match response.StatusCode with
      | 200 -> response.EntityBody.Value |> Ok
      | _ -> response.StatusCode |> Error
    return httpResponse
  }

let asyncResponseToObservable = getResponseAsync >> Observable.ofAsync

The getResponseAsync function makes use of Http.fs, an HTTP Client library for F#, to fire the HTTP GET request and returns the HTTP response asynchronously.

The HTTP response has been modeled as either Ok with the response content for all the responses with the status code 200 and everything else is treated as Error for simplicity.

In the final line, we create a stream (aka Observable) from asynchronous response using the function Observable.ofAsync from FSharp.Control.Reactive

As all the GitHub API requests require user agent, we are adding one before firing the request.

Parsing and Transforming GitHub API Responses

Upon successful response from GitHub, the next step is parsing the JSON response and transforming the parsed response as per the Profile screen requirements.

To parse the response, we are going to leverage the powerful tool of FSharp called JsonTypeProvider

The JSON Type Provider provides statically typed access to JSON documents. It takes a sample document as an input. Here in our case, it is user.json and repos.json

Get the sample response from GitHub using its User API and Repos API and save the response in user.json and repos.json respectively.

With the help of JsonTypeProvider, we can now easily parse the raw JSON response to its equivalent types in just a few lines of code in GitHub.fs!

1
2
3
4
5
6
7
8
9
10
module GitHub

open Http
open FSharp.Data

type GitHubUser = JsonProvider<"user.json">
type GitHubUserRepos = JsonProvider<"repos.json">

let parseUser = GitHubUser.Parse
let parseUserRepos = GitHubUserRepos.Parse

As GitHub.fs is an abstraction of GitHub let’s put their URLs also here in the same file

1
2
3
4
let host = "https://api.github.com"
let userUrl = sprintf "%s/users/%s" host
let reposUrl = sprintf "%s/users/%s/repos" host
let languagesUrl repoName userName = sprintf "%s/repos/%s/%s/languages" host userName repoName

We have used partial application of function sprintf in the userUrl and reposUrl function here, so both of these functions take username as its parameter implicitly

The JSON response of languages API is little tricky to parse as its keys are dynamic. The type provider will work only if the underlying response has a fixed schema. So, we can’t use the type provider directly to parse the response of languages API.

We are going to use the inbuilt JSON Parser available with JsonTypeProvider to parse the languages response

1
2
3
4
5
let parseLanguages languagesJson =
  languagesJson
  |> JsonValue.Parse
  |> JsonExtensions.Properties
  |> Array.map fst

The JsonValue.Parse parses the raw string JSON to a type called JsonValue and the JsonExtensions.Properties function takes a JsonValue and returns the key and value pairs of all the properties in the JSON as tuples. As we are interested only in the Keys here, we just pluck that value alone using the function fst

Great! Now we are done with parsing the JSON response of all the three APIs and creating it’s equivalent types. The next step is doing some business transformation

One of the requirements of the Profile Screen is that we need to show only the top 3 popular repositories based on the stars received by the repository. Let’s implement it

1
2
3
4
5
6
7
8
9
let popularRepos (repos : GitHubUserRepos.Root []) =
  let ownRepos = repos |> Array.filter (fun repo -> not repo.Fork)
  let takeCount = if ownRepos.Length > 3 then 3 else repos.Length

  ownRepos
  |> Array.sortBy (fun r -> -r.StargazersCount)
  |> Array.toSeq
  |> Seq.take takeCount
  |> Seq.toArray

In the Http.fs file, we defined how to get the raw JSON response using Stream as HttpResponse (Ok & Error) and in this GitHub.fs, we have defined how to parse this raw JSON response and transform them its equivalent strongly types.

The final step is integrating both these logic and return the Output record type needed by the Profile Screen

let’s start by defining the Profile type

1
2
3
4
5
6
7
8
9
type Profile = {
  Name : string
  AvatarUrl : string
  PopularRepositories : Repository seq
} and Repository = {
  Name : string
  Stars : int
  Languages : string[]
}

Then write functions to get the value from HttpResponse and create this profile output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let reposResponseToPopularRepos = function
  |Ok(r) -> r |> parseUserRepos |> popularRepos
  |_ -> [||]

// Languages always associated with a repository
let languageResponseToRepoWithLanguages (repo : GitHubUserRepos.Root) = function
  |Ok(l) -> {Name = repo.Name; Languages = (parseLanguages l); Stars = repo.StargazersCount}
  |_ -> {Name = repo.Name; Languages = Array.empty; Stars = repo.StargazersCount}

let toProfile  = function
  |Ok(u), repos ->
      let user = parseUser u
      {Name = user.Name; PopularRepositories = repos; AvatarUrl = user.AvatarUrl} |> Some
  | _ -> None

All the above functions except toProfile take HttpResponse as its last parameter implicitly.

In reposResponseToPopularRepos function, if the repos API request is successful, we parse the response to its equivalent type and then pick only three of them based on star count and in the case of an error we just return an empty array.

The languageResponseToRepoWithLanguages function handles the response from the Languages API request which takes its associated repository as its first parameter. If the response is successful, then it creates the Repository record with the returned languages else it just the Repository record with an empty array for languages.

The last function toProfile is a merge function which takes a tuple of HttpResponse (of User API request) and Repository [] and creates a Profile record if the response is successful. In case of an error, it just returns None

Note: To keep this blog post simple, I am handling the errors using empty arrays and None. It can be extended using ROP

Implementing API Gateway

Let me quickly summarize what we have done so far. We have created two abstractions.

  • Http - Responsible for firing the HTTP GET request with the given URL and give the response as a Rx Stream
1
2
3
URL
  \
----------Response--|
  • GitHub - Takes care of parsing the JSON response from GitHub API and does some business logic (finding top 3 popular repositories). Then returns the output in the format that the Client needs.

With the help of these abstractions now we are going to build the API Gateway to get the profile object.

Open Gateway.fs and add the getProfile function

1
2
3
4
5
6
//string -> Async<Profile>
let getProfile username =
  async {
    // TODO
    return! profile
  }

It is just a skeleton which just describes the what function does. Let’s start its implementation.

In Rx world, everything is a stream. So, the first step is converting GitHub User API request to stream

1
2
3
4
let getProfile username =
  // ...
  let userStream = username |> userUrl |> asyncResponseToObservable
  // ...

We have created the userStream using the userUrl and asyncResponseToObservable function defined earlier.

1
2
3
userURL
  \
-----------UJ--|              // UJ - GitHub User Response in JSON format

Then we need top three popular repositories as a stream

1
2
3
4
5
6
7
8
let getProfile username =
  // ...
  let popularReposStream =
    username
    |> reposUrl
    |> asyncResponseToObservable
    |> Observable.map reposResponseToPopularRepos
  // ...

Except the last line, everything is same as that of creating userStream. If you check GitHub repos API it just returns all the repos associated with the given username. But what we need is only top three of them based on the number of stars it has received.

We already have a function reposResponseToPopularRepos in GitHub.fs which does the job of picking the top three repos from the raw JSON. As the response is in the form Rx stream, we need to get the value from the stream and then we need to apply this function and that’s what the Observable.map does. It is very similar to Array.map and LINQ Select

1
2
3
4
5
6
reposURL
  \
----------RJ---|                          // RJ - GitHub user repos in JSON format
          | MAP function (RJ -> PR)       // PR - Popular Repos
          V
--------------PR---|

The next operation is a little complex. i.e. finding out the programming languages being used in these popular repositories. To get this GitHub API, we need to fire three separate requests to the Languages API for each repository and then merge the results back with the corresponding repository.

To implement this with the help of Rx streams, we need to understand the flatMap function of Rx.

It is similar to the map function that we have seen before with a difference that it takes a function that returns a new item stream instead of a new item.

Map

Map

The map function has the following signature

1
('a -> 'b) -> IObservable<'a> -> IObservable<'b>

FlatMap

FlatMap

The flatMap function has the following signature

1
('a -> IObservable<'b>) -> IObservable<'a> -> IObservable<'b>

This function is also called as bind function in the functional programming world. If you would like to know further on this topic, I strongly recommend this blog series by the fsharp great, Scott Wlaschin

Back to the problem in hand, we need to fire three HTTP GET Requests to get back the languages associated with the each of the top three popular repos. In terms of the flatMap function, it boils down to three functions of the following syntax

1
2
(GitHubUserRepos.Root -> IObservable<Repostiory>)
  -> IObservable<GitHubUserRepos.Root> -> IObservable<Repostiory>

We can implement the solution using three flatMap functions using the above syntax. But, we can make it more granular by creating a new variant of flatMap function to achieve this in a more straightforward way.

The ideal function that we are looking for in this flatMap variant holds the following signature

1
('a -> IObservable<'b>) -> IObservable<'a []> -> IObservable<'b []>

i.e three flatMap can be rewritten as

1
2
(GitHubUserRepos.Root -> IObservable<Repostiory>)
  -> IObservable<GitHubUserRepos.Root []> -> IObservable<Repostiory []>

Let’s name it as flatMap2 and add the implementation of it in the file ObservableExtensions.fs

1
2
3
4
5
6
7
module ObservableExtensions
open FSharp.Control.Reactive

let flatmap2 f observable =
  observable
  |> Observable.flatmap (Array.map f >> Observable.mergeArray)
  |> Observable.toArray

Here is the representation of what this function does

1
2
3
4
5
6
7
8
9
10
11
-----X[n]--------------------------|->
      \   flatMap on each item in X which yield the stream of R
-------R1-------------------------|->
----------R2----------------------|->
          ......
-------------Rn-------------------|->
          ||  mergeArray -> merge array of R streams into one stream
          VV
------R1-R2--Rn-------------------|->
              \ toArray -> Creates an array from an observable sequence
-------------R[n]-----------------|->

It’s hard to get it right in a single shot. So, let’s see it in detail step by step by applying it to our use case here

  • We have the stream of three popular repos (i.e array of repos)
1
-----X[n]--------------------------|->
  • To get the languages associated with the each repo we need to call the languages API for every repo item in the above step
1
2
3
4
5
6
-----X[n]--------------------------|->
      \   flatMap on each item in X which yield the n streams of R
-------R1-------------------------|->
----------R2----------------------|->
          ......
-------------Rn-------------------|->
  • After we received the response from each languages API call, we need to merge them into one stream
1
2
3
4
5
6
7
-------R1-------------------------|->
----------R2----------------------|->
          ......
-------------Rn-------------------|->
          |  mergeArray -> merge array of R streams into one stream
          V
------R1-R2--Rn-------------------|->
  • To integrate this with the expected application response, we need all the responses in a single go
1
2
3
------R1-R2--Rn-------------------|->
              \ toArray -> Creates an array from an observable sequence
-------------R[n]-----------------|->

Great! You got it right!!

Let’s use this flatMap2 function and complete the implementation of profile API gateway.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let getProfile username =
  // ...
  let toRepoWithLanguagesStream (repo : GitHubUserRepos.Root) =
    username
    |> languagesUrl repo.Name
    |> asyncResponseToObservable
    |> Observable.map (languageResponseToRepoWithLanguages repo)

  let popularReposStream =
    username
    |> reposUrl
    |> asyncResponseToObservable
    |> Observable.map reposResponseToPopularRepos
    |> flatmap2 toRepoWithLanguagesStream

  // ...

The flatmap2 function takes the function toRepoWithLanguagesStream which converts the GitHubUserRepos.Root type to IObservable<Repository> to find out the languages associated with the given popular repositories.

The toRepoWithLanguagesStream function does the following

1
2
3
4
5
GitHubUserRepos.Root
  \ create a languages GitHub API stream using the repo name from the input
-------LR----------|              // LR - languages GitHub API response
        \ MAP function (GitHubUserRepos.Root -> LR -> R)
------------R------|              // R - Repository type that defined earlier

The Observable.map function takes only one input, but here we need to two inputs. So, With the help of partial application, we created an intermediate function by partially applying the first parameter alone

1
Observable.map (languageResponseToRepoWithLanguages repo)

The languageResponseToRepoWithLanguages function has been already defined in the GitHub.fs file.

The last step of the getProfile function is combining this popularReposStream with the userStream created earlier and return the Profile type asynchronously.

1
2
3
4
5
6
7
8
9
10
let getProfile username =
  // ...
  async {
    return! popularReposStream
            |> Observable.zip userStream
            |> Observable.map toProfile
            |> TaskObservableExtensions.ToTask
            |> Async.AwaitTask
  }
  // ...

The Observable.zip function takes two streams as its input, then merges the output of each stream and return the output as a tuple. From this tuple, we have used Observable.map function to map it a Profile type using the toProfile function created earlier in GitHub.fs

1
2
3
4
5
6
-----UR-------------|     // UR - GitHub User API Response
--------PR----------|     // PR - Popular Repos
        \ ZIP function (UR -> PR -> (UR,PR))
---------(UR,PR)----|
          \ MAP ( (UR,PR) -> P )  // P - Profile
----------P---------|

The last functions TaskObservableExtensions.ToTask and Async.AwaitTask does the conversion of IObservable to async by converting it to a Task first and then the Task to async

The final getProfile function will be like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let getProfile username =

  let userStream = username |> userUrl |> asyncResponseToObservable

  let toRepoWithLanguagesStream (repo : GitHubUserRepos.Root) =
    username
    |> languagesUrl repo.Name
    |> asyncResponseToObservable
    |> Observable.map (languageResponseToRepoWithLanguages repo)

  let popularReposStream =
    username
    |> reposUrl
    |> asyncResponseToObservable
    |> Observable.map reposResponseToPopularRepos
    |> flatmap2 toRepoWithLanguagesStream

  async {
    return! popularReposStream
            |> Observable.zip userStream
            |> Observable.map toProfile
            |> TaskObservableExtensions.ToTask
            |> Async.AwaitTask
  }

This function is a testimonial on How functional programming helps to write less, robust, and readable code to solve a complex problem

We have handled all the five HTTP requests asynchronously, did some error handling (by returning empty types), and finally efficiently combined outputs of these five HTTP requests and created a type to send back to the client. Everything is asynchronous!

Pretty awesome isn’t it?

Exposing the API

The final step is exposing what we have done so far as an API to the outside world. We are going to implement this using Suave

Open ApiGateway.fs file and update it as below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let JSON v =
  let jsonSerializerSettings = new JsonSerializerSettings()
  jsonSerializerSettings.ContractResolver <- new CamelCasePropertyNamesContractResolver()

  JsonConvert.SerializeObject(v, jsonSerializerSettings)
  |> OK
  >=> Writers.setMimeType "application/json; charset=utf-8"

let getProfile userName (httpContext : HttpContext) =
   async {
      let! profile = getProfile userName
      match profile with
      | Some p -> return! JSON p httpContext
      | None -> return! NOT_FOUND (sprintf "Username %s not found" userName) httpContext
   }

The JSON is a utility function (WebPart in the world of Suave) which takes any type, serialize it to JSON format and return it as JSON HTTP response.

The getProfile function is the API WebPart which calls our backend API gateway implementation and pass the received response to the JSON WebPart defined before.

In case if there is no profile available (Remember? we return empty types in case of errors), we just return 404 with the message that the given username is not found.

Then update the Program.fs to write the web server code

1
2
3
4
5
[<EntryPoint>]
let main argv =
  let webpart = pathScan "/api/profile/%s" ApiGateway.getProfile
  startWebServer defaultConfig webpart
  0

Thanks to Suave for it’s lightweight and low-ceremony offerings in creating an API. We just exposed it in two lines!

Hit F5 and access the API at http://localhost:8083/api/profile/{github-username} Bingo!!

Summary

A language that doesn’t affect the way you think about programming is not worth knowing - Alan Perils

The above quote summarizes the gist of this blog post. Functional Programming will help you think better. You can get the source code associated with the blog post in my blog-samples GitHub repository

Wish you a happy and prosperous new year