A tour of IHaskell extensions and integrations

Posted by Pythux on Mon 11 January 2016

Introduction

In my previous post, I tried to provide a step-by-step explanation of how to install IHaskell on Ubuntu 14.04 (Should also work on other versions). Now is time to start using it!

This post is not a quickstart on how to use IHaskell, as it has already been covered in the official documentation. And there are more advanced examples here:

Instead, I'll focus more on something a bit more mysterious for me: displaying custom Haskell types in notebooks. I'll first try to give a quick explanation on how it works, and then give basic examples of the provided integrations with existing libraries (aeson, blaze, charts, diagrams, etc.). I'll also try to show how to support your custom types.

Disclaimer: Some of the information found in the post may be redundant with other sources (like official documentation, and in particular the last post in the list above: Static Canvas IHaskell Display), but I hope this post will bring value by giving an overview of what is possible with IHaskell, explained with the words of a newcomer. Comments and fixes would be greatly appreciated!

How does it work?

Jupyter allows you to embed arbitrary HTML, and this mechanism is used by IHaskell to display values in custom ways. The IHaskellDisplay typeclass is used to this effect. By providing an instance for your own types, they can be displayed in notebooks (we'll see later that some extensions already exist to provide such display to known Haskell libraries). A Display can be of several types, but for now we will focus on html and plain (see this notebook for more information).

Note that you can provide several choices of display outputs so that your custom type can be display in notebooks (html) or console (plain text), the frontend will then select the best choice.

In [15]:
import IHaskell.Display

-- Custom data type
data Answer = Answer

-- Make it "displayable"
instance IHaskellDisplay Answer where
    -- List of two kinds of Display: html and plain text
    display value = return $ Display [htmlDisplay, txtDisplay]
        where
            -- HTML Display
            htmlDisplay = html "<div>The answer is 42!</div>"
            -- Plain Text Display
            txtDisplay = plain "42"

-- Display an instance of our type
Answer
The answer is 42!

In the following sections we'll see how to use some of the extensions officially provided by IHaskell to display types of known libraries. The packages we'll need are:

  • ihaskell-basic
  • ihaskell-aeson
  • ihaskell-blaze
  • ihaskell-charts
  • ihaskell-diagrams
  • ihaskell-magic

You can install them using stack if you intend to try this out yourself:

stack build ihaskell-basic ihaskell-aeson ihaskell-blaze ihaskell-charts ihaskell-diagrams ihaskell-magic

ihaskell-basic

IHaskell basic contains "Instances of IHaskellDisplay for default prelude data types". Currently, only Maybe seems to be supported. Maybe some more Displays will be provided in the future?

Anyway, here is how you can use it, and what it looks like:

In [2]:
-- import IHaskell.Display.Basic
Just 42
Just (Just "Foo Bar Baz")
Nothing
Just42
JustJust "Foo Bar Baz"
Nothing

ihaskell-aeson

Aeson is a library used to manipulate JSON format from Haskell. It allows you to use ToJSON and FromJSON typeclasses to convert your custom data-types to and from JSON format.

In our small example, we declare a type of document with an arbitrary number of metadata attached, here is how we could do it:

In [34]:
:extension OverloadedStrings

import qualified Data.Text as T
import qualified Data.Aeson as A
-- IHaskell.Display.Aeson

newtype Property = Property T.Text
newtype Value = Value T.Text
data Metadata = Metadata [(Property, Value)]

newtype Body = Body T.Text
newtype Title = Title T.Text
data Document  = Document
    { _title :: Title
    , _body :: Body
    , _metadata :: Metadata}

instance A.ToJSON Metadata where
   toJSON (Metadata d) = A.object $ [p A..= v | (Property p, Value v) <- d]

instance A.ToJSON Document where
   toJSON (Document (Title t) (Body b) m) = A.object [
       "title" A..= t,
       "body" A..= b,
       "metadata" A..= A.toJSON m]

document = let body = Body "Lorem Ipsum"
               title = Title "Foo Bar"
               metadata = Metadata [
                   (Property "Encoding", Value "UTF-8"),
                   (Property "Author", Value "Jonh Doe")]
           in Document {_title=title, _body=body, _metadata=metadata }

A.Null
A.Bool True
A.toJSON document
null
true
{ "body": "Lorem Ipsum", "metadata": { "Author": "Jonh Doe", "Encoding": "UTF-8" }, "title": "Foo Bar" }

ihaskell-blaze

Blaze is a fast combinator library used to assemble HTML documents directly in Haskell code (Embedded Domain Specific Language). According to the official description, "the project is aimed at those who seek to write web applications in Haskell – it integrates well with all Haskell web frameworks."

In [4]:
-- This example if from the IHaskell official introduction
:extension OverloadedStrings

-- import IHaskell.Display.Blaze
import Control.Monad
import Prelude hiding (div, id)
import qualified Text.Blaze.Html4.Strict as B
import qualified Text.Blaze.Html4.Strict.Attributes as A

forM [1..5] $ \size -> do
  let s = B.toValue $ size * 70
  B.img B.! A.src "https://www.google.com/images/srpr/logo11w.png" B.! A.width s

ihaskell-charts

Charts is a "2D charting library for haskell". Here a two examples taken from the official wiki on github. To adapt examples to notebooks, you must replace any reference of toFile, etc. by toRenderable.

In [5]:
-- This example is taken from the wiki: https://github.com/timbod7/haskell-chart/wiki/example%205
import qualified Graphics.Rendering.Chart.Easy as E

values :: [(String, Double, Bool)]
values = [ ("Mexico City", 19.2, False)
         , ("Mumbai", 12.9, False)
         , ("Sydney", 4.3, False)
         , ("London", 8.3, False)
         , ("New York",8.2,True)]

pitem (s, v, o) = E.pitem_value E..~ v
              $ E.pitem_label E..~ s
              $ E.pitem_offset E..~ (if o then 25 else 0)
              $ E.def

E.toRenderable 
  $ E.pie_title E..~ "Relative Population"
  $ E.pie_plot . E.pie_data E..~ map pitem values
  $ E.def
In [6]:
-- This example is taken from the wiki: https://github.com/timbod7/haskell-chart/wiki/example%2012
import qualified Graphics.Rendering.Chart.Easy as E

r' x y z = sqrt $ x^2 + y^2 + z^2
efield sign x y = (sign * x / r, sign * y / r) where r = r' x y 10
bfield sign x y = (-sign * y / r^2, sign * x / r^2) where r = r' x y 10
square a s = [(x, y) | x <- range, y <- range] where range = [-a, -a + s..a] :: [Double]
add (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

ef (x, y) = efield 1 (x - 20) y `add` efield (-1) (x + 20) y
bf (x, y) = bfield 1 (x - 20) y `add` bfield (-1) (x + 20) y
grid = square 30 3

vectorField title f grid = fmap E.plotVectorField $ E.liftEC $ do
    c <- E.takeColor  
    E.plot_vectors_mapf E..= f
    E.plot_vectors_grid E..= grid
    E.plot_vectors_style . E.vector_line_style . E.line_color E..= c
    E.plot_vectors_style . E.vector_head_style . E.point_color E..= c
    E.plot_vectors_title E..= title

main = E.toRenderable $ do
    E.setColors [E.opaque E.black, E.opaque E.blue]

    E.layout_title E..= "Positive and Negative Charges"
    E.plot $ vectorField "Electric Field" ef grid
    E.plot $ vectorField "B-field" bf grid
main

ihaskell-diagrams

Diagrams is a "powerful, flexible, declarative domain-specific language for creating vector graphics". That is, it provides a flexible embedded DSL used to describe vectorized figures that you can then render using different backends.

In [7]:
:extension NoMonomorphismRestriction
:extension FlexibleContexts
:extension GADTs

import qualified Diagrams.Prelude as D
import qualified Diagrams.TwoD.Sunburst as S
import Data.Tree (unfoldTree)

aTree = unfoldTree (\n -> (0, replicate n (n - 1))) 6
diagram $ S.sunburst aTree D.# D.centerXY D.# D.pad 1.1