Servant is one of many high-quality Haskell packages available for writing web applications. I cannot hope to best the official Servant docs. However, while the docs do a great job of explaining the mechanics of Servant, in this post, I want to impart my conceptual model of Servant.
The big idea behind Servant is that we can describe an endpoint in a web application formally, as a Haskell type. Once we have a formal description of the endpoint, Servant can do many “nice” things, such as generate documentation, generation client functions, etc. However, those features are tangential to serving the actual web application. Where Servant’s true benefits lie is that, once it has this formal specification of the endpoint, Servant can ensure that the request adheres to that specification. It can ensure that the data our request handlers require — query params, request headers, request bodies / content — is available. …
In this post, I want to explain how to use the
MonadIO typeclass in Haskell. Note that this post is an explanation of what
MonadIO does for us, not how it does it.
In fact, more than anything, this is going to be an example of using the substitution model and equational reasoning to understand a small program.
The code that we are going to be looking at is this:
If you have stack installed, you can play around with this by creating a new stack project with the
main function above, the entry point for our program, is defined as
scotty 3000 <second argument here>. …
Note: This was translated from markdown to medium via markdowntomedium.com.
Reader is a type that is useful when we want to make some information available intermittently or universally throughout an application. Conceptually, the
Reader type allows us to read a value from the environment. Mechanically,
Reader is a newtype wrapper for a function of type
r -> a.
In order to understand how
Reader works, it is necessary to understand how the functor of functions works.
In short, the functor of functions is simply function composition:
The above may look a bit strange but we can rearrange the (specialized) type…
This is a continuation of my trials and tribulations as I work through the Haskell Book.
Unlike some of my other posts, in which the material came came almost exclusively from the Haskell Book itself, the material from this post is an amalgamation of different source, include the Haskell wikibook, Learn You a Haskell, and others source which I can’t now recall.
Conceptually, we can think of a “stateful” computation as a function with the
f :: s -> (a, s)
In other words, given a state, the function will return an answer and a new
There is a type that we can use to wrap stateful…
NOTE: This entire post is in draft stage. The reason why it is published is because I want others to review. Once enough errors have been fixed, I’ll remove this, though it will probably always be considered “beta”.
Servant is a Haskell library that can be used as the back-end for a website. There are many web frameworks in Haskell, but what is interesting about Servant is that, given a type describing an API, the framework can write the server, produce client functions, and generate documentation.
This might seem kind of magical, and frankly it is. I don’t understand the implementation details of this myself. However, in order to be productive with Servant, we only need to understand what is going on at a conceptual level — we need a correct mental model. …
Functional programming is a hot topic nowadays. Because pure functions are an important part of functional programming, that means that pure functions are a hot topic too.
The question is, why should you care about pure functions?
First, what is a pure function? A pure function is a function that will return the same results given the same arguments, and has no side-effects.
return 15 + n;
No-matter how often or when you call it, the invocation of
addFifteen(10) will always, in all cases, unequivocally return
Custom Data Types — A Haskell best practice is to create custom datatypes to model a domain. We want to use datatypes to describe the structure of the data that our program is going to be processing. Modeling our domain through datatypes is very important, because it allows the compiler to help us later on. Modeling our domain through datatypes is like modeling our persistence layer in databases — get this part right, and the rest of the program will be easier and more obvious.
Cardinality, Sum Types, Product Types — Why do we care about sum types, product types and cardinality? (Note: Definition of all of these terms are provided in the section below). The reason these things are important is because it tells us how many different implementations there are for a given type. …
Folds — Folds are is a family of higher order functions that process a data structure in some order and build a return value. It is common to use folds to reduce a list (let’s forget about
Foldable for a bit) to a single value. …
Data Structures and Spines — When we talk about data structures in Haskell, and in particular lists, sequences, and trees, we talk about these data structures having a spine. This is the connective structure that ties the collection of values together. The spine of a structure can be evaluated separately from the values contained by the structure.
List Comprehensions — List comprehensions are a way to write concise map and filter operations in one go. In the expression
[x ^ 2 | x <- xs, x > 5],
x ^ 2 is the output function,
x <- xs is the generator, and
x > 5 is the predicate function. Note that we can have more than one generator, and more than one predicate. …
Recursion — A recursive function is a function defined in terms of itself. When writing a recursive function, we must handle two cases. The first is the base case, when no further computation is needed and we can return a result. The second case is the inductive case, which occurs when further computation is in fact needed. When writing recursive functions, it is helpful to think about the base case first. It is important to keep in mind that, for any given recursive function, we might have several base / inductive cases. For example:
fibonacci :: Integer -> Integer
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n - 1) + fibonacci (n …