The Standard Library

As well as the compiler infrastructure, Hobbes comes packed with a rich Standard Library of functions and types that make it easy to get started.

They’re all available in the default namespace, and we’ve used quite a few of them already throughout the documentation.

In this section, we’ll draw your attention to some of the most common and useful elements of the Hobbes Standard Library.

All the members here are written in plain, vanilla Hobbes - so digging into the source on GitHub is a great way to learn the language!

Simple Arithmetic

These Type Classes, and their instances (what we’d call a ‘realisation’ of the type class for a concrete type) allow Hobbes to handle arithmetic in a polymorphic manner:

class Add a b c | a b -> c where
  (+) :: (a,b) -> c

instance Add int int int where
  (+) = iadd

instance Add long long long where
  (+) = ladd

...

Because the Add Type Class (and the rest of the family: Subtract, Multiply, and Divide) is available in the global namespace, I can use its instances implicitly - i.e., the + operator is defined for all the basic types:

> 1 + 2
3

Note

built in?

It’s important to note that the + operator isn’t “built in” to Hobbes as operators might be in other programming languages. The resolution of + works because the compiler has recognised that there is a typeclass Add which provides an operator called + that can act on two ints.

Indeed, we can support + elsewhere by instantiating the Type Class for our own types:

type counter = { count: int}

counterAdd = (\x y. { count = iadd(x.count,y.count)})

instance Add counter counter counter where
  (+) = counterAdd

Note

type definitions in hi

Just like in Haskell, the hi REPL doesn’t support type definitions. So in this case, we’ve included the above Hobbes code in a file called counter.hob and instructed the hi REPL to read it at startup. The members defined in counter.hob can then be used directly.

$ ./hi counter.hob
loaded module 's.hob'

> {count = 22} + {count = 33}
55
>

Maybe

Maybe is deeply idiomatic in functional programming, and is starting to seep into more popular languages too.

Effectively, it’s a clean handling of the case where a function may legitimately not be able to provide a value.

Note

Maybe

Consider a function getUser([char]) -> User, which gets user information from some external source, when given the users name.

What would you expect the function to return if a user with that name was not found? It’s an entirely reasonable situation and not really exceptional at all.

Indeed, in some languages you’d expect the function to throw a UserNotFoundException, which you’d have to catch and deal with outside the regular flow of control.

In Functional Languages, we’d change the signature of the function to return a Maybe type.

The maybe is really a sum type that looks like this:

(()+a)

That is, it can either have a value, or it can have no value. You instantiate maybe with the two constructors just and nothing:

> maybenums = [just(1), just(2), nothing, just(3)]
maybenums = [1, 2, , 3]
> dropNulls(maybenums)
[1, 2, 3]

Maybe has a large number of utility functions that deal with either case - i.e. where there is a value and where there is no value.

> map((\x.x+2), maybenums)
[3, 4, , 5]

fromMaybe(0, maybenums[1])
> 2

fromMaybe(0, maybenums[2])
> 0

In the above example we’re extracting the value from the maybe if one exists, or else we’re providing a sensible default (in this case, the integer 0).