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

-- Make it "displayable"
-- 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,

document = let body = Body "Lorem Ipsum"
title = Title "Foo Bar"
(Property "Encoding", Value "UTF-8"),
(Property "Author", Value "Jonh Doe")]

A.Null
A.Bool True
A.toJSON document

null
true
{ "body": "Lorem Ipsum", "metadata": { "Author": "Jonh Doe", "Encoding": "UTF-8" }, "title": "Foo Bar" }

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

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


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


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