Haskell Relational Record

Quick start

View project on GitHub

Preparing HRR

Following installation methods are selectable

Debian sid / Debian stable (stretch) / Ubuntu - universe

% sudo apt-get install haskell-relational-record


openSUSE Tumbleweed

# zypper addrepo http://download.opensuse.org/repositories/devel:languages:haskell:lts:10/openSUSE_Tumbleweed/devel:languages:haskell:lts:10.repo
# zypper refresh
# zypper install ghc-relational-record-devel

Build with stack

% stack build relational-record

You may want to use LTS Haskell release of stackage. For example, command-line to build with LTS-11:

% stack --resolver lts-11 build relational-record

You can use LTS-11.x or older LTS Haskell releases, LTS-10.x, LTS-9.x, LTS-8.x, LTS-7.x or LTS-6.x.

Build with cabal

To start using Haskell Relational Record (HRR), you need to install:

  1. Glasgow Haskell Compiler (GHC) + the “cabal” command
  2. The Haskell “relational-record” library

To install the Haskell “relational-record” library, run the following commands:

% cabal update
% cabal install relational-record

Prepare Relational database system

Relational database system - In this quickstart, we assume that SQLite version 3 has been installed

The first relation

In HRR, select statements are called relations. Let’s define our first relation. Copy the following to “hello.hs”:

import Database.Relational -- for LTS-11 or LTS-10
-- import Database.Relational.Query -- instead of "import Database.Relational" for LTS-9 or LTS-8

hello :: Relation () (Int, String)
hello = relation $ return (value 0 >< value "Hello")

main :: IO ()
main = putStrLn $ show hello ++ ";"

hello defines our first relation. This “SELECT”s a constant tuple value (0,”Hello”) from the (virtual) empty table. In other words, this relation just returns (0,”Hello”).

Let’s run the above Haskell code to show what kind of SQL statement is generated:

% runghc hello.hs
SELECT ALL 0 AS f0, 'Hello' AS f1;

OK. Next, let’s execute the SQL produced above in SQLite:

% runghc hello.hs | sqlite3 dummy.db

We got “0|Hello”! Note that “dummy.db” is just a dummy file.

Composing relations

Next, let’s compose relations. Copy the following to “helloworld.hs”:

import Data.Functor.ProductIsomorphic
import Database.Relational

hello :: Relation () (Int, String)
hello = relation $ return (value 0 >< value "Hello")

world :: Relation () (Int, String)
world = relation $ return (value 0 >< value "World!")

helloWorld :: Relation () (Int, String, String)
helloWorld = relation $ do
    h <- query hello
    w <- query world
    on $ h ! fst' .=. w ! fst'
    return $ (,,) |$| h ! fst' |*| h ! snd' |*| w ! snd'

main :: IO ()
main = putStrLn $ show helloWorld ++ ";"

(You may use older LTS. Please check LTS-9 - helloworld.hs or LTS-8 - helloworld.hs.)

This code defines queries called hello and world. And helloworld composes them by joining them on the first element of the tuples.

This code generates the following SQL statement:

% runghc helloworld.hs
SELECT ALL T0.f0 AS f0, T0.f1 AS f1, T1.f1 AS f2 FROM (SELECT ALL 0 AS f0, 'Hello' AS f1) T0 INNER JOIN (SELECT ALL 0 AS f0, 'World!' AS f1) T1 ON (T0.f0 = T1.f0);

Finally, let’s execute it in SQLite:

% runghc helloworld.hs | sqlite3 dummy.db

Now we understand that relations are composable. Raw SQL does NOT have this feature. Moreover, relations are type safe. If our HRR code can be compiled by GHC, it always generates valid SQL statements.

The next step is to read the HRR tutorial.