James Bowen James Bowen

Purescript II: Typeclasses and Monads

purescript_2.png

Last week, we started our exploration of Purescript. Purescript seeks to bring some of the awesomeness of Haskell to the world of web development. Its syntax looks a lot like Haskell's, but it compiles to Javascript. This makes it very easy to use for web applications. And it doesn't just look like Haskell. It uses many of the important features of the language, such as a strong system and functional purity.

If you need to brush up on the basics of Purescript, make sure to check out that first article again. This week, we're going to explore a couple other areas where Purescript is a little different. We'll see how Purescript handles type-classes, and we'll also look at monadic code. We'll also take a quick look at some other small details with operators. Next week, we'll look at how we can use Purescript to write some front-end code.

For another perspective on functional web development, check out our Haskell Web Series. You can also download our Production Checklist for some more ideas! You can also take a gander at our Elm Series for another frontend language!

Type Classes

The idea of type classes remains pretty consistent from Haskell to Purescript. But there are still a few gotchas. Let's remember our Triple type from last week.

data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  }

Let's write a simple Eq instance for it. To start with, instances in Purescript must have names. So we'll assign the name tripleEq to our instance:

instance tripleEq :: Eq Triple where
  eq (Triple t1) (Triple t2) = t1 == t2

Once again, we only unwrap the one field for our type. This corresponds to the record, rather than the individual fields. We can, in fact, compare the records with each other. The name we provide helps Purescript to generate Javascript that is more readable. Take note: naming our instances does NOT allow us to have multiple instances for the same type and class. We'll get a compile error if we try to create another instance like:

instance otherTripleEq :: Eq Triple where
  ...

There's another small change when using an explicit import for classes. We have to use the class keyword in the import list:

import Data.Eq (class Eq)

You might hope we could derive the Eq typeclass for our Triple type, and we can. Since our instance needs a name though, the normal Haskell syntax doesn't work. The following will fail:

-- DOES NOT WORK
data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  } deriving (Eq)

For simple typeclasses though, we CAN use standalone deriving. This allows us to provide a name to the instance:

derive instance eqTriple :: Eq Triple

As a last note, Purescript does not allow orphan instances. An orphan instance is where you define a typeclass instance in a different file from both the type definition and the class definition. You can get away with these in Haskell, though GHC will warn you about it. But Purescript is less forgiving. The way to work around this issue is to define a newtype wrapper around your type. Then you can define the instance on that wrapper.

Effects

In part 1, we looked at a small snippet of monadic code. It looked like:

main :: Effect Unit
main = do
  log ("The answer is " <> show answer)

If we're trying to draw a comparison to Haskell, it seems as though Effect is a comparable monad to IO. And it sort've is. But it's a little more complicated than that. In Purescript, we can use Effect to represent "native" effects. Before we get into exact what this means and how we do it, let's first consider "non-native" effects.

A non-native effect is one of those monads like Maybe or List that can stand on its own. In fact, we have an example of the List monad in part 1 of this series. Here's what Maybe might look like.

maybeFunc :: Int -> Maybe Int

mightFail :: Int -> Maybe Int
mightFail x = do
  y <- maybeFunc x
  z <- maybeFunc y
  maybeFunc z

Native effects use the Effect monad. These include a lot of things we'd traditionally associate with IO in Haskell. For instance, random number generation and console output use the Effect monad:

randomInt :: Int -> Int -> Effect Int

log :: String -> Effect Unit

But there are also other "native effects" related to web development. The most important of these is anything that writes to the DOM in our Javascript application. Next week, we'll use the purescript-react library to create a basic web page. Most of its main functions are in the Effect monad. Again, we can imagine that this kind of effect would use IO in Haskell. So if you want to think of Purescript's Effect as an analogue for IO, that's a decent starting point.

What's interesting is that Purescript used to be more based on the system of free monads. Each different type of native effect would build on top of previous effects. The cool part about this is the way Purescript uses its own record syntax to track the effects in play. You can read more about how this can work in chapter 8 of the Purescript Book. However, we won't need it for our examples. We can just stick with Effect.

Besides free monads, Purescript also has the purescript-transformers library. If you're more familiar with Haskell, this might be a better starting spot. It allows you to use the MTL style approach that's more common in Haskell than free monads.

Special Operators

It's worth noting a couple other small differences. Some rules about operators are a little different between Haskell and Purescript. Since Purescript uses the period operator . for record access, it no longer refers to function composition. Instead, we would use the <<< operator:

odds :: List Int -> List Int
odds myList = filter (not <<< isEven) myList
  where
    isEven :: Int -> Boolean
    isEven x = mod x 2 == 0

Also, we cannot define operators in an infix way. We must first define a normal name for them. The following will NOT work:

(=%=) :: Int -> Int -> Int
(=%=) a b = 2 * a - b

Instead, we need to define a name like addTwiceAndSubtract. Then we can tell Purescript to apply it as an infix operator:

addTwiceAndSubtract :: Int -> Int -> Int
addTwiceAndSubtract a b = 2 * a - b

infixrl 6 addTwiceAndSubtract as =%=

Finally, using operators as partial functions looks a little different. This works in Haskell but not Purescript:

doubleAll :: List Int -> List Int
doubleAll myList = map (* 2) myList

Instead, we want syntax like this:

doubleAll :: List Int -> List Int
doubleAll myList = map (_ * 2) myList

Conclusion

This wraps up our look at the key differences between Haskell and Purescript. Now that we understand typeclasses and monads, it's time to dive into what Purescript is best at. Come back next week, and we'll look at how we can write real frontend code with Purescript!

For some more ideas on using Haskell for some cool functionality, download our Production Checklist! For another look at function frontend development, check out our recent Elm Series!

Read More
James Bowen James Bowen

Getting Started with Purescript!

purescript.png

Our Haskell Web Series covers a lot of cool libraries you can use when making a web app. But one thing we haven't covered on this blog yet is using Haskell for front-end web development. There are a number libraries and frameworks out there. Yesod and Snap come to mind. Another option is Reflex FRP, which uses GHCJS under the hood.

But for this new series I've decided to take a different approach. For the next few weeks, we're going to be exploring the Purescript language. Purescript is a bit of a meld between Haskell and Javascript. Its syntax is like Haskell's, and it incorporates many elements of functional purity. But it compiles to Javascript and thus has some features that seem more at home in that language.

This week, we'll start out by exploring the basics of Purescript. We'll see some of the main similarities and differences between it and Haskell. We'll culminate this series by making a web front-end with Purescript. We'll connect this front-end to Haskell code on the back-end.

Purescript is the tip of the iceberg when it comes to using functional languages in product! Check out our Production Checklist for some awesome Haskell libraries!

Getting Started

Since Purescript is its own language, we'll need some new tools. You can follow the instructions on the Purescript website, but here are the main points.

  1. Install Node.js and NPM, the Node.js package manager
  2. Run npm install -g purescript
  3. Run npm install -g pulp bower
  4. Create your project directory and run pulp init.
  5. You can then build and test code with pulp build and pulp test.
  6. You can also use PSCI as a console, similar to GHCI.

First, we need NPM. Purescript is its own language, but we want to compile it to Javascript we can use in the browser, so we need Node.js. Then we'll globally install the Purescript libraries. We'll also install pulp and bower. Pulp will be our build tool like Cabal.

Bower is a package repository like Hackage. To get extra libraries into our program, you would use the bower command. For instance, we need purescript-integers for our solution later in the article. To get this, run the command:

bower install --save purescript-integers

A Simple Example

Once you're set up, it's time to start dabbling with the language. While Purescript compiles to Javascript, the language itself actually looks a lot more like Haskell! We'll examine this by comparison. Suppose we want to find the all pythagorean triples whose sum is less than 100. Here's how we can write this solution in Haskell:

sourceList :: [Int]
sourceList = [1..100]

allTriples :: [(Int, Int, Int)]
allTriples =
  [(a, b, c) | a <- sourceList, b <- sourceList, c <- sourceList]

isPythagorean :: (Int, Int, Int) -> Bool
isPythagorean (a, b, c) = a ^ 2 + b ^ 2 == c ^ 2

isSmallEnough :: (Int, Int, Int) -> Bool
isSmallEnough (a, b, c) = a + b + c < 100

finalAnswer :: [(Int, Int, Int)]
finalAnswer = filter 
  (\t -> isPythagorean t && isSmallEnough t)
    allTriples

Let's make a module in Purescript that will allow us to solve this same problem. We'll start by writing a module Pythagoras.purs. Here's the code we would write to match up with the Haskell above. We'll examine the specifics piece-by-piece below.

module Pythagoras where

import Data.List (List, range, filter)
import Data.Int (pow)
import Prelude

sourceList :: List Int
sourceList = range 1 100

data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  }

allTriples :: List Triple
allTriples = do
  a <- sourceList
  b <- sourceList
  c <- sourceList
  pure $ Triple {a: a, b: b, c: c}

isPythagorean :: Triple -> Boolean
isPythagorean (Triple triple) =
  (pow triple.a 2) + (pow triple.b 2) == (pow triple.c 2)

isSmallEnough :: Triple -> Boolean
isSmallEnough (Triple triple) =
  (triple.a) + (triple.b) + (triple.c) < 100

finalAnswer :: List Triple
finalAnswer = filter
  (\triple -> isPythagorean triple && isSmallEnough triple) 
  allTriples

For the most part, things are very similar! We still have expressions. These expressions have type signatures. We use a lot of similar elements like lists and filters. On the whole, Purescript looks a lot more like Haskell than Javascript. But there are some key differences. Let's explore those, starting with the higher level concepts.

Differences

One difference you can't see in code syntax is that Purescript is NOT lazily evaluated. Javascript is an eager language by nature. So it is much easier to compile to JS by starting with an eager language in the first place.

But now let's consider some of the differences we can see from the code. For starters, we have to import more things. Purescript does not import a Prelude by default. You must always explicitly bring it in. We also need imports for basic list functionality.

And speaking of lists, Purescript lacks a lot of the syntactic sugar Haskell has. For instance, we need to use List Int rather than [Int]. We can't use .. to create a range, but instead resort to the range function.

We also cannot use list comprehensions. Instead, to generate our original list of triples, we use the list monad. As with lists, we have to use the term Unit instead of ():

-- Comparable to main :: IO ()
main :: Effect Unit
main = do
  log "Hello World!"

Next week, we'll discuss the distinction between Effect in Purescript and monadic constructs like IO in Haskell.

One annoyance is that polymorphic type signatures are more complicated. Whereas in Haskell, we have no issue creating a type signature [a] -> Int, this will fail in Purescript. Instead, we must always use the forall keyword:

myListFunction :: forall a. List a -> Int

Another thing that doesn't come up in this example is the Number type. We can use Int in Purescript as in Haskell. But aside from that the only important numeric type is Number. This type can also represent floating point values. Both of these get translated into the number type in Javascript.

Purescript Data Types

But now let's get into one of the more glaring differences between our examples. In Purescript, we need to make a separate Triple type, rather than using a simple 3-tuple. Let's look at the reasons for this by considering data types in general.

If we want, we can make Purescript data types in the same way we would in Haskell. So we could make a data type to represent a Pythagorean triple:

data Triple = Triple a b c

This works fine in Purescript. But, it forces us to use pattern matching every time we want to pull an individual value out of this element. We can fix this in Haskell by using record syntax to give ourselves accessor functions:

data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  }

This syntax still works in Purescript, but it means something different. In Purescript a record is its own type, like a generic Javascript object. For instance, we could do this as a type synonym and not a full data type:

type Triple = { a :: Int, b :: Int, c :: Int}

oneTriple :: Triple
oneTriple = { a: 5, b: 12, c: 13}

Then, instead of using the field names like functions, we use "dot-syntax" like in Javascript. Here's what that looks like with our type synonym definition:

type Triple = { a :: Int, b :: Int, c :: Int}

oneTriple :: Triple
oneTriple = { a: 5, b: 12, c: 13}

sumAB :: Triple -> Int
sumAB triple = triple.a + triple.b

Here's where it gets confusing though. If we use a full data type with record syntax, Purescript no longer treats this as an item with 3 fields. Instead, we would have a data type that has one field, and that field is a record. So we would need to unwrap the record using pattern matching before using the accessor functions.

data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  }

oneTriple :: Triple
oneTriple = Triple { a: 5, b: 12, c: 13}

sumAB :: Triple -> Int
sumAB (Triple triple) = triple.a + triple.b

-- This is wrong!
sumAB :: Triple -> Int
sumAB triple = triple.a + triple.b

That's a pretty major gotcha. The compiler error you get from making this mistake is a bit confusing, so be careful!

Pythagoras in Purescript

With this understanding, the Purescript code above should make some more sense. But we'll go through it one more time and point out the little details.

To start out, let's make our source list. We don't have the range syntactic sugar, but we can still use the range function:

import Data.List (List, range, filter)

data Triple = Triple
  { a :: Int
  , b :: Int
  , c :: Int
  }

sourceList :: List Int
sourceList = range 1 100

We don't have list comprehensions. But we can instead use do-syntax with lists instead to get the same effect. Note that to use do-syntax in Purescript we have to import Prelude. In particular, we need the bind function for that to work. So let's generate all the possible triples now.

import Prelude

…

allTriples :: List Triple
allTriples = do
  a <- sourceList
  b <- sourceList
  c <- sourceList
  pure $ Triple {a: a, b: b, c: c}

Notice also we use pure instead of return. Now let's write our filtering functions. These will use the record pattern matching and accessing mentioned above.

isPythagorean :: Triple -> Boolean
isPythagorean (Triple triple) = 
  (pow triple.a 2) + (pow triple.b 2) == (pow triple.c 2)

isSmallEnough :: Triple -> Boolean
isSmallEnough (Triple triple) =
  (triple.a) + (triple.b) + (triple.c) < 100

Finally, we can combine it all with filter in much the same way we did in Haskell:

finalAnswer :: List Triple
finalAnswer = filter 
  (\triple -> isPythagorean triple && isSmallEnough triple)
  allTriples

And now our solution will work!

Conclusion

This week we started our exploration of Purescript. Syntactically, Purescript is a very near cousin of Haskell. But there are a few key differences we highlighted here about the nature of the language.

Next week, we'll look at some other important differences in the type system. We'll see how Purescript handles type-classes and monads. After that, we'll see how we can use Purescript to build a web front-end with some of the security of a solid type system.

Download our Production Checklist for some more cool ideas of libraries you can use!

Read More
James Bowen James Bowen

Elm Series + Purescript Coming Up!

In the last few weeks, we've been quite busy learning about Elm. We're taking a quick break this week from new material. But we're pleased to announce that our Elm series is now a permanent fixture on the advanced section of the site! Here's a quick review of what we covered there:

  1. Part 1 covers the language basics. We learned how to install Elm, the basic syntax, and how it's different from Haskell.
  2. In part 2, we see Elm in its natural habitat, building a simple web application. We make a Todo list app that showcases how Elm's architecture works.
  3. Part 3 expands our vocabulary of Elm techniques. We use effects to incorporate randomness and send some HTTP requests.
  4. In the final part of the series, we learn the basics of navigation. We start by writing a very simple multi-page application. Then we see how the page changes hook into Elm's architecture.

Coming Up: Purescript!

But we're not done with functional frontend yet! Starting next week, we'll learn Purescript! Like Elm, it compiles to Javascript, but has a syntax very reminiscent of Haskell's. It incorporates even more of Haskell's language features than Elm. This makes it even easier to put our functional skills to use with it. So you won't want to miss this series!

If you'd like to stick to Haskell for now though, make sure to check out our other resources, like our Liftoff Series and our Web Skills Series. You can also subscribe to our newsletter and get access to all our awesome resources!

Read More
James Bowen James Bowen

Elm IV: Navigation!

navigation.jpg

Last week, we learned a few more complexities about how Elm works. We examined how to bridge Elm types and Haskell types using the elm-bridge library. We also saw a couple basic ways to incorporate effects into our Elm application. We saw how to use a random generator and how to send HTTP requests.

These forced us to stretch our idea of what our program was doing. Our original Todo application only controlled a static page with the sandbox function. But this new program used element to introduce effects into our program structure.

But there's still another level for us to get to. Pretty much any web app will need many pages, and we haven't seen what navigation looks like in Elm. To conclude this series, let's see how we incorporate different pages. We'll need to introduce a couple more components into our application for this.

Simple Navigation

Now you might be thinking navigation should be simple. After all, we can use normal HTML elements on our page, including the a element for links. So we'd set up different HTML files in our project and use routes to dictate which page to visit. Before Elm 0.19, this was all you would do.

But this approach has some key performance weaknesses. Clicking a link will always lead to a page refresh which can be disrupting for the user. This approach will also lead us to do a lot of redundant loading of our library code. Each new page will have to reload the generated Javascript for Data.String, for example. The latest version of Elm has a new solution for this that fits within the Elm architecture.

An Application

In our previous articles, we described our whole application using the element function. But now it's time to evolve from that definition. The application function provides us the tools we need to build something bigger. Let's start by looking at its type signature (see the appendix at the bottom for imports):

application :
  { init : flags -> Url -> Key -> (model, Cmd msg)
  , view : model -> Document msg
  , update : msg -> model -> (model, Cmd msg)
  , subscriptions : model -> Sub msg
  , onUrlRequest : UrlRequest -> msg
  , onUrlChange : Url -> msg
  }
  - > Program flags model msg

There are a couple new fields to this application function. But we can start by looking at the changes to what we already know. Our init function now takes a couple extra parameters, the Url and the Key. Getting a Url when our app launches means we can display different content depending on what page our users visit first. The Key is a special navigation tool we get when our app starts that helps us in routing. We need it for sending our own navigation commands.

Our view and update functions haven't really changed their types. All that's new is the view produces Document instead of only Html. A Document is a wrapper that lets us add a title to our web page, nothing scary. The subscriptions field has the same type (and we'll still ignore it for the most part).

This brings us to the new fields, onUrlRequest and onUrlChange. These intercept events that can change the page URL. We use onUrlChange to update our page when a user changes the URL at the top bar. Then we use onUrlRequest to deal with a links the user clicks on the page.

Basic Setup

Let's see how all these work by building a small dummy application. We'll have three pages, arbitrarily titled "Contents", "Intro", and "Conclusion". Our content will just be a few links allowing us to navigate back and forth. Let's start off with a few simple types. For our program state, we store the URL so we can configure the page we're on. We also store the navigation key because we need it to push changes to the page. Then for our messages, we'll have constructors for URL requests and changes:

type AppState = AppState
  { url: Url
  , navKey : Key
  }

type AppMessage =
  NoUpdate |
  ClickedLink UrlRequest |
  UrlChanged Url

When we initialize this application, we'll pass the URL and Key through to our state. We'll always start the user at the contents page. We cause a transition with the pushUrl command, which requires we use the navigation key.

appInit : () -> Url -> Key -> (AppState, Cmd AppMessage)
appInit _ url key =
  let st = AppState {url = url, navKey = key}
  in (st, pushUrl key "/contents")

Updating the URL

Now we can start filling in our application. We've got message types corresponding to the URL requests and changes, so it's easy to fill those in.

main : Program () AppState AppMessage
main = Browser.application
  { init : appInit
  , view = appView
  , update = appUpdate
  , subscriptions = appSubscriptions
  , onUrlRequest = ClickedLink -- Use the message!
  , onUrlChanged = UrlChanged
  }

Our subscriptions, once again, will be Sub.none. So we're now down to filling in our update and view functions.

The first real business of our update function is to handle link clicks. For this, we have to break the UrlRequest down into its Internal and External cases:

appUpdate : AppMessage -> AppState -> (AppState, Cmd AppMessage)
appUpdate msg (AppState s) = case msg of
  NoUpdate -> (AppState s, Cmd.none)
  ClickedLink urlRequest -> case urlRequest of
    Internal url -> …
    External href -> ...

Internal requests go to pages within our application. External requests go to other sites. We have to use different commands for each of these. As we saw in the initialization, we use pushUrl for internal requests. Then external requests will use the load function from our navigation library.

appUpdate : AppMessage -> AppState -> (AppState, Cmd AppMessage)
appUpdate msg (AppState s) = case msg of
  NoUpdate -> (AppState s, Cmd.none)
  ClickedLink urlRequest -> case urlRequest of
    Internal url -> (AppState s, pushUrl s.navKey (toString url))
    External href -> (AppState s, load href)

Once the URL has changed, we'll have another message. The only thing we need to do with this one is update our internal state of the URL.

appUpdate : AppMessage -> AppState -> (AppState, Cmd AppMessage)
appUpdate msg (AppState s) = case msg of
  NoUpdate -> (AppState s, Cmd.none)
  ClickedLink urlRequest -> …
  UrlChanged url -> (AppState {s | url = url}, Cmd.None)

Rounding out the View

Now our application's internal logic is all set up. All that's left is the view! First let's write a couple helper functions. The first of these will parse our URL into a page so we know where we are. The second will create a link element in our page:

type Page =
  Contents |
  Intro |
  Conclusion |
  Other

parseUrlToPage : Url -> Page
parseUrlToPage url =
  let urlString = toString url
  in if contains "/contents" urlString
       then Contents
       else if contains "/intro" urlString
         then Intro
         else if contains "/conclusion" urlString
           then Conclusion
           else Other


link : String -> Html AppMessage
link path = a [href path] [text path]

Finally let's fill in a view function by applying these:

appView : AppState -> Document AppMessage
appView (AppState st) =
  let body = case parseUrlToPage st.url of
               Contents -> div [] 
                 [ link "/intro", br [] [], link "/conclusion" ]
               Intro -> div [] 
                 [ link "/contents", br [] [], link "/conclusion" ]
               Conclusion -> div [] 
                 [ link "/intro", br [] [], link "/contents" ]
               Other -> div [] [ text "The page doesn't exist!" ]
  in  Document "Navigation Example App" [body]

And now we can navigate back and forth among these pages with the links!

Conclusion

In this last part of our series, we completed the development of our Elm skills. We learned how to use an application to achieve the full power of a web app and navigate between different pages. There's plenty more depth we can get into with designing an Elm application. For instance, how do you structure your message types across your different pages? What kind of state do you use to manage your user's experience. We'll explore these another time.

We're not done with functional frontend yet though! We've got another series coming up that'll teach you the basics of Purescript. So stay tuned for that!

And you'll also want to make sure your backend skills are up to snuff as well! Read our Haskell Web Series for more details on that! You can also download our Production Checklist!

Appendix: Imports

import Browser exposing (application, UrlRequest(..), Document)
import Browser.Navigation exposing (Key, load, pushUrl)
import Html exposing (button, div, text, a, Html, br)
import Html.Attributes exposing (href)
import Html.Events exposing (onClick)
import String exposing (contains)
import Url exposing (Url, toString)
Read More
James Bowen James Bowen

Elm III: Adding Effects

bridge.jpg

Last week we dug deeper into using Elm. We saw how to build a more complicated web page forming a Todo list application. We learned about the Elm architecture and saw how we could use a couple simple functions to build our page. We laid the groundwork for bringing effects into our system, but didn't use any of these.

This week, we'll add some useful pieces to our Elm skill set. We'll see how to include more effects in our system, specifically randomness and HTTP requests.

To learn more about constructing a backend for your system, you should read up on our Haskell Web Series. It'll teach you things like connecting to a database and making an HTTP server.

Incorporating Effects

Last week, we explored using the element expression to build our application. Unlike sandbox, this allowed us to add commands, which enable side effects. But we didn't use any of commands. Let's examine a couple different effects we can use in our application.

One simple effect we can cause is to get a random number. It might not be obvious from the code we have so far, but we can't actually do it in our Todo application at the moment! Our update function is pure! This means it doesn't have access to IO. What it can do is send commands as part of its output. Commands can trigger messages, and incorporate effects along the way.

Making a Random Task

We're going to add a button to our application. This button will generate a random task name and add it to our list. To start with, we'll add a new message type to process:

type TodoListMessage =
  AddedTodo Todo |
  FinishedTodo Todo |
  UpdatedNewTodo (Maybe Todo) |
  AddRandomTodo

Now here's the HTML element that will send the new message. We can add it to the list of elements in our view:

randomTaskButton : Html TodoListMessage
randomTaskButton = button [onClick AddRandomTodo] [text "Random"]

Now we need to add our new message to our update function. We need a case for it:

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  …
  AddRandomTodo ->
    (TodoListState { todoList = todoList, newTodo = newTodo}, …)

So for the first time, we're going to fill in the Cmd element! To generate randomness, we need the generate function from the Random module.

generate : (a -> msg) -> Generator a -> Cmd msg

We need two arguments to use this. The second argument is a random generator on a particular type a. The first argument then is a function from this type to our message. In our case, we'll want to generate a String. We'll use some functionality from the package elm-community/random-extra. See Random.String and Random.Char for details. Our strings will be 10 letters long and use only lowercase.

genString : Generator String
genString = string 10 lowerCaseLatin

Now we can easily convert this to a new message. We generate the string, and then add it as a Todo:

addTaskMsg : String -> TodoListMessage
addTaskMsg name = AddedTodo (Todo {todoName = name})

Now we can plug these into our update function, and we have our functioning random command!

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  …
  AddRandomTodo ->
    (..., generate addTaskMsg genString)

Now clicking the random button will make a random task and add it to our list!

Sending an HTTP Request

A more complicated effect we can add is to send an HTTP request. We'll be using the Http library from Elm. Whenever we complete a task, we'll send a request to some endpoint that contains the task's name within its body.

We'll hook into our current action for FinishedTodo. Currently, this returns the None command along with its update. We'll make it send a command that will trigger a post request. This post request will, in turn, hook into another message type we'll make for handling the response.

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  …
  (FinishedTodo doneTodo) ->
    (..., postFinishedTodo doneTodo)
  ReceivedFinishedResponse -> ...

postFinishedTodo : Todo -> Cmd TodoListMessage
postFinishedTodo = ...

We create HTTP commands using the send function. It takes two parameters:

send : (Result Error a -> msg) -> Request a -> Cmd Msg

The first of these is a function interpreting the server response and giving us a new message to send. The second is a request expecting a result of some type a. Let's plot out our code skeleton a little more for these parameters. We'll imagine we're getting back a String for our response, but it doesn't matter. We'll send the same message regardless:

postFinishedTodo : Todo -> Cmd TodoListMessage
postFinishedTodo todo = send interpretResponse (postRequest todo)

interpretResponse : Result Error String -> TodoListMessage
interpretResposne _ = ReceivedFinishedResponse

postRequest : Todo -> Request String
postRequest = ...

Now all we need is to create our post request using the post function:

post : String -> Body -> Decoder a -> Request a

Now we've got three more parameters to fill in. The first of these is the URL we're sending the request to. The second is our body. The third is a decoder for the response. Our decoder will be Json.Decode.string, a library function. We'll imagine we are running a local server for the URL.

postRequest : Todo -> Request String
postRequest todo = post "localhost:8081/api/finish" … Json.Decode.string

All we need to do now is encode the Todo in the post request body. This is straightforward. We'll use Json.Encode.object, which takes a list of tuples. Then we'll use the string encoder on the todo name.

jsonEncTodo : Todo -> Value
jsonEncTodo (Todo todo) = Json.Encode.object
  [ ("todoName", Json.encode.string todo.todoName) ]

Now all we have to do is use this encoder together with the jsonBody function. And then we're done!

postRequest : Todo -> Request String
postRequest todo = post
  "localhost:8081/api/finish"
  (jsonBody (jsonEncTodo todo))
  Json.Decode.string

As a reminder, most of the types and helper functions from this last part come from the HTTP Library for Elm. We could then further process the response in our interpretResponse function. If we get an error, we could send a different message. Either way, we can ultimately do more updates in our update function.

Conclusion

This concludes part 3 of our series on Elm! We took a look at a few nifty ways to add extra effects to our Elm projects. We saw how to introduce randomness into our Elm project, and then how to send HTTP requests. Next week, we'll explore navigation, a vital part of any web application. We'll see how the Elm architecture supports a multi-page application. Then we'll see how to move between the different pages efficiently, without needing to reload every bit of our Elm code each time.

Now that you know how to write a functional frontend, you should learn more about the backend! Read our Haskell Web Series for some tutorials on how to do this. You can also download our Production Checklist for some more ideas!

Read More
James Bowen James Bowen

Elm II: Making a Todo List!

todo_list.png

Last week we learned about the basics of Elm. Elm is a functional language you can use for front-end web development. Its syntax is very close to Haskell. Though as we explored, it lacks a few key language features.

This week, we're going to make a simple Todo List application to show a bit more about how Elm works. We'll see how to apply the basics we learned, and take things a bit further.

But a front-end isn't much use without a back-end! Take a look at our Haskell Web Series to learn some cool libraries for a Haskell back-end!

Todo Types

Before we get started, let's define our types. We'll have a basic Todo type, with a string for its name. We'll also make a type for the state of our form. This includes a list of our items as well as a "Todo in Progress", containing the text in the form:

module Types exposing
  ( Todo(..)
  , TodoListState(..)
  , TodoListMessage(..)
  )

type Todo = Todo 
  { todoName : String }

type TodoListState = TodoListState
  { todoList : List Todo
  , newTodo : Maybe Todo
  }

We also want to define a message type. These are the messages we'll send from our view to update our model.

type TodoListMessage =
  AddedTodo Todo |
  FinishedTodo Todo |
  UpdatedNewTodo (Maybe Todo)

Elm's Architecture

Now let's review how Elm's architecture works. Last week we described our program using the sandbox function. This simple function takes three inputs. It took an initial state (we were using a basic Int), an update function, and a view function. The update function took a Message and our existing model and returned the updated model. The view function took our model and rendered it in HTML. The resulting type of the view was Html Message. You should read this type as, "rendered HTML that can send messages of type Message". The resulting type of this expression is a Program, parameterized by our model and message type.

sandbox :
  { init : model
  , update : msg -> model -> model
  , view : model -> Html msg
  }
  -> Program () model msg

A sandbox program though doesn't allow us to communicate with the outside world very much! In other words, there's no IO, except for rendering the DOM! So there a few more advanced functions we can use to create a Program. For a normal application, you'll want to use the application function seen here. For the single page example we'll do this week, we can pretty much get away with sandbox. But we'll show how to use the element function instead to get at least some effects into our system. The element function looks a lot like sandbox, with a few changes:

element :
  { init : flags -> (model, Cmd msg)
  , view : model -> Html msg
  , update : msg -> model -> (model, Cmd msg)
  , subscriptions : model -> Sub msg
  }
  -> Program flags model msg

Once again, we have functions for init, view, and update. But a couple signatures are a little different. Our init function now takes program flags. We won't use these. But they allow you to embed your Elm project within a larger Javascript project. The flags are information passed from Javascript into your Elm program.

Using init also produces both a model and a Cmd element. This would allow us to run "commands" when initializing our application. You can think of these commands as side effects, and they can also produce our message type.

Another change we see is that the update function can also produce commands as well as the new model. Finally, we have this last element subscriptions. This allows us to subscribe to outside events like clock ticks and HTTP requests. We'll see more of this next week. For now, let's lay out the skeleton of our application and get all the type signatures down. (See the appendix for an imports list).

main : Program () TodoListState TodoListMessage
main = Browser.element 
  { init = todoInit
  , update = todoUpdate
  , view = todoView
  , subscriptions = todoSubscriptions
  }

todoInit : () -> (TodoListState, Cmd TodoListMessage)

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)

todoView : TodoListState -> Html TodoListMessage

todoSubscriptions : TodoListState -> Sub TodoListMessage

Initializing our program is easy enough. We'll ignore the flags and return a state that has no tasks and Nothing for the task in progress. We'll return Cmd.none, indicating that initializing our state has no effects. We'll also fill in Sub.none for the subscriptions.

todoInit : () -> (TodoListState, Cmd TodoListMessage)
todoInit _ = 
  let st = TodoListState { todoList = [], newTodo = Nothing }
  in (st, Cmd.none)

todoSubscriptions : TodoListState -> Sub TodoListMessage
todoSubscriptions _ = Sub.none

Filling in the View

Now for our view, we'll take our basic model components and turn them into HTML. When we have a list of Todo elements, we'll display them in an ordered list. We'll have a list item for each of them. This item will state the name of the item and give a "Done" button. Clicking the button allows us to send a message for finishing that Todo:

todoItem : Todo -> Html TodoListMessage
todoItem (Todo todo) = li [] 
  [ text todo.todoName
  , button [onClick (FinishedTodo (Todo todo))] [text "Done"]
  ]

Now let's put together the input form for adding a Todo. First, we'll determine what value is in the input and whether to disable the done button. Then we'll define a function for turning the input string into a new Todo item. This will send the message for changing the new Todo.

todoForm : Maybe Todo -> Html TodoListMessage
todoForm maybeTodo = 
  let (value_, isEnabled_) = case maybeTodo of
                              Nothing -> ("", False)
                              Just (Todo t) -> (t.todoName, True)
      changeTodo newString = case newString of
                               "" -> UpdatedNewTodo Nothing
                               s -> UpdatedNewTodo (Just (Todo { todoName = s }))
  in ...

Now, we'll make the HTML for the form. The input element itself will tie into our onChange function that will update our state. The "Add" button will send the message for adding the new Todo.

todoForm : Maybe Todo -> Html TodoListMessage
todoForm maybeTodo = 
  let (value_, isEnabled_) = ...
      changeTodo newString = ...
  in div []
       [ input [value value_, onInput changeTodo] []
       , button [disabled (not isEnabled_), onClick (AddedTodo (Todo {todoName = value_}))] 
          [text "Add"]
       ]

We can then pull together all our view code in the view function. We have our list of Todos, and then add the form.

todoView : TodoListState -> Html TodoListMessage
todoView (TodoListState { todoList, newTodo }) = div []
  [ ol [] (List.map todoItem todoList)
  , todoForm newTodo
  ]

Updating the Model

The last thing we need is to write out our update function. All this does is process a message and update the state accordingly. We need three cases:

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  (AddedTodo newTodo_) -> ...
  (FinishedTodo doneTodo) -> ...
  (UpdatedNewTodo newTodo_) -> ...

And each of these cases is pretty straightforward. For adding a Todo, we'll append it at the front of our list:

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  (AddedTodo newTodo_) ->
    let st = TodoListState { todoList = newTodo_ :: todoList
                           , newTodo = Nothing
                           }
  (FinishedTodo doneTodo) -> ...
  (UpdatedNewTodo newTodo_) -> ...

When we've finished a Todo, we'll remove it from our list by filtering on the name being equal.

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  (AddedTodo newTodo_) -> ...
  (FinishedTodo doneTodo) ->
    let st = TodoListState { todoList = List.filter (todosNotEqual doneTodo) todoList
                           , newTodo = newTodo
                           }
    in (st, Cmd.none)
  (UpdatedNewTodo newTodo_) -> ..

todosNotEqual : Todo -> Todo -> Bool
todosNotEqual (Todo t1) (Todo t2) = t1.todoName /= t2.todoName

And updating the new todo is the easiest of all! All we need to do is replace it in the state.

todoUpdate : TodoListMessage -> TodoListState -> (TodoListState, Cmd TodoListMessage)
todoUpdate msg (TodoListState { todoList, newTodo}) = case msg of
  (AddedTodo newTodo_) -> ...
  (FinishedTodo doneTodo) -> ...
  (UpdatedNewTodo newTodo_) -> ..
    (TodoListState { todoList = todoList, newTodo = newTodo_ }, Cmd.none)

And with that we're done! We have a rudimentary program for our Todo list.

Conclusion

This wraps up our basic Todo application! There are still a few more things we should learn moving on from Elm. Next week, we'll see how to implement some important types of effects any site will need. We'll see an intro to Elm's effect system, and use it to send HTTP requests.

For some more ideas on building cool products in Haskell, take a look at our Production Checklist. It goes over some libraries for many topics, including databases and parsing!

Appendix: Imports

import Browser
import Html exposing (Html, button, div, text, ol, li, input)
import Html.Attributes exposing (value, disabled)
import Html.Events exposing (onClick, onInput)
Read More
James Bowen James Bowen

Elm: Functional Frontend!

elm_logo.png

Our Haskell Web Series covers a lot of cool libraries you can use when making a web app. But one thing we haven't covered yet on this blog is using Haskell for front-end web development. There are many libraries and frameworks out there. Yesod and Snap come to mind. Another option is Reflex FRP which uses GHCJS under the hood.

But for this new series I've decided to take a different approach. For the next few weeks we're going to be looking at Elm!

I love Elm for a few reasons. Elm builds on my strong belief that we can take the principles of functional programming and put them to practical use. The language is no-nonsense and the documentation is quite good. Elm has a few syntactic quirks . It also lacks a few key Haskell features. And yet, we can still do a lot with it.

This week we'll look at basic installation, differences, and usage. Next week, we'll compose a simple Todo application in Elm. This will give us a feel for how we architect our Elm applications. We'll wrap up by exploring how to add more effects to our app, and how to integrate Elm types with Haskell.

Front-end is, of course, only part of the story. To learn more about using Haskell for backend web, check out our Haskell Web Series! You can also download our Production Checklist for more ideas!

Basic Setup

As with any language, there will be some setup involved in getting Elm onto our machine for the first time. For Windows and Mac, you can run the installer program provided here. There are separate instructions for Linux, but they're straightforward enough. You fetch the binary, tar it, and move to your bin.

Once we have the elm executable installed, we can get going. When you've used enough package management programs, the process gets easier to understand. The elm command has a few fundamental things in common with stack and npm.

First, we can run elm init to create a new project. This will make a src folder for us as well as an elm.json file. This JSON file is comparable to a .cabal file or package.json for Node.js. It's where we'll specify all our different package dependencies. The default version of this will provide most of your basic web packages. Then we'll make our .elm source files in /src.

Running a Basic Page

Elm development looks different from most normal Javascript systems I've worked with. While we're writing our code, we don't need to specify a specific entry point to our application. Every file we make is a potential web page we can view. So here's how we can start off with the simplest possible application:

import Browser
import HTML exposing (Html, div, text)

type Message = Message

main : Program () Int Message
main =
  Browser.sandbox { init = 0, update = update, view = view }

update : Message -> Int -> Int
update _ x = x

view : Int -> Html Message
view _ = div [] [text "Hello World!"]

Elm uses a model/view/controller system. We define our program in the main function. Our Program type has three parameters. The first relates to flags we can pass to our program. We'll ignore those for now. The second is the model type for our program. We'll start with a simple integer. Then the final type is a message. Our view will cause updates by sending messages of this type. The sandbox function means our program is simple, and has no side effects. Aside from passing an initial state, we also pass an update function and a view function.

The update function allows us to take a new message and change our model if necessary. Then the view is a function that takes our model and determines the HTML components. You can read the type of view as "an HTML component that sends messages of type Message.

We can run the elm-reactor command and point our browser at localhost:8000. This takes us to a dashboard where we can examine any file we want. We'll only want to look at the ones with a main function. Then we'll see our simple page with the div on the screen. (It strangely spins if we select a pure library file).

As per the Elm tutorial we can make this more interesting by using the Int in our model. We'll change our Message type so that it can either represent an Increment or a Decrement. Then our update function will change the model based on the message.

type Message = Increment | Decrement

update : Message -> Int -> Int
update msg model = case msg of
  Increment -> model + 1
  Decrement -> model - 1

view : Int -> Html Message
view model = div [] [String.fromInt model]

As a last change, we'll add + and - buttons to our interface. These will allow us to send the Increment and Decrement messages to our type.

view model = div []
  [ button [onClick Decrement] [text "-"]
  , div [] [ text (String.fromInt model) ]
  , button [onClick Increment] [text "+"]
  ]

Now we have an interface where we can press each button and the number on the screen will change! That's our basic application!

The Make Command

The elm reactor command builds up a dummy interface for us to use and examine our pages. But our ultimate goal is to make it so we can generate HTML and Javascript from our elm code. We would then export these assets so our back-end could serve them as resources. We can do this with the elm make command. Here's a sample:

elm make Main.elm --output=main.html

We'll want to use scripting to pull all these elements together and dump them in an assets folder. We'll get some experience with this in a couple weeks when we put together a full Elm + Haskell project.

Differences From Haskell

There are a few syntactic gotchas when comparing Elm to Haskell. We won't cover them all, but here are the basics.

We can already see that import and module syntax is a little different. We use the exposing keyword in an import definition to pick out specific expressions we want from that module.

import HTML exposing (Html, div, text)

import Types exposing (Message(..))

When we define our own module, we will also use the exposing keyword in place of where in the module definition:

module Types exposing
  (Message(..))

type Message = Increment | Decrement

We can also see that Elm uses type where we would use data in Haskell. If we want a type synonym, Elm offers the type alias combination:

type alias Count = Int

As you can see from the type operators above, Elm reverses the : operator and ::. A single colon refers to a type signature. Double colons refer to list appending:

myNumber : Int
myNumber = 5

myList : [Int]
myList = 5 :: [2, 3]

Elm is also missing some of the nicer syntax elements of Haskell. For instance, Elm lacks pattern matching on functions and guards. Elm also does not have where clauses. Only case and let statements exist. And instead of the . operator for function composition, you would use <<. data-preserve-html-node="true" Here are a few examples of these points:

isBigNumber : Int -> Bool
isBigNumber x = let forComparison = 5 in x > forComparison

findSmallNumbers : List Int -> List Int
findSmallNumbers numbers = List.filter (not << isBigNumber) numbers

As a last note in this section, Elm is strictly evaluated. Elm compiles to Javascript so it can run in browsers. And it's much easier to generate sensible Javascript with a strict language.

Elm Records

Another key difference with Elm is how record syntax works. It Elm, a "record" is a specific type. These simulation Javascript objects. In this example, we define a type synonym for a record. While we don't have pattern matching in general, we can use pattern matching on records:

type alias Point2D =
  { x: Float
  , y: Float
  }

sumOfPoint : Point2D -> Float
sumOfPoint {x, y} = x + y

To make our code feel more like Javascript, we can use the . operator to access records in different ways. We can either use the Javascript like syntax, or use the period and our field name as a normal function.

point1 : Point2D
point1 = {x = 5.0, y = 6.0}

p1x : Float
p1x = point1.x

p1y : Float
p1y = .y point1

We can also update particular fields of records with ease. This approach scales well to many fields:

newPoint : Point2D
newPoint = { point1 | y = 3.0 }

Typeclasses and Monads

The more controversial differences between Haskell and Elm lie with these two concepts. Elm does not have typeclasses. For a Haskell veteran such as myself, this is a big restriction. Because of this, Elm also lacks do syntax. Remember that do syntax relies upon the idea that the Monad typeclass exists.

There is a reason for these omissions though. The Elm creator wrote an interesting article about it.

His main point is that (unlike me), most Elm users are coming from Javascript rather than Haskell. They tend not to have much background with functional programming and related concepts. So it's not as big a priority for Elm to capture these constructs. So what alternatives are available?

Well when it comes to typeclasses, each type has to come up with its own definition for a function. Let's take the simple example of map. In Haskell, we have the fmap function. It allows us to apply a function over a container, without knowing what the container is:

fmap :: (Functor f) => (a -> b) -> f a -> f b

We can apply this same function whether we have a list or a dictionary. In Elm though, each library has its own map function. So we have to qualify the usage of it:

import List
import Dict

double : List Int -> List Int
double l = List.map (* 2) l

doubleDict : Dict String Int -> Dict String Int
doubleDict d = Dict.map (* 2) d

Instead of monads, Elm uses a function called andThen. This acts a lot like Haskell's >>= operator. We see this pattern more often in object oriented languages like Java. As an example from the documentation, we can see how this works with Maybe.

toInt : String -> Maybe Int

toValidMonth : Int -> Maybe Int
toValidMonth month =
    if month >= 1 && month <= 12
        then Just month
        else Nothing

toMonth : String -> Maybe Int
toMonth rawString =
    toInt rawString `andThen` toValidMonth

So Elm doesn't give us quite as much functional power as we have in Haskell. That said, Elm is a front-end language first. It expresses how to display our data and how we bring components together. If we need complex functional elements, we can use Haskell and put that on the back-end.

Conclusion

We'll stop there for now. Next week we'll expand our understanding of Elm by writing a more complicated program. We'll write a simple Todo list application and see Elm's architecture in action.

To hear more from Monday Morning Haskell, make sure to Subscribe to our newsletter! That will also give you access to our awesome Resources page!

Read More
James Bowen James Bowen

MMH Blog Archive!

We’re taking a quick breather this week on Monday Morning Haskell to announce a new website feature! Several readers have suggested it, so we now have the archive! You can scroll through the page and find all the different articles we’ve done over the last couple years. We’ve also implemented a tag system, so you can search for articles on specific topics. Our current tags list includes the following:

  1. Beginners
  2. Production
  3. Library
  4. Open Source
  5. AI
  6. Non-Technical
  7. Announcements

Take a look and let us know what you think!

Community Callouts

I’d also like to take a moment to call out some of the other excellent Haskell blog work going on in the community. I’ll give a special shoutout to Anuj Agarwal who listed us in his top 10 Haskell blogs. You can check out some of the other folks on that list as well. In particular, I enjoy Neil Mitchell’s blog as well as the material on Haskell Reddit and Haskell Weekly.

Wrapup

Next week we’ll start our exploration of frontend web development. This will involve looking at a couple other functional languages. Over the course of this series, we’ll explore both Elm and Purescript. We’ll see how both languages are similar to and different from Haskell. We’ll also see some ways we can integrate them with our Haskell code. You won’t want to miss this series!

And remember if you’re new to the community there are plenty of good resources out there to help you get off the ground! Take a look at our Beginners Checklist and our Liftoff Series for tips on getting started!

Read More
James Bowen James Bowen

Deeper Stack Knowledge

stack_of_books.png

This week we'll look at another way we can "level up" our Haskell skills. We'll look at some of the details around how Stack determines package versions. This will help us explain the nuances of when you need "extra deps" and why. We'll also explore some ways to bring in non-standard Haskell code.

But of course you need to know the basics before you can really start going! So if you've never used Stack before, take our free Stack mini-course!

Adding Libraries (Basics)

When I'm writing a small project for one of these articles, I don't have to think much about library versions. Generally, anything recent is fine. Let's take a simple example using Servant. I can start the project with stack new ServantExample. Then I can add servant as a dependency for my library by modifying the .cabal file:

build-depends:
  servant

When we run stack build, it'll install a whole bunch of dependencies for our project. We can do stack ls dependencies (or stack list-dependencies if you're on an older version of Stack). We'll see a list with many libraries, because servant has a lot of dependencies. In this article, we'll explore a few questions. How does Stack know which versions of these to get? Is it always finding the latest version? What happens if we need a different version? Do we ever need to look elsewhere?

Well first, we can, if we want, specify manual constraints on libraries within the .cabal file. To start, we can observe that Stack has downloaded servant-0.14.1 and directory-1.3.1.5 for our program. What happens if we add constraints like so:

build-depends:
  servant >= 0.14.1
  directory <= 1.2.0.0

We'll find that we can't build, because no version of directory matches our constraints. The error message will suggest adding it to extra dependencies in stack.yaml (we'll talk about this later). But this will cause dependency conflicts. So how do we avoid these conflicts? To understand this, let's examine the concept of resolvers.

Resolvers

Resolvers are one of the big things that separate Stack from Cabal on its own. A resolver is a series of libraries that have no conflicts among their dependencies. Each resolver has a version number.

If we go into our stack.yaml file, we'll see that we have a field that relates to the lts version number of our resolver. When we invoked the stack new command, this chose the latest lts, or "Long-Term Support" resolver. If there's an issue with this resolver, we can ask the great people at Stackage what's going wrong. At the time of writing, this version was 12.9:

resolver: lts-12.9

There are other kinds of resolvers we can use, as the comments in our auto-generated file will tell us. There are nightly builds, and resolvers that map to particular versions of GHC.

But let's stick to the idea of lts resolvers for now. A resolver gives us a big set of packages that work together and have no dependency conflicts. This prevents some of the more annoying issues that can come along when we try to have a lot of libraries.

For lts resolvers, the package directory lives on Stackage, and we can examine it if we like. We can see, for instance, on the site that there's a page dedicated to listing everything for lts-12.9. And we can compare the library versions on this site to what we've already got in our directory. And we'll see they're the same! For example, it lists version 0.14.1 of servant and version 1.3.1.5 of directory.

So when we write our cabal file, we don't need to list version constraints on our packages. Stack will find the matching version from the resolver. Then we'll know that we can meet dependency constraints with other packages there!

Resolvers and GHC

Now, our resolver has to work with whatever compiler we use. Each lts resolver links to a specific GHC version. We can't use our lts-12.9 resolver with GHC 7.10.3, because many of the library versions it links to only work for GHC 8+. If we are intent on using an older version of GHC, we'll have to use a resolver version that corresponds to it.

We can also get this kind of information by going onto the Stackage website. Let's lookup GHC 7.10.3, and we'll find that the last resolver for that was 6.35. Given this information, we can then set the resolver in our stack.yaml file. We'll then run stack build again. We'll find it uses different versions of certain packages for servant! For instance, servant itself is now version 0.7.1. Meanwhile the dependency on directory is gone entirely!

Extra Dependencies

Now let's suppose we don't want to write our program using the Servant library. Let's suppose we want to use Spock instead, like we did recently. When we first try to add Spock as a dependency in our .cabal file, we'll actually get an error. It looks like this:

In the dependencies for SpockExample-0.1.0.0:                                                                                     Spock needed, but the stack configuration has no specified version  (latest matching version                                                is 0.13.0.0)

Stack then recommends we add Spock as an extra dependency in stack.yaml. Why do we need to do this? (Get ready for some rhyming). We can't depend on Stackage to contain every package that lives on Hackage. After all, pretty much anyone can publish to Hackage! As you add more libraries, it's more work to ensure they are conflict free.

Often, updates will introduce new conflicts. And often, the original library's authors are no longer maintaining the package. This means they won't release an update to fix it. Thus, the package gets dropped from the latest resolver. And many packages aren't used enough to justify the effort of keeping them in the resolver.

But this is OK! We can still introduce Spock into our Stack program. We'll go to our stack.yaml file and add it under our extra-deps part of the file:

extra-deps:
- Spock-0.13.0.0

This however, leads us to more dependencies we must add:

Spock-core-0.13.0.0                                                                                                - reroute-0.5.0.0

After adding these to our extra packages, everything will build!

Unfortunately, it can be an arduous process to slog through ALL the extra deps you need as a result of one library. You can use the stack solver command to list them all. If you add the --update-config flag, it will even add them to your file for you! At the time of writing though, there seems to be a bug in this feature, as it fails whenever I try to use it on Spock.

Be warned though. Extra dependencies have no guards against conflicts. Packages within the resolver still won't conflict. But every new extra package you introduce brings some more risk. Sometimes you'll need to play version tetris to get things to work. Sometimes you may need to try a different library altogether.

Different Kinds of Packages

Changing gears a bit, the stack.yaml file allows you to specify different packages within your project. Each package is a self-contained Haskell unit containing its own .cabal file. The auto-generated stack.yaml always has this simple packages section:

packages:
- .

One option for what to use as a package is a local directory, relative to wherever the stack.yaml file lives. So the default is the directory itself. But at a certain point, it might make sense for you to break your project into more pieces. This way, they can be independently maintained and tested. You might have sub-packages that look a bit like this:

packages:
- ‘./my-project-core'
- ‘./my-project-server'
- ‘./my-project-client'
- ‘./my-project-db'

You can use other options besides local directories as well. If you have a package stored on a remote server as a tar file, you can reference that:

packages:
- ‘.'
- https://mysite.com/my-project-client-1.0.0.tar.gz

Stack will download the code as necessary and build it. The other common option you'll use is a Github repository. You'll often want to reference a specific commit hash to use. Here's what that would look like:

packages:
- ‘.'
- location:
  git: https://github.com/my-user/my-project.git
  commit: b7deadc0def7128384

This technique is especially useful when you need to fix bugs on a dependency. The normal release process on a library can take a long time. And the library's maintainers might not have time to review your fix. But you can supply your own code and then reference it through Github. Say you want to fix something in Servant. You can make your own fork of the repository, fix the bug, and use that as a package in your project:

packages:
- ‘.'
- location:
  git: https://github.com/jhb563/servant.git
  commit: b7deadc0def7128384

Other Fields

That covers most of what you'll want to do in the Stack file. There are other fields. For instance, the flags field allows you to override certain build flags for packages. Here's an example covered in the docs. The yackage package typically builds with the flag upload. If you're using it as a package or a dependency, you can set this flag in the stack.yaml file:

flags:
  yackage:
    upload: true

But if you want to set it to false, you can do this as well by flipping the flag there.

You can also use the extra-package-dbs field. This is necessary if you need a specialized set of libraries that aren't on Hackage. You can create your own local database if you like and store modified versions of packages there. This feature is pretty advanced so it's unlikely most of you will need it.

Conclusion

Using Stack is easy at a basic level. For starter projects, you probably won't have to change the stack.yaml file much at all. At most you'll add a couple extra dependencies. But as you make more complicated things, you'll need some extra features. You'll need to know how Stack resolves conflicts and how you can bring in code from different places. These small extra features are important to your growth as a Haskell developer.

If you've never learned the basics of Stack, you're in luck! You can take our free Stack mini-course! If you've never learned Haskell at all, nows the time to start! Download our Beginners Checklist to start your journey!

Read More
James Bowen James Bowen

Stuck in the Middle: Adding Middleware to a Servant Server

middleware.png

This week we're going to take a quick look at a small problem. On a web server, the core of our functionality lies in the different endpoints. We define different behavior for different requests. Sometimes though, there are behaviors we want to add for all requests or all responses on our web server.

For these kinds of behaviors, we'll want to use "middleware". This week, we'll examine how we can represent middleware at a type level with the Network.Wai library. We'll see how it works in conjunction with a Servant server.

For a more in depth look at Haskell and web development, read our Haskell Web Series! You can also download our Production Checklist for some more ideas!

The Problem

As I was running some test code from a frontend client, I found certain requests returned network errors. This seemed odd, but after some research, I found the cause of the errors was the access control policy. Since this project was a pure debugging effort, I wanted to allow requests from any origin. But, this requires modifying every response that goes out from our server. How do we do this?

The answer is by attaching "middleware" to our application. This term will be familiar to those of you who do a lot of web programming in Javascript. Middleware is a layer between our server and incoming requests or outgoing responses. It can do something to change all the requests, or all the responses.

One common form of middleware on Javascript projects is the body-parser package. In Node.js applications, it makes it a lot easier to get information out of a request body. It acts on every request sent to your server, and parses out data from, say, a post request. Then it's much easier and intuitive to access that data. In our server, we'll put a layer between it and the outgoing responses to change the access policy.

Servant Refresher

For a moment, let's recall the basics of running a server using the Servant library. We'll specify an API with a single endpoint, a handler for that API, and we'll serve it:

type AppAPI = "api" :> "hello" :> Get ‘[JSON] String

appAPI :: Proxy AppAPI
appAPI = Proxy :: Proxy AppAPI

helloHandler :: Handler String
helloHandler = return "Hello World!"

apiHandler :: Server AppAPI
apiHandler = helloHandler

runServer :: IO ()
runServer = do
  let port = 8081
  run port (serve appAPI apiHandler)

To understand things a little better, let's examine the types from the Network.Wai library. The main output of all our Servant code is an Application. The serve function returns us this type. Then we can use run to run our application from a particular port.

serve :: Proxy api -> Server api -> Application

run :: Port -> Application -> IO ()

And it's also useful to know exactly what an Application is in the Wai library. This is actually a type synonym. Here is its real identity:

type Application = 
  Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

This gives us an excellent functional definition of what a server application does. An application is a function taking two parameters. The first is an HTTP request. The second is a function from an HTTP response to a ResponseReceived item. We don't need to understand too much about this type. It indicates how the client has received our response. We'll use the function though to hijack the responses we return.

Adding Middleware

Above, we described middleware using some vague terminology. But now that we can see the types, it's easier to describe! Middleware is a function taking one application and returning another. The new application has slightly different behavior. This type alias exists within the Network.Wai library already:

type Middleware = Application -> Application

We will, of course, use the core functionality of the first application. But we'll apply something a little bit different to the response.

In our case, we'll want to allow all origins, so we'll add this field as a header to the response:

"Access-Control-Allow-Origin" : "*"

So let's start defining our middleware expression:

addAllOriginsMiddleware :: Application -> Application
addAllOriginsMiddleware = ...

And here's how we would add this middleware onto our server:

run port (addAllOriginsMiddleware (serve appAPI apiHandler))

Defining the Middleware

Now the type we've laid out for our middleware is pretty complicated. But we can start working through how we'll actually hook into the existing app. We'll take the base application as an argument, and then we want to return a function with two parameters:

addAllOriginsMiddleware :: Application -> Application
addAllOriginsMiddleware baseApp = \req responseFunc -> ...

Remember that baseApp is also a function taking a request and a response function. So we can partially apply it to our new application's request. We'll then have to define the new response function for the final app:

addAllOriginsMiddleware :: Application -> Application
addAllOriginsMiddleware baseApp = \req responseFunc -> 
  baseApp req newResponseFunc
    where
      newResponseFunc :: Response -> IO ResponseReceived
      newResponseFunc = ...

We want to take the existing response from the base application and change it a little bit. So let's define a function to do this:

addOriginsAllowed :: Response -> Response
addOriginsAllowed = ...

We can now hook this function into our middleware using function composition! Remember how we can use the composition operator in this circumstance:

-- Function composition, on our types:
(.) :: 
  (Response -> IO ResponseReceived) ->
  (Response -> Response) ->
  (Response -> IO ResponseReceived)

addAllOriginsMiddleware :: Application -> Application
addAllOriginsMiddleware baseApp = \req responseFunc -> baseApp req newResponseFunc
  where
    newResponseFunc :: Response -> IO ResponseReceived
    newResponseFunc = responseFunc . addOriginsAllowed

And we're almost done!

Modifying the Header

All we have to do now is handle the dirty work of appending our new header to the response. We'll use the mapResponseHeaders function. It treats our headers as a list, and applies a function to change that list. All we do is append our new key/value pair!

addOriginsAllowed :: Response -> Response
addOriginsAllowed = mapResponseHeaders $
  (:) ("Access-Control-Allow-Origin", "*")

Now our application modifies every header! We're done!

Conclusion

This week we went into the guts of Haskell's HTTP libraries a little bit. We used a type driven development approach to define what middleware is for a web server. Then we saw how to add it to our application. We saw how to add a new header to every response going out from our server.

For a more in depth tutorial on how to use Servant and combine it with other services, read our Haskell Web Series! To learn about more libraries you can use in your apps, download our Production Checklist!

Read More
James Bowen James Bowen

Spock II: Databases and Sessions!

Spock.png

Last week we learned the basics of the the Spock library. We saw how to set up some simple routes. Like Servant, there's a bit of dependent-type machinery with routing. But we didn't need to learn any complex operators. We just needed to match up the number of arguments to our routes. We also saw how to use an application state to persist some data between requests.

This week, we'll add a couple more complex features to our Spock application. First we'll connect to a database. Second, we'll use sessions to keep track of users.

For some more examples of useful Haskell libraries, check out our Production Checklist!

Adding a Database

Last week, we added some global application state. Even with this improvement, our vistor count doesn't persist. When we reset the server, everything goes away, and our users will see a different number. We can change this by adding a database connection to our server. We'll follow the Spock tutorial example and connect to an SQLite database by using Persistent.

If you haven't used Persistent before, take a look at this tutorial in our Haskell Web series! You can also look at our sample code on Github for any of the boilerplate you might be missing. Here's the super simple schema we'll use. Remember that Persistent will give us an auto-incrementing primary key.

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
  NameEntry json
    name Text
    deriving Show
|]

Spock expects us to use a pool of connections to our database when we use it. So let's create one to an SQLite file using createSqlitePool. We need to run this from a logging monad. While we're at it, we can migrate our database from the main startup function. This ensures we're using an up-to-date schema:

import Database.Persist.Sqlite (createSqlitePool)

...

main :: IO ()
main = do
  ref <- newIORef M.empty
  pool <- runStdoutLoggingT $ createSqlitePool "spock_example.db" 5
  runStdoutLoggingT $ runSqlPool (runMigration migrateAll) pool
  ...

Now that we've created this pool, we can pass that to our configuration. We'll use the PCPool constructor. We're now using an SQLBackend for our server, so we'll also have to change the type of our router to reflect this:

main :: IO ()
main = do
  …
  spockConfig <-
    defaultSpockCfg EmptySession (PCPool pool) (AppState ref)
  runSpock 8080 (spock spockConfig app)

app :: SpockM SqlBackend MySession AppState ()
app = ...

Now we want to update our route action to access the database instead of this map. But first, we'll write a helper function that will allow us to call any SQL action from within our SpockM monad. It looks like this:

runSQL :: (HasSpock m, SpockConn m ~ SqlBackend)
  => SqlPersistT (LoggingT IO) a -> m a
runSQL action = runQuery $ \conn -> 
  runStdoutLoggingT $ runSqlConn action conn

At the core of this is the runQuery function from the Spock library. It works since our router now uses SpockM SqlBackend instead of SpockM (). Now let's write a couple SQL actions we can use. We'll have one performing a lookup by name, and returning the Key of the first entry that matches, if one exists. Then we'll also have one that will insert a new name and return its key.

fetchByName
  :: T.Text
  -> SqlPersistT (LoggingT IO) (Maybe Int64)
fetchByName name = (fmap (fromSqlKey . entityKey)) <$> 
  (listToMaybe <$> selectList [NameEntryName ==. name] [])

insertAndReturnKey
  :: T.Text
  -> SqlPersistT (LoggingT IO) Int64
insertAndReturnKey name = fromSqlKey <$> insert (NameEntry name)

Now we can use these functions instead of our map!

app :: SpockM SqlBackend MySession AppState ()
app = do
  get root $ text "Hello World!"
  get ("hello" <//> var) $ \name -> do
    existingKeyMaybe <- runSQL $ fetchByName name
    visitorNumber <- case existingKeyMaybe of
      Nothing -> runSQL $ insertAndReturnKey name
      Just i -> return i
    text ("Hello " <> name <> ", you are visitor number " <> 
      T.pack (show visitorNumber))

And voila! We can shutdown our server between runs, and we'll preserve the visitors we've seen!

Tracking Users

Now, using a route to identify our users isn't what we want to do. Anyone can visit any route after all! So for the last modification to the server, we're going to add a small "login" functionality. We'll use the App's session to track what user is currently visiting. Our new flow will look like this:

  1. We'll change our entry route to /hello.
  2. If the user visits this, we'll show a field allowing them to enter their name and log in.
  3. Pressing the login button will send a post request to our server. This will update the session to match the session ID with the username.
  4. It will then send the user to the /home page, which will greet them and present a logout button.
  5. If they log out, we'll clear the session.

Note that using the session is different from using the app state map that we had in the first part. We share the app state across everyone who uses our server. But the session will contain user-specific references.

Adding a Session

The first step is to change our session type. Once again, we'll use a IORef wrapper around a map. This time though, we'll use a simple type synonym to simplify things. Here's our type definition and the updated main function.

type MySession = IORef (M.Map T.Text T.Text)

main :: IO ()
main = do
  ref <- newIORef M.empty
  -- Initialize a reference for the session
  sessionRef <- newIORef M.empty
  pool <- runStdoutLoggingT $ createSqlitePool "spock_example.db" 5
  runStdoutLoggingT $ runSqlPool (runMigration migrateAll) pool
  -- Pass that reference!
  spockConfig <-
    defaultSpockCfg sessionRef (PCPool pool) (AppState ref)
  runSpock 8080 (spock spockConfig app)

Updating the Hello Page

Now let's update our "Hello" page. Check out the appendix below for what our helloHTML looks like. It's a "login" form with a username field and a submit button.

-- Notice we use MySession!
app :: SpockM SqlBackend MySession AppState ()
app = do
  get root $ text "Hello World!"
  get "hello" $ html helloHTML
  ...

Now we need to add a handler for the post request to /hello. We'll use the post function instead of get. Now instead of our action taking an argument, we'll extract the post body using the body function. If our application were more complicated, we would want to use a proper library for Form URL encoding and decoding. But for this small example, we'll use a simple helper decodeUsername. You can view this helper in the appendix.

app :: SpockM SqlBackend MySession AppState ()
app = do
  …
  post "hello" $ do
    nameEntry <- decodeUsername <$> body
    ...

Now we want to save this user using our session and then redirect them to the home page. First we'll need to get the session ID and the session itself. We use the functions getSessionId and readSession for this. Then we'll want to update our session by associating the name with the session ID. Finally, we'll redirect to home.

post "hello" $ do
  nameEntry <- decodeUsername <$> body
  sessId <- getSessionId 
  currentSessionRef <- readSession
  liftIO $ modifyIORef' currentSessionRef $
    M.insert sessId (nameEntryName nameEntry)
  redirect "home"

The Home Page

Now on the home page, we'll want to check if we've got a user associated with the session ID. If we do, we'll display some text greeting that user (and also display a logout button). Again, we need to invoke getSessionId and readSession. If we have no user associated with the session, we'll bounce them back to the hello page.

get "home" $ do
  sessId <- getSessionId 
  currentSessionRef <- readSession
  currentSession <- liftIO $ readIORef currentSessionRef
  case M.lookup sessId currentSession of
    Nothing -> redirect "hello"
    Just name -> html $ homeHTML name

The last piece of functionality we need is to "logout". We'll follow the familiar pattern of getting the session ID and session. This time, we'll change the session by clearing the session key. Then we'll redirect the user back to the hello page.

post "logout" $ do
  sessId <- getSessionId 
  currentSessionRef <- readSession
  liftIO $ modifyIORef' currentSessionRef $ M.delete sessId
  redirect "hello"

And now our site tracks our users' sessions! We can access the same page as a different user on different sessions!

Conclusion

This wraps up our exploration of the Spock library! We've done a shallow but wide look at some of the different features Spock has to offer. We saw several different ways to persist information across requests on our server! Connecting to a database is the most important. But using the session is a pretty advanced feature that is quite easy in Spock!

For some more cool examples of Haskell web libraries, take a look at our Web Skills Series! You can also download our Production Checklist for even more ideas!

Appendix - HTML Fragments and Helpers

helloHTML :: T.Text
helloHTML =
  "<html>\
    \<body>\
      \<p>Hello! Please enter your username!\
      \<form action=\"/hello\" method=\"post\">\
        \Username: <input type=\"text\" name=\"username\"><br>\
        \<input type=\"submit\"><br>\
      \</form>\
    \</body>\
  \</html>"

homeHTML :: T.Text -> T.Text
homeHTML name =
  "<html><body><p>Hello " <> name <> 
    "</p>\
    \<form action=\"logout\" method=\"post\">\
      \<input type=\"submit\" name=\"logout_button\"<br>\
    \</form>\
  \</body>\
  \</html>" 

-- Note: 61 -> '=' in ASCII
-- We expect input like "username=christopher"
parseUsername :: B.ByteString -> T.Text
parseUsername input = decodeUtf8 $ B.drop 1 tail_
  where
    tail_ = B.dropWhile (/= 61) input
Read More
James Bowen James Bowen

Simple Web Routing with Spock!

spock_image2.jpg

In our Haskell Web Series, we go over the basics of how we can build a web application with Haskell. That includes using Persistent for our database layer, and Servant for our HTTP layer. But these aren't the only libraries for those tasks in the Haskell ecosystem.

We've already looked at how to use Beam as another potential database library. In these next two articles, we'll examine Spock, another HTTP library. We'll compare it to Servant and see what the different design decisions are. We'll start this week by looking at the basics of routing. We'll also see how to use a global application state to coordinate information on our server. Next week, we'll see how to hook up a database and use sessions.

For some useful libraries, make sure to download our Production Checklist. It will give you some more ideas for libraries you can use even beyond these! Also, you can follow along the code here by looking at our Github repository!

Getting Started

Spock gives us a helpful starting point for making a basic server. We'll begin by taking a look at the starter code on their homepage. Here's our initial adaptation of it:

data MySession = EmptySession
data MyAppState = DummyAppState (IORef Int)

main :: IO ()
main = do
  ref <- newIORef 0
  spockConfig <- defaultSpockCfg EmptySession PCNoDatabase (DummyAppState ref)
  runSpock 8080 (spock spockConfig app)

app :: SpockM () MySession MyAppState ()
app = do
  get root $ text "Hello World!"
  get ("hello" <//> var) $ \name -> do
    (DummyAppState ref) <- getState
    visitorNumber <- liftIO $ atomicModifyIORef' ref $ \i -> (i+1, i+1)
    text ("Hello " <> name <> ", you are visitor number " <> T.pack (show visitorNumber))

In our main function, we initialize an IO ref that we'll use as the only "state" of our application. Then we'll create a configuration object for our server. Last, we'll run our server using our app specification of the actual routes.

The configuration has a few important fields attached to it. For now, we're using dummy values for all these. Our config wants a Session, which we've defined as EmptySession. It also wants some kind of a database, which we'll add later. Finally, it includes an application state, and for now we'll only supply our pointer to an integer. We'll see later how we can add a bit more flavor to each of these parameters. But for the moment, let's dig a bit deeper into the app expression that defines the routing for our Server.

The SpockM Monad

Our router lives in the SpockM monad. We can see this has three different type parameters. Remember the defaultSpockConfig had three comparable arguments! We have the empty session as MySession and the IORef app state as MyAppState. Finally, there's an extra () parameter corresponding to our empty database. (The return value of our router is also ()).

Now each element of this monad is a path component. These path components use HTTP verbs, as you might expect. At the moment, our router only has a couple get routes. The first lies at the root of our path, and outputs Hello World!. The second lies at hello/{name}. It will print a message specifying the input name while keeping track of how many visitors we've had.

Composing Routes

Now let's talk a little bit now about the structure of our router code. The SpockM monad works like a Writer monad. Each action we take adds a new route to the application. In this case, we take two actions, each responding to get requests (we'll see an example of a post request next week).

For any of our HTTP verbs, the first argument will be a representation of the path. On our first route, we use the hard-coded root expression to refer to the / path. For our second expression, we have a couple different components that we combine with <//>.

First, we have a string path component hello. We could combine other strings as well. Let's suppose we wanted the route /api/hello/world. We'd use the expression:

"api" <//> "hello" <//> "world"

In our original code though, the second part of the path is a var. This allows us to substitute information into the path. When we visit /hello/james, we'll be able to get the path component james as a variable. Spock passes this argument to the function we have as the second argument of the get combinator.

This argument has a rather complicated type RouteSpec. We don't need to go into the details here. But the simplest thing we can return is some raw text by using the text combinator. (We could also use html if we have our own template). We conclude both our route definitions by doing this.

Notice that the expression for our first route has no parameters, while the second has one parameter. As you might guess, the parameter in the second route refers to the variable we can pull out of the path thanks to var. We have the same number of var elements in the path as we do arguments to the function. Spock uses dependent types to ensure these match.

Using the App State

Now that we know the basics, let's start using some of Spock's more advanced features. This week, we'll see how to use the App State.

Currently, we bump the visitor count each time we visit the route with a name, even if that name is the same. So visiting /hello/michael the first time results in:

Hello michael, you are visitor number 1

Then we'll visit again and see:

Hello michael, you are visitor number 2

Instead, let's make it so we assign each name to a particular number. This way, when a user visits the same route again, they'll see what number they originally were.

Making this change is rather easy. Instead of using an IORef on an Int for our state, we'll use a mapping from Text to Int:

data AppState = AppState (IORef (M.Map Text Int))

Now we'll initialize our ref with an empty map and pass it to our config:

main :: IO ()
main = do
  ref <- newIORef M.empty
  spockConfig <- defaultSpockCfg EmptySession PCNoDatabase (AppState ref)
  runSpock 8080 (spock spockConfig app)

And for our hello/{name} route, we'll update it to follow this process:

  1. Get the map reference
  2. See if we have an entry for this user yet.
  3. If not, insert them with the length of the map, and write this back to our IORef
  4. Return the message

This process is pretty straightforward. Let's see what it looks like:

app :: SpockM () MySession AppState ()
app = do
  get root $ text "Hello World!"
  get ("hello" <//> var) $ \name -> do
    (AppState mapRef) <- getState
    visitorNumber <- liftIO $ atomicModifyIORef' mapRef $ updateMapWithName name
    text ("Hello " <> name <> ", you are visitor number " <> T.pack (show visitorNumber))

updateMapWithName :: T.Text -> M.Map T.Text Int -> (M.Map T.Text Int, Int)
updateMapWithName name nameMap = case M.lookup name nameMap of
  Nothing -> (M.insert name (mapSize + 1) nameMap, mapSize + 1)
  Just i -> (nameMap, i)
  where
    mapSize = M.size nameMap

We create a function to update the map every time our app encounters a new name. The we update our IORef with atomicModifyIORef. And now if we visit /hello/michael twice in a row, we'll get the same output both times!

Conclusion

That's as far as we'll go this week! We covered the basics of how to make a basic application in Spock. We saw the basics of composing routes. Then we saw how we could use the app state to keep track of information across requests. Next week, we'll improve this process by adding a database to our application. We'll also use sessions to keep track of users.

For more cool libraries, read up on our Haskell Web Series. Also, you can download our Production Checklist for more ideas!

Read More
James Bowen James Bowen

Common (But not so Common) Monads

Function Monad.png

Last week we looked at how monads can help you make the next jump in your Haskell development. We went over the runXXXT pattern and how it’s a common gateway for us to use certain monads from the rest of our code. But sometimes it also helps to go back to the basics. I actually went a long time without really grasping how to use a couple basic monads. Or at the very least, I didn’t understand how to use them as monads.

In this article, we’ll look at how to use the list monad and the function monad. Lists and functions are core concepts that any Haskeller learns from the get-go. But the list data structure and function application are also monads! And understanding how they work as such can teach us more about how monads work.

For an in-depth discussion of monads, check out our Functional Data Structures Series!

The General Pattern of Do Syntax

Using do syntax is one of the keys to understanding how to actually use monads. The bind operator makes it hard to track where your arguments are. Do syntax keeps the structure clean and allows you to pass results with ease. Let’s see how this works with IO, the first monad a lot of Haskellers learn. Here’s an example where we read the second line from a file:

readLineFromFile :: IO String
readLineFromFile = do
  handle <- openFile “myFile.txt” ReadMode
  nextLine <- hGetLine handle
  secondLine <- hGetLine handle
  _ <- hClose handle
  return secondLine

By keeping in mind the type signatures of all the IO functions, we can start to see the general pattern of do syntax. Let’s replace each expression with its type:

openFile :: FilePath -> IOMode -> IO Handle
hGetLine :: Handle -> IO String
hClose :: Handle -> IO ()
return :: a -> IO a

readLineFromFile :: IO String
readLineFromFile = do
  (Handle) <- (IO Handle)
  (String) <- (IO String)
  (String) <- (IO String)
  () <- (IO ())
  IO String

Every line in a do expression (except the last) uses the assignment operator <-. Then it has an expression of IO a on the right side, which it assigns to a value of a on the left side. The last line’s type then matches the final return value of this function. What’s important now is to recognize that we can generalize this structure to ANY monad:

monadicFunction :: m c
monadicFunction = do
  (_ :: a) <- (_ :: m a)
  (_ :: b) <- (_ :: m b)
  (_ :: m c)

So for example, if we have a function in the Maybe monad, we can use it and plug that in for m above:

myMaybeFunction :: a -> Maybe a

monadicMaybe :: a -> Maybe a
monadicMaybe x = do
  (y :: a) <- myMaybeFunction x
  (z :: a) <- myMaybeFunction y
  (Just z :: Maybe a)

The important thing to remember is that a monad captures a computational context. For IO, this context is that the computation might interact with the terminal or network. For Maybe, the context is that the computation might fail.

The List Monad

Now to graph the list monad, we need to know its computational context. We can view any function returning a list as non-deterministic. It could have many different values. So if we chain these computations, our final result is every possible combination. That is, our first computation could return a list of values. Then we want to check what we get with each of these different results as an input to the next function. And then we’ll take all those results. And so on.

To see this, let’s imagine we have a game. We can start that game with a particular number x. On each turn, we can either subtract one, add one, or keep the number the same. We want to know all the possible results after 5 turns, and the distribution of the possibilities. So we start by writing our non-deterministic function. It takes a single input and returns the possible game outputs:

runTurn :: Int -> [Int]
runTurn x = [x - 1, x, x + 1]

Here’s how we’d apply on this 5 turn game. We’ll add the type signatures so you can see the monadic structure:

runGame :: Int -> [Int]
runGame x = do
  (m1 :: Int) <- (runTurn x :: [Int])
  (m2 :: Int) <- (runTurn m1 :: [Int])
  (m3 :: Int) <- (runTurn m2 :: [Int])
  (m4 :: Int) <- (runTurn m3 :: [Int])
  (m5 :: Int) <- (runTurn m4 :: [Int])
  return m5

On the right side, every expression has type [Int]. Then on the left side, we get our Int out. So each of the m expressions represents one of the many solutions we'll get from runTurn. Then we run the rest of the function imagining we’re only using one of them. In reality though, we’ll run them all, because of how the list monad defines its bind operator. This mental jump is a little tricky. And it’s often more intuitive to just stick to using where expressions when we do list computations. But it's cool to see patterns like this pop up in unexpected places.

The Function Monad

The function monad is another one I struggled to understand for a while. In some ways, it's the same as the Reader monad. It encapsulates the context of having a single argument we can pass to different functions. But it’s not defined in the same way as Reader. When I tried to grok the definition, it didn’t make much sense to me:

instance Monad ((->) r) where
  return x = \_ -> x
  h >>= f = \w -> f (h w) w

The return definition makes sense. We’ll have a function that takes some argument, ignore that argument, and give the value as an output. The bind operator is a little more complicated. When we bind two functions together, we’ll get a new function that takes some argument w. We’ll apply that argument against our first function ((h w)). Then we’ll take the result of that, apply it to f, and THEN also apply the argument w again. It’s a little hard to follow.

But let’s think about this in the context of do syntax. Every expression on the right side will be a function that takes our type as its only argument.

myFunctionMonad :: a -> (x, y, z)
myFunctionMonad = do
  x <- :: a -> b
  y <- :: a -> c
  z <- :: a -> d
  return (x, y, z)

Now let’s imagine we’ll pass an Int and use a few different functions that can take an Int. Here’s what we’ll get:

myFunctionMonad :: Int -> (Int, Int, String)
myFunctionMonad = do
  x <- (1 +)
  y <- (2 *)
  z <- show
  return (x, y, z)

And now we have valid do syntax! So what happens when we run this function? We’ll call our different functions on the same input.

>> myFunctionMonad 3
(4, 6, "3")
>> myFunctionMonad (-1)
(0, -2, "-1")

When we pass 3 in the first example, we add 1 to it on the first line, multiply it by 2 on the second line, and show it on the third line. And we do this all without explicitly stating the argument! The tricky part is that all your functions have to take the input argument as their last argument. So you might have to do a little bit of argument flipping.

Conclusion

In this article we explored lists and functions, two of the most common concepts in Haskell. We generally don’t use these as monads. But we saw how they still fit into the monadic structure. We can use them in do-syntax, and follow the patterns we already know to make things work.

Perhaps you’ve tried to learn Haskell before but found monads a little too complex. Hopefully this article helped clarify the structure of monads. If you want to get your Haskell journey back under way, download our Beginners Checklist! Or to learn monads from the ground up, read our series on Functional Data Structures!

Read More
James Bowen James Bowen

Making the Jump II: Using More Monads

making_jump_2.jpg

A few weeks ago, we addressed some important steps to advance past the "beginner" stage of Haskell. We learned how to organize your project and how to find the relevant documentation. This week we’re going to continue to look at another place where we can make a big step up. We’ll explore how to expand our vocabulary on monad usage.

Monads are a vital component of Haskell. You can’t use a lot of libraries unless you know how to incorporate their monadic functions. These functions often involve a monad that is custom to that library. When you’re first starting out, it can be hard to know how to incorporate these monads into the rest of your program.

In this article, we’ll focus on a specific pattern a lot of monads and libraries use. I call this pattern the “run” pattern. Often, you’ll use a function with a name like runXXX or runXXXT, where XXX is the name of the monad. These functions will always take a monadic expression as their first argument. Then they'll also take some other initialization information, and finally return some output. This output can either be in pure form or a different monad you’re already using like IO. We’ll start by seeing how this works with the State monad, and then move onto some other libraries.

Once you grasp this topic, it seems very simple. But a lot of us first learned monads with a bad mental model. For instance, the first thing I learned about monads was that they had side effects. And thus, you can only call them from places that have the same side effects. This applies to IO but doesn’t generalize to other monads. So even though it seems obvious now, I struggled to learn this idea at first. But let's start looking at some examples of this pattern.

For a more in depth look at monads, check out our series on Functional Data Structures! We start by learning about simpler things like functors. Then we eventually work our way up to monads and even monad transformers!

The Basics of “Run”: The State Monad

Let’s start by recalling the State monad. This monad has a single type parameter, and we can access this type as a global read/write state. Here’s an example function written in the State monad:

stateExample :: State Int (Int, Int, Int)
stateExample = do
  a <- get
  modify (+1)
  b <- get
  put 5
  c <- get
  return (a, b, c)

If this function is confusing, you should take a look at the documentation for State. It’ll at least show you the relevant type signatures. First we read the initial state. Then we modify it with some function. Finally we completely change it.

In the example above, if our initial state is 1, we’ll return (1,2,5) as the result. If the initial state is 2, we’ll return (2,3,5). But suppose we have a pure function. How do we call our state function?

pureFunction :: Int -> Int
pureFunction = ???

The answer is the runState function. We can check the documentation and find its type:

runState :: State s a -> s -> (a, s)

This function has two parameters. The first is a State action. We’ll pass our function above as this parameter! Then the second is the initial state, and this is how we’ll configure it. Then the result is pure. It contains our result, as well as the final value of the state. So here’s a sample call we can make that gives us this monadic expression in our pure function. We’ll call it from a where clause, and discard the final state:

pureFunction :: Int -> Int
pureFunction input = a + b + c
  where
    ((a,b,c), _) = runState stateExample input

This is the simplest example of how we can use the runXXX pattern.

Upgrading to Transformers

Now, suppose our State function isn’t quite pure. It now wants to print some of its output, so it’ll need the IO monad. This means it’ll use the StateT monad transformer over IO:

stateTExample :: StateT Int IO (Int, Int, Int)
stateTExample = do
  a <- get
  lift $ print “Initial Value:”
  lift $ print a
  modify (+1)
  b <- get
  lift $ putStrLn “After adding 1:”
  lift $ print b
  put 5
  c <- get
  lift $ putStrLn “After setting as 5:”
  lift $ print c
  return (a, b, c)

Now instead of calling this function from a pure format, we’ll need to call it from an IO function. But once again, we’ll use a runXXX function. Now though, since we’re using a monad transformer, we won’t get a pure result. Instead, we’ll get our result in the underlying monad. This means we can call this function from IO. So let’s examine the type of the runStateT function. We’ve substituted IO for the generic monad parameter m:

runStateT :: StateT s IO a -> s -> IO (a, s)

It looks a lot like runState, except for the extra IO parameters! Instead of returning a pure tuple for the result, it returns an IO action containing that result. Thus we can call it from the IO monad.

main :: IO ()
main = do
  putStrLn “Please enter a number.”
  input <- read <$> getLine
  results <- runStateT stateTExample input
  print results

We’ll get the following output as a result:

Please enter a number.
10
Initial Value:
10
After adding 1
11
After setting as 5
5
(10, 11, 5)

Using Run For Libraries

This pattern will often extend into libraries you use. For example, in our series on parsing, we examine the Megaparsec library. A lot of the individual parser combinators in that library exist in the Parsec or ParsecT monad. So we can combine a bunch of different parsers together into one function.

But then to run that function from your normal IO code (or another monad), you need to use the runParserT function. Let’s look at its type signature:

runParserT
  :: Monad m
  -> ParsecT e s m a
  -> String -- Name of source file
  -> s -- Input for parser
  -> m (Either (ParseError (Token s) e) a)

There are a lot of type parameters there that you don’t need to understand. But the structure is the same. The first parameter to our run function is the monadic action. Then we’ll supply some other inputs we need. Then we get some result, wrapped in an outer monad (such as IO).

We can see the same pattern if we use the servant-client library to make client-side API calls. Any call you make to your API will be in the ClientM monad. Now here’s the type signature of the runClientM function:

runClientM :: ClientM a -> ClientEnv -> IO (Either ServantError a)

So again, the same pattern emerges. We’ll compose our monadic action and pass that as the first parameter. Then we’ll provide some initial state, in this case a ClientEnv. Finally, we’ll get our result (Either ServantError a) wrapped in an outer monad (IO).

Monads Within Expressions

It’s also important to remember that a lot of basic monads work without even needing a runXXX function! For instance, you can use a Maybe or Either monad to take out some of your error handling logic:

divideIfEven :: Int -> Maybe Int
divideIfEven x = if x `mod` 2 == 0
  then Just (x `quot` 2)
  else Nothing

dividesBy8 :: Int -> Bool
dividesBy8 = case thirdResult of
  Just _ -> True
  Nothing -> False
  where
    thirdResult :: Maybe Int
    thirdResult = do
      y <- divideIfEven x
      z <- divideIfEven y
      divideIfEven z

Conclusion

Monads are the key to using a lot of different Haskell libraries. But when you’re first starting out, it can be very confusing how you call into these functions from your code. The same applies with some common monad transformers like Reader and State. The most common pattern to look out for is the runXXXT pattern. Master this pattern and you’re well on your to understanding monads and writing better Haskell!

For a closer look at monads and similar structures, make sure to read our series on Functional Data Structures. If the code in this article was confusing, you should definitely check it out! And if you’ve never written Haskell but want to start, download our Beginners Checklist!

Read More
James Bowen James Bowen

Taking a Look Back: My Mistakes in Learning Haskell

mistakes.jpg

Last week, we announced our Haskell From Scratch Beginners Course. Course sign-ups are still open, but not for much longer! They will close at midnight Pacific time on Wednesday, August 29th!

The course starts next week. But before it does, I wanted to take this opportunity to tell a little bit of the story of how I learned Haskell. I want to share the mistakes I made, since those motivated me to make this course.

My Haskell History

I first learned Haskell in college as part of a course on programming language theory. I admired the elegance of a few things in particular. I liked how lists and tuples worked well with the type system. I also appreciated the elegance of Haskell’s type definitions. No other language I had seen represented the idea of sum types so well. I also saw how useful pattern matching and recursion were. They made it very easy to break problems down into manageable parts.

After college, I had the idea for a code generation project. A couple college assignments had dealt with code generation. So I realized already knew a couple Haskell libraries that could provide the foundation for the work. So I got to work writing up some Haskell. At first things were quite haphazard. Eventually though, I developed some semblance of test driven development and product organization.

About nine months into that project, I had the great fortune of landing a Haskell project at my day-job. As I ramped up on this project, I realized how deficient my knowledge was in a lot of areas. I realized then a lot of the mistakes I had been making while learning the language. This motivated me to start the Monday Morning Haskell blog.

Main Advice

Of course, I’ve tried to incorporate my learnings throughout the material on this blog. But if I had to distill the key ideas, here’s what they’d be.

First, learn tools and project organization early! Learn how to use Stack and/or Cabal! For help with this, you can check out our free Stack mini-course! After several months on my side project, I had to start from scratch to some extent. The only “testing” I was doing was running some manual executables and commands in GHCI. So once I learned more about these tools, I had to re-work a lot of code.

Second, it helps a lot to have some kind of structure when you’re first learning the language. Working on a project is nice, but there are a lot of unknown-unknowns out there. You’ll often find a “solution” for your problem, only to see that you need a lot more knowledge to implement it. You need to have a solid foundation on the core concepts before you can dive in on anything. So look for a source that provides some kind of structure to your Haskell learning, like a book (or an online course!).

Third, let’s get to monads. They’re an important key to Haskell and widely misunderstood. But there are a couple things that will help a lot. First, learn the syntactic patterns of do-syntax. Second, learn how to use run functions (runState, runReaderT, etc.). These are how you bring monadic expressions into the rest of your code. You can check out our Monads Series for some help on these ideas. We’ll also have an article on this topic next week! (And of course, you’ll learn all about monads in Haskell From Scratch!)

Finally, ask for help earlier! I still don’t plug into the Haskell network as much as I should. There are a lot of folks out there who are more than willing to help. Freenode is a great place, as is Reddit and even Twitter!

Conclusion

There’s never been a better time to start learning Haskell! The language tools have developed a ton in the last few years and the community is growing stronger. As we announced last week, we’ve now opened up our Haskell From Scratch Beginners Course! You don’t need any Haskell experience to take this course. So if you always wanted to learn more about the language but needed more organization, this is your chance!

Read More
James Bowen James Bowen

Announcing: Haskell From Scratch Beginners Course!

newlogo3transparent.png

This week we have a huge announcement we’ve been working towards for a long time. One of the main goals of this blog has been to create content to make it easy for newcomers to learn Haskell. We’ve now reached the culmination of that goal with our brand new Haskell From Scratch course. This online course will teach you the basics of using and writing Haskell. It assumes no prior knowledge of Haskell, but you should have at least some programming background. To sign up, head over to the course page.

Course Overview

The course consists of seven modules. Each module has a series of video lectures and accompanying exercises. In the first module, we’ll go over the fundamental structure of the language. We’ll take an in-depth look at how we compose programs by using expressions. Then we’ll see how the type system affects what we can do with those expressions.

In module 2, we’ll try to build a deeper mastery of the type system by learning how to construct our own types. We’ll also see how Haskell’s typeclass system lets us capture common behavior between types.

Module 3 deals with lists and recursion. Haskell doesn’t use for-loops like you have in mainstream languages. Instead, we tend to solve problems with recursion. There's a clear, recognizable pattern to recursion. We'll use Haskell's list structure to help understand this pattern.

In module 4, we’ll take our first steps towards learning about monads and writing real programs. We’ll learn about the IO monad, whose functions allow us to do more interesting things. We'll see how to get user input, manipulate files and even use threads.

In module 5, we’ll take what we learned from writing IO code and apply it to learning about other monads. We’ll start by learning about other kinds of functional data structures. Then we’ll use these patterns to help us learn this most dreaded of Haskell concepts (it’s actually not bad!).

Module 6 deals with the complications of similar-looking data. We have many different ways of representing numbers or strings, for example. And each representation can have a different type. This presents a lot of unique challenges for us as Haskell developers. We’ll explore these more in this module.

Finally, we’ll wrap the course up by learning the "Haskell" approach to problem solving. We'll look at some common programming problems and see how to solve them in Haskell. We’ll consider paradigms like memoization and dynamic programming.

Besides the course material, there will also be a Slack group for this course. This will be a place where you can get help from myself or any of your fellow classmates!

Course Schedule

The course will launch on Monday, September 3rd, with the release of module 1. We will then release a new module each Thursday and Monday thereafter. Don’t worry if you’re busy on a particular week! You’ll be able to access all old content indefinitely.

Now, sign-ups for the course will end on Wednesday, August 29th! So don’t miss out! Head over to the course page and reserve your spot today! If you’re not sure yet about starting with Haskell, you can also download our Beginners Checklist. It’ll give you the tools you need to try the language out!

Read More
James Bowen James Bowen

Series Spotlight: Haskell Web Skills!

Haskell has a reputation of being a neat language with cool concepts used a lot in academic research. But the dark side of this reputation is that people imagine it's unsuited for production use. But nothing could be further from the truth!

A common misconception about Haskell is that it “doesn’t have side effects.” Of course any language needs to have side effects to be effective. What makes Haskell unique isn’t that it doesn’t have side effects at all. What makes it unique (or at least uncommon) is that you have to encode side effects within the type system. This means you can know at compile time where the effects lie in your system.

To get a feel for how you can write normal programs with Haskell, you should read our Haskell Web Series. It will take you through the basics of building a simple web application using Haskell. We’ll highlight a couple specific libraries that allow you to do common web tasks.

In part 1, we’ll use the Persistent library to connect to a Postgresql database. This involves using a more complex concept called Template Haskell to create our database schema. That way, we can automatically generate the SQL statements we need.

In part 2, we’ll learn how to use the Servant library to create an HTTP server. We’ll also see how we can connect it to our database and make requests from there.

Next, we’ll use the Hedis library to connect to Redis. This will allow us to cache some of our database information. Then when we need it again, we won't have to go to our database!

Part 4 covers how we test such a complicated system. We’ll also see how to use Stack to connect our system with Docker so that our side services are easy to manage.

Finally, we wrap this series up in part 5, where we’ll look at some more complicated database queries we can make. This will require learning another library called Esqueleto. This cool library works in tandem with Persistent.

There are a lot of different libraries involved in this series. So it’s essential you know how to bring outside code into your Haskell application. To learn more about package management, you should also take our free Stack mini-course. It’ll help you learn how to create a real Haskell program and connect to libraries with ease.

And if you’ve never written Haskell before, now’s a great time to start! Download our Beginner’s Checklist for some tips!

Read More
James Bowen James Bowen

Keeping it Clean: Haskell Code Formatters

clean_code.png

A long time ago, we had an article that featured some tips on how to organize your import statements. As far as I remember, that’s the only piece we’ve ever done on code formatting. But as you work on projects with more people, formatting is one thing you need to consider to keep everyone sane. You’d like to have a consistent style across the code base. That way, there’s less controversy in code reviews, and people don't need to think as much to update code. They shouldn't have to wonder about following the style guide or what exists in a fragment of code.

This week, we’re going to go over three different Haskell code formatting tools. We’ll examine Stylish Haskell, Hindent, and Brittany. These all have their pluses and minuses, as we’ll see.

For some ideas of what Haskell projects you can do, download our Production Checklist. You can also take our free Stack mini-course and learn how to use Stack to organize your code!

Stylish Haskell

The first tool we’ll look at is Stylish Haskell. This is a straightforward tool to use, as it does some cool things with no configuration required. Let’s take a look at a poorly formatted version of code from our Beam article.

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ImpredicativeTypes #-}

module Schema where

import Database.Beam
import Database.Beam.Backend
import Database.Beam.Migrate
import Database.Beam.Sqlite
import Database.SQLite.Simple (open, Connection)

import Data.Int (Int64)
import Data.Text (Text)
import Data.Time (UTCTime)
import qualified Data.UUID as U

data UserT f = User
  { _userId :: Columnar f Int64
  , _userName :: Columnar f Text
  , _userEmail :: Columnar f Text
  , _userAge :: Columnar f Int
  , _userOccupation :: Columnar f Text
  } deriving (Generic)

There are many undesirable things here. Our language pragmas don’t line up their end braces. They also aren’t in any discernible order. Our imports are also not lined up, and neither are the fields in our data types.

Stylish Haskell can fix all this. First, we’ll install it globally with:

stack install stylish-haskell

(You can also use cabal instead of stack). Then we can call the stylish-haskell command on a file. By default, it will output the results to the terminal. But if we pass the -i flag, it will update the file in place. This will make all the changes we want to line up the various statements in our file!

>> stylish-haskell -i Schema.hs

--- Result:

{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE ImpredicativeTypes    #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE StandaloneDeriving    #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeSynonymInstances  #-}

module Schema where

import           Database.Beam
import           Database.Beam.Backend
import           Database.Beam.Migrate
import           Database.Beam.Sqlite
import           Database.SQLite.Simple (Connection, open)

import           Data.Int               (Int64)
import           Data.Text              (Text)
import           Data.Time              (UTCTime)
import qualified Data.UUID              as U

data UserT f = User
  { _userId         :: Columnar f Int64
  , _userName       :: Columnar f Text
  , _userEmail      :: Columnar f Text
  , _userAge        :: Columnar f Int
  , _userOccupation :: Columnar f Text
  } deriving (Generic)

Stylish Haskell integrates well with most common editors. For instance, if you use Vim, you can also run the command from within the editor with the command:

:%!stylish-haskell

We get all these features without any configuration. If we want to change things though, we can create a configuration file. We’ll make a default file with the following command:

stylish-haskell --defaults > .stylish-haskell.yaml

Then if we want, we can modify it a bit. For one example, we've aligned our imports above globally. This means they all leave space for qualified. But we can decide we don’t want a group of imports to have that space if there are no qualified imports. There’s a setting for this in the config. By default, it looks like this:

imports:
  align: global

We can change it to group to ensure our imports are only aligned within their grouping.

imports:
  align: group

And now when we run the command, we’ll get a different result:

module Schema where

import Database.Beam
import Database.Beam.Backend
import Database.Beam.Migrate
import Database.Beam.Sqlite
import Database.SQLite.Simple (Connection, open)

import           Data.Int  (Int64)
import           Data.Text (Text)
import           Data.Time (UTCTime)
import qualified Data.UUID as U

So in short, Stylish Haskell is a great tool for a limited scope. It has uncontroversial suggestions for several areas like imports and pragmas. It also removes trailing whitespace, and adjusts case statements sensibly. That said, it doesn’t affect your main Haskell code. Let’s look at a couple tools that can do that.

Hindent

Another program we can use is hindent. As its name implies, it deals with updating whitespace and indentation levels. Let’s look at a very simple example. Consider this code, adapted from our Beam article:

user1' = User default_  (val_ "James")  (val_ "james@example.com")  (val_ 25)  (val_ "programmer")

findUsers :: Connection -> IO ()
findUsers conn = runBeamSqlite conn $ do
    users <- runSelectReturningList $ select $ do
        user <- (all_ (_blogUsers blogDb))
        article <- (all_ (_blogArticles blogDb))
        guard_ (user ^. userName ==. (val_ "James"))
        guard_ (article ^. articleUserId ==. user ^. userId) 
        return (user, article)
    mapM_ (liftIO . putStrLn . show) users

There are a few things we could change. First, we might want to update the indentation level so that it is 2 instead of 4. Second, let's restrict the line size to only being 80. When we run hindent on this file, it’ll make the changes.

user1' =
  User
    default_
    (val_ "James")
    (val_ "james@example.com")
    (val_ 25)
    (val_ "programmer")

findUsers :: Connection -> IO ()
findUsers conn =
  runBeamSqlite conn $ do
    users <-
      runSelectReturningList $
      select $ do
        user <- (all_ (_blogUsers blogDb))
        article <- (all_ (_blogArticles blogDb))
        guard_ (user ^. userName ==. (val_ "James"))
        guard_ (article ^. articleUserId ==. user ^. userId)
        return (user, article)
    mapM_ (liftIO . putStrLn . show) users

Hindent is also configurable. We can create a file .hindent.yaml. By default, we would have the following configuration:

indent-size: 2
line-length: 80
force-trailing-newline: true

But then we can change it if we want so that the indentation level is 3:

indent-size: 3

And now when we run it, we’ll actually see that it’s changed to reflect that:

findUsers :: Connection -> IO ()
findUsers conn =
   runBeamSqlite conn $ do
      users <-
         runSelectReturningList $
         select $ do
            user <- (all_ (_blogUsers blogDb))
            article <- (all_ (_blogArticles blogDb))
            guard_ (user ^. userName ==. (val_ "James"))
            guard_ (article ^. articleUserId ==. user ^. userId)
            return (user, article)
      mapM_ (liftIO . putStrLn . show) users

Hindent also has some other effects that, as far as I can tell, are not configurable. You can see that the separation of lines was not preserved above. In another example, it spaced out instance definitions that I had grouped in another file:

-- BEFORE
deriving instance Show User
deriving instance Eq User
deriving instance Show UserId
deriving instance Eq UserId

-- AFTER
deriving instance Show User

deriving instance Eq User

deriving instance Show UserId

deriving instance Eq UserId

So make sure you’re aware of everything it does before committing to using it. Like stylish-haskell, hindent integrates well with text editors.

Brittany

Brittany is an alternative to Hindent for modifying your expression definitions. It mainly focuses on the use of horizontal space throughout your code. As far as I see, it doesn’t line up language pragmas or change import statements in the way stylish-haskell does. It also doesn’t touch data type declarations. Instead, it seeks to reformat your code to make maximal use of space while avoiding lines that are too long. As an example, we could look at this line from our Beam example:

insertArticles :: Connection -> IO ()
insertArticles conn = runBeamSqlite conn $ runInsert $ 
  insert (_blogArticles blogDb) $ insertValues articles

Our decision on where to separate the line is a little bit arbitrary. But at the very least we don’t try to cram it all on one line. But if we have either the approach above or the one-line version, Brittany will change it to this:

brittany --write-mode=inplace MyModule.hs

--

insertArticles :: Connection -> IO ()
insertArticles conn =
  runBeamSqlite conn $ runInsert $ insert (_blogArticles blogDb) $ insertValues
    articles

This makes “better” use of horizontal space in the sense that we get as much on the first line. That said, one could argue that the first approach we have actually looks nicer. Brittany can also change type signatures that overflow the line limit. Suppose we have this arbitrary type signature that’s too long for a single line:

myReallyLongFunction :: State ComplexType Double -> Maybe Double -> Either Double ComplexType -> IO a -> StateT ComplexType IO a

Brittany will fix it up so that each argument type is on a single line:

myReallyLongFunction
  :: State ComplexType Double
  -> Maybe Double
  -> Either Double ComplexType
  -> IO a
  -> StateT ComplexType IO a

This can be useful in projects with very complicated types. The structure makes it easier for you to add Haddock comments to the various arguments.

Dangers

There is of course, a (small) danger to using tools like these. If you’re going to use them, you want to ensure everyone on the project is using them. Suppose person A isn’t using the program, and commits code that isn’t formatted by the program. Person B might then look through that code, and their editor will correct the file. This will leave them with local changes to the file that aren’t relevant to whatever work they’re doing. This can cause a lot of confusion when they submit code for review. Whoever reviews their code has to sift through the format changes, which slows the review.

People can also have (unreasonably) strong opinions about code formatting. So it’s generally something you want to nail down early on a project and avoid changing afterward. With the examples in this article, I would say it would be an easy sell to use Stylish Haskell on a project. However, the specific choices made in H-Indent and Brittany can be more controversial. So it might cause more problems than it would solve to institute those project-wide.

Conclusion

It’s possible to lose a surprising amount of productivity to code formatting. So it can be important to nail down standards early and often. Code formatting programs can make it easy to enforce particular standards. They’re also very simple to incorporate into your projects with stack and your editor of choice!

Now that you know how to format your code, need some suggestions for what to work on next? Take a look at our Production Checklist! It’ll give you some cool ideas of libraries you can use for building Haskell web apps and much more!

Read More
James Bowen James Bowen

Beam: Database Power without Template Haskell!

haskell_beam.png

As part of our Haskell Web Series, we examined the Persistent and Esqueleto libraries. The first of these allows you to create a database schema in a special syntax. You can then use Template Haskell to generate all the necessary Haskell data types and instances for your types. Even better, you can write Haskell code to query on these that resembles SQL. These queries are type-safe, which is awesome. However, the need to specify our schema with template Haskell presented some drawbacks. For instance, the code takes longer to compile and is less approachable for beginners.

This week on the blog, we'll be exploring another database library called Beam. This library allows us to specify our database schema without using Template Haskell. There's some boilerplate involved, but it's not bad at all! Like Persistent, Beam has support for many backends, such as SQLite and PostgresQL. Unlike Persistent, Beam also supports join queries as a built-in part of its system.

For some more ideas on advanced libraries, be sure to check out our Production Checklist! It includes a couple more different database options to look at.

Specifying our Types

As a first note, while Beam doesn't require Template Haskell, it does need a lot of other compiler extensions. You can look at those in the appendix below, or else take a look at the example code on Github. Now let's think back to how we specified our schema when using Persistent:

import qualified Database.Persist.TH as PTH

PTH.share [PTH.mkPersist PTH.sqlSettings, PTH.mkMigrate "migrateAll"] [PTH.persistLowerCase|
  User sql=users
    name Text
    email Text
    age Int
    occupation Text
    UniqueEmail email
    deriving Show Read Eq

  Article sql=articles
    title Text
    body Text
    publishedTime UTCTime
    authorId UserId
    UniqueTitle title
    deriving Show Read Eq

With Beam, we won't use Template Haskell, so we'll actually be creating normal Haskell data types. There will still be some oddities though. First, by convention, we'll specify our types with the extra character T at the end. This is unnecessary, but the convention helps us remember what types relate to tables. We'll also have to provide an extra type parameter f, that we'll get into a bit more later:

data UserT f =
  …

data ArticleT f =
  ...

Our next convention will be to use an underscore in front of our field names. We will also, unlike Persistent, specify the type name in the field names. With these conventions, I'm following the advice of the library's creator, Travis.

data UserT f =
  { _userId :: ...
  , _userName :: …
  , _userEmail :: …
  , _userAge :: …
  , _userOccupation :: …
  }

data ArticleT f =
  { _articleId :: …
  , _articleTitle :: …
  , _articleBody :: …
  , _articlePublishedTime :: …
  }

So when we specify the actual types of each field, we'll just put the relevant data type, like Int, Text or whatever, right? Well, not quite. To complete our types, we're going to fill in each field with the type we want, except specified via Columnar f. Also, we'll derive Generic on both of these types, which will allow Beam to work its magic:

data UserT f =
  { _userId :: Columnar f Int64
  , _userName :: Columnar f Text
  , _userEmail :: Columnar f Text
  , _userAge :: Columnar f Int
  , _userOccupation :: Columnar f Text
  } deriving (Generic)

data ArticleT f =
  { _articleId :: Columnar f Int64
  , _articleTitle :: Columnar f Text
  , _articleBody :: Columnar f Text
  , _articlePublishedTime :: Columnar f Int64 -- Unix Epoch
  } deriving (Generic)

Now there are a couple small differences between this and our previous schema. First, we have the primary key as an explicit field of our type. With Persistent, we separated it using the Entity abstraction. We'll see below how we can deal with situations where that key isn't known. The second difference is that (for now), we've left out the userId field on the article. We'll add this when we deal with primary keys.

Columar

So what exactly is this Columnar business about? Well under most circumstances, we'd like to specify a User with the raw field types. But there are some situations where we'll have to use a more complicated type for an SQL expression. Let's start with the simple case first.

Luckily, Columnar works in such a way that if we useIdentity for f, we can use raw types to fill in the field values. We'll make a type synonym specifically for this identity case. We can then make some examples:

type User = UserT Identity
type Article = ArticleT Identity

user1 :: User
user1 = User 1 "James" "james@example.com" 25 "programmer"

user2 :: User
user2 = User 2 "Katie" "katie@example.com " 25 "engineer"

users :: [User]
users = [ user1, user2 ]

As a note, if you find it cumbersome to repeat the Columnar keyword, you can shorten it to C:

data UserT f =
  { _userId :: C f Int64
  , _userName :: C f Text
  , _userEmail :: C f Text
  , _userAge :: C f Int
  , _userOccupation :: C f Text
  } deriving (Generic)

Now, our initial examples will assign all our fields with raw values. So we won't initially need to use anything for the f parameter besides Identity. Further down though, we'll deal with the case of auto-incrementing primary keys. In this case, we'll use the default_ function, whose type is actually a Beam form of an SQL expression. In this case, we'll be using a different type for f, but the flexibility will allow us to keep using our User constructor!

Instances for Our Types

Now that we've specified our types, we can use the Beamable and Table type classes to tell Beam more about our types. Before we can make any of these types a Table, we'll want to assign its primary key type. So let's make a couple more type synonyms to represent these:

type UserId = PrimaryKey UserT Identity
type ArticleId = PrimaryKey ArticleT Identity

While we're at it, let's add that foreign key to our Article type:

data ArticleT f =
  { _articleId :: Columnar f Int64
  , _articleTitle :: Columnar f Text
  , _articleBody :: Columnar f Text
  , _articlePublishedTime :: Columnar f Int64
  , _articleUserId :: PrimaryKey UserT f
  } deriving (Generic)

We can now generate instances for Beamable both on our main types and on the primary key types. We'll also derive instances for Show and Eq:

data UserT f =
  …

deriving instance Show User
deriving instance Eq User

instance Beamable UserT
instance Beamable (PrimaryKey UserT)

data ArticleT f =
  …

deriving instance Show Article
deriving instance Eq Article

instance Beamable ArticleT
instance Beamable (PrimaryKey ArticleT)

Now we'll create an instance for the Table class. This will involve some type family syntax. We'll specify UserId and ArticleId as our primary key data types. Then we can fill in the primaryKey function to match up the right field.

instance Table UserT where
  data PrimaryKey UserT f = UserId (Columnar f Int64) deriving Generic
  primaryKey = UserId . _userId

instance Table ArticleT where
  data PrimaryKey ArticleT f = ArticleId (Columnar f Int64) deriving Generic
  primaryKey = ArticleId . _articleId

Accessor Lenses

We'll do one more thing to mimic Persistent. The Template Haskell automatically generated lenses for us. We could use those when making database queries. Below, we'll use something similar. But we'll use a special function, tableLenses, to make these rather than Template Haskell. If you remember back to how we used the Servant Client library, we could create client functions by using client and matching it against a pattern. We'll do something similar with tableLenses. We'll use LensFor on each field of our tables, and create a pattern constructing an item.

User
  (LensFor userId)
  (LensFor userName)
  (LensFor userEmail)
  (LensFor userAge)
  (LensFor userOccupation) = tableLenses

Article
  (LensFor articleId)
  (LensFor articleTitle)
  (LensFor articleBody)
  (LensFor articlePublishedTime)
  (UserId (LensFor articuleUserId)) = tableLenses

Note we have to wrap the foreign key lens in UserId.

Creating our Database

Now unlike Persistent, we'll create an extra type that will represent our database. Each of our two tables will have a field within this database:

data BlogDB f = BlogDB
  { _blogUsers :: f (TableEntity UserT)
  , _blogArticles :: f (TableEntity ArticleT)
  } deriving (Generic)

We'll need to make our database type an instance of the Database class. We'll also specify a set of default settings we can use on our database. Both of these items will involve a parameter be, which stands for a backend, (e.g. SQLite, Postgres). We leave this parameter generic for now.

instance Database be BlogDB

blogDb :: DatabaseSettings be BlogDB
blogDb = defaultDbSettings

Inserting into Our Database

Now, migrating our database with Beam is a little more complicated than it is with Persistent. We might cover that in a later article. For now, we'll keep things simple, and use an SQLite database and migrate it ourselves. So let's first create our tables. We have to follow Beam's conventions here, particularly on the user_id__id field for our foreign key:

CREATE TABLE users \
  ( id INTEGER PRIMARY KEY AUTOINCREMENT\
  , name VARCHAR NOT NULL \
  , email VARCHAR NOT NULL \
  , age INTEGER NOT NULL \
  , occupation VARCHAR NOT NULL \
  );
CREATE TABLE articles \
  ( id INTEGER PRIMARY KEY AUTOINCREMENT \
  , title VARCHAR NOT NULL \
  , body VARCHAR NOT NULL \
  , published_time INTEGER NOT NULL \
  , user_id__id INTEGER NOT NULL \
  );

Now we want to write a couple queries that can interact with the database. Let's start by inserting our raw users. We begin by opening up an SQLite connection, and we'll write a function that uses this connection:

import Database.SQLite.Simple (open, Connection)

main :: IO ()
main = do
  conn <- open "blogdb1.db"
  insertUsers conn

insertUsers :: Connection -> IO ()
insertUsers = ...

We start our expression by using runBeamSqlite and passing the connection. Then we use runInsert to specify to Beam that we wish to make an insert statement.

import Database.Beam
import Database.Beam.SQLite

insertUsers :: Connection -> IO ()
insertUsers conn = runBeamSqlite conn $ runInsert $
  ...

Now we'll use the insert function and signal which one of our tables we want out of our database:

insertUsers :: Connection -> IO ()
insertUsers conn = runBeamSqlite conn $ runInsert $
  insert (_blogUsers blogDb) $ ...

Last, since we are inserting raw values (UserT Identity), we use the insertValues function to complete this call:

insertUsers :: Connection -> IO ()
insertUsers conn = runBeamSqlite conn $ runInsert $
  insert (_blogUsers blogDb) $ insertValues users

And now we can check and verify that our users exist!

SELECT * FROM users;
1|James|james@example.com|25|programmer
2|Katie|katie@example.com|25|engineer

Let's do the same for articles. We'll use the pk function to access the primary key of a particular User:

article1 :: Article
article1 = Article 1 "First article" 
  "A great article" 1531193221 (pk user1)

article2 :: Article
article2 = Article 2 "Second article" 
  "A better article" 1531199221 (pk user2)

article3 :: Article
article3 = Article 3 "Third article" 
  "The best article" 1531200221 (pk user1)

articles :: [Article]
articles = [ article1, article2, article3]

insertArticles :: Connection -> IO ()
insertArticles conn = runBeamSqlite conn $ runInsert $
  insert (_blogArticles blogDb) $ insertValues articles

Select Queries

Now that we've inserted a couple elements, let's run some basic select statements. In general for select, we'll want the runSelectReturningList function. We could also query for a single element with a different function if we wanted:

findUsers :: Connection -> IO ()
findUsers conn = runBeamSqlite conn $ do
  users <- runSelectReturningList $ ...

Now we'll use select instead of insert from the last query. We'll also use the function all_ on our users field in the database to signify that we want them all. And that's all we need!:

findUsers :: Connection -> IO ()
findUsers conn = runBeamSqlite conn $ do
  users <- runSelectReturningList $ select (all_ (_blogUsers blogDb))
  mapM_ (liftIO . putStrLn . show) users

To do a filtered query, we'll start with the same framework. But now we need to enhance our select statement into a monadic expression. We'll start by selecting user from all our users:

findUsers :: Connection -> IO ()
findUsers conn = runBeamSqlite conn $ do
  users <- runSelectReturningList $ select $ do
   user <- (all_ (_blogUsers blogDb))
    ...
  mapM_ (liftIO . putStrLn . show) users

And we'll now filter on that by using guard_ and applying one of our lenses. We use a ==. operator for equality like in Persistent. We also have to wrap our raw comparison value with val:

findUsers :: Connection -> IO ()
findUsers conn = runBeamSqlite conn $ do
  users <- runSelectReturningList $ select $ do
    user <- (all_ (_blogUsers blogDb))
    guard_ (user ^. userName ==. (val_ "James"))
    return user
  mapM_ (liftIO . putStrLn . show) users

And that's all we need! Beam will generate the SQL for us! Now let's try to do a join. This is actually much simpler in Beam than with Persistent/Esqueleto. All we need is to add a couple more statements to our "select" on the articles. We'll just filter them by the user ID!

findUsersAndArticles :: Connection -> IO ()
findUsersAndArticles conn = runBeamSqlite conn $ do
  users <- runSelectReturningList $ select $ do
    user <- (all_ (_blogUsers blogDb))
    guard_ (user ^. userName ==. (val_ "James"))
    articles <- (all_ (_blogArticles blogDb))
    guard_ (article ^. articleUserId ==. user ^. userId)
    return user
  mapM_ (liftIO . putStrLn . show) users

That's all there is to it!

Auto Incrementing Primary Keys

In the examples above, we hard-coded all our IDs. But this isn't typically what you want. We should let the database assign the ID via some rule, in our case auto-incrementing. In this case, instead of creating a User "value", we'll make an "expression". This is possible through the polymorphic f parameter in our type. We'll leave off the type signature since it's a bit confusing. But here's the expression we'll create:

user1' = User
  default_ 
  (val_ "James")
  (val_ "james@example.com")
  (val_ 25)
  (val_ "programmer")

We use default_ to represent an expression that will tell SQL to use a default value. Then we lift all our other values with val_. Finally, we'll use insertExpressions instead of insertValues in our Haskell expression.

insertUsers :: Connection -> IO ()
insertUsers conn = runBeamSqlite conn $ runInsert $
  insert (_blogUsers blogDb) $ insertExpressions [ user1' ]

Then we'll have our auto-incrementing key!

Conclusion

That concludes our introduction to the Beam library. As we saw, Beam is a great library that lets you specify a database schema without using any Template Haskell. For more details, make sure to check out the documentation!

For a more in depth look at using Haskell libraries to make a web app, be sure to read our Haskell Web Series. It goes over some database mechanics as well as creating APIs and testing. As an added challenge, trying re-writing the code in that series to use Beam instead of Persistent. See how much of the Servant code needs to change to accommodate that.

And for more examples of cool libraries, download our Production Checklist! There are some more database and API libraries you can check out!

Appendix: Compiler Extensions

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE NoMonoMorphismRestriction #-}
Read More
James Bowen James Bowen

Making the Jump: Advancing Past Beginner Haskell

At certain points in your Haskell development, you might plateau. This can happen to people at many stages, including right at the start. You get used to certain patterns in the language, and it’s hard to know where to go next. This is our first article on how to break out of these plateaus. Specifically, we’ll look at breaking out of a beginner’s plateau. To jump past the simple language constructs, you need to learn how to organize your Haskell code and incorporate other people’s code.

Of course, you can’t make this jump if you’ve never started! Download our Beginners Checklist for some tools on starting out with Haskell. And read our Liftoff Series for some more details on the basic language mechanics.

Organizing Your Project

When I started writing my first Haskell project, it felt like I was trying to hold together steel beams with Elmer’s glue. I installed all my packages system-wide and only tested my code with GHCI and the runghc command. This limited the development of the project and made it hard for me to incorporate anything new.

Generally, you’ll either be building a library for other people to use, or an executable program. And you want to be able to organize everything involved in that so you can coherently run and test what you’ve created. To do this, you need to learn one of the package management tools for Haskell.

The oldest system in common use is Cabal. Cabal allows you to install Haskell packages. It also allows you to organize your project, install executables, and run tests with ease. Check out the user guide for more information on using Cabal on its own.

Cabal has some gotchas though. So if you’re a beginner, I strongly recommend using Stack. Stack adds another layer on top of Cabal, and it simplifies many of Cabal’s difficulties. To learn more about Stack, you can take our free Stack mini-course!

With either of these, there are three main takeaways. You should declare all your dependencies in 1-2 files. The final product of your project, should be explicitly declared in your .cabal file. Finally, you should be able to run all your tests with a single command.

Hackage

Stack and Cabal both make it easy to bring in dependencies. But how do you know what other libraries you can use, and what their functions are? The main source of Haskell libraries is Hackage. You should get used to the idea of googling Haskell functions, and finding the package you need on Hackage. Once you’ve found a function you want to use, you should get used to this pattern:

  1. What module is this function is?
  2. What package is that module part of?
  3. Add this package to your .cabal file and import the module into your code.
  4. Compile your code, make sure it works

Sometimes, you’ll need code that isn’t on Hackage (Github, for instance). Stack has some special ways for you to incorporate that code. You’ll need an entry in the stack.yaml file instead of just the .cabal file.

Hackage Gotchas

While we’re discussing Hackage, there are some gotchas you should be aware of when perusing Haskell documentation. First, it’s important to keep an eye on the version of the package you’re using. On many occasions, I’ve found a discrepancy between the type I had in my program and the type in the documentation. Typically, I would google the package name, and get a link to the documentation for the wrong version. And types can sometimes change between versions, so that will definitely cause problems!

A useful command is stack list-dependencies. It will show the version number for every package you are currently using. Then compare that number for your package to what is at the top of the Hackage page. If they don’t match, click the contents button in the top right. This will take you to the list of versions, where you can find the one you’re actually using!

Another gotcha can occur when you know a function name and want to find it on Hackage. You’ll import it via a generic sounding module name. And then you’ll go to the documentation for that module and find that all you have is a list of re-exported modules. This can be frustrating, especially when those modules in turn re-export other modules. You have to do a full depth first search to try to find the function you’re after. In this situation, you’ll want help from some other tools.

Hoogle and Hayoo

Hackage does have a search function. But I’ve often had better luck with Hoogle and Hayoo. Both of these are search engines written for finding things on Hackage. They’ll both help you track down the actual module a function comes from.

Hayoo especially does a good job with operators. Oftentimes, a simple google search will help you find a function with a normal sounding name (e.g. split). But Google searches are bad at finding special characters. But if you enter the specific operator in Hayoo, you have a great chance of finding the module you’re looking for!

Using Types

My last piece of documentation related advice is to learn to use types. In truth, the type of a function is the most important piece of information you need. You can generally bring up GHCI and get the type of an expression. But oftentimes you’ll only see a type synonym that is not understandable. Also, you won’t be able to see the ways in which you can create something of a particular type. If the code and documentation are well organized, you can almost always do this on Hackage. This can help guide your development process so you can fill in missing holes. For more ideas about using types to drive your development, check out this article in our Haskell Brain series!

Conclusion

Haskell is a cool language. But you’ll soon get over the cool factor of functional purity and laziness. Then it’ll be time to get down to business and start writing more serious programs. To do this, you have to master a Haskell package manager. This will help you organize your work. You’ll also have to learn a lot about the organization of Haskell documentation on Hackage.

To get started on your Haskell journey, download our Beginners Checklist and read our Liftoff Series! For some more ideas on getting to the next level, read our Haskell Brain series!

Read More