# Creating Rich IHaskell Displays: An Example (static-canvas)

Recently, Jeffrey Rosenbluth published (and showcased on Reddit) a pretty cool Haskell package called static-canvas. This package uses the free monad DSL pattern to make a DSL for programming for HTML5 `canvas`, restricted to fairly simple static use cases. While you can't use this to make user interfaces, it's still potentially a pretty cool tool, and there's a few very clear examples on the GitHub readme.

As with most things involving pretty graphics or pictures, I think this would be a whole ton of fun to experiment with interactively, making it a great fit for IHaskell, an interactive notebook-based environment for Haskell.

IHaskell allows the creation of "addon" packages to specify how to display various data types in its browser-based UI. These addons can render data types as text, as images, or even as HTML mixed with Javascript; they can even render them as interactive Javascript widgets that can evaluate Haskell code at will. All of this is done without GHCJS or similar Haskell-to-Javascript compilation tools.

However, these display packages have mostly been written by only a few people, those fairly closely involved with IHaskell development. As the creator of IHaskell, I'd love to have more of these packages, but I obviously can't create display instances for all existing packages, and certainly can't anticipate what people might want for their own packages or new ones. Thus, I'd love to use this very neat library as a showcase and tutorial for how to make IHaskell display packages.

## The Tools

In this section, I'll very briefly introduce you to the tools IHaskell provides for creating IHaskell display packages. If you'd like to get to the real meat of this tutorial, skip this, read the next section, and maybe come back here if you need to.

IHaskell internally uses a data type called `Display` to represent possible outputs. The `Display` data types looks like this:

    -- In IHaskell.Display
    data Display = Display [DisplayData] -- Display just one thing.
                 | ManyDisplay [Display] -- Display several things.

In turn, the `DisplayData` data type from the `ipython-kernel` package specifies how to actually display the object in the browser:

    -- In IHaskell.IPython.Display
    data DisplayData = DisplayData MimeType Text

    -- All the possible ways to display things.
    data MimeType = PlainText
                  | MimeHtml
                  | MimePng Width Height -- Base64 encoded.
                  | MimeJpg Width Height -- Base64 encoded.
                  | MimeSvg
                  | MimeLatex
                  | MimeJavascript

For example, to output the string "Hello" in red in the browser, you might construct a value like this:

    redStr :: Display
    redStr = Display [textDisplay, htmlDisplay]

    textDisplay :: DisplayData
    textDisplay = DisplayData PlainText "Hello"

    htmlDisplay :: DisplayData
    htmlDisplay = DisplayData MimeHtml "<span style=\"color: red;\">Hello</span>"

You may note that `Display` takes a list of `DisplayData` values; this allows IHaskell to choose the proper display mechanism for the frontend. The frontend can be a console or the in-browser notebook, and the in-browser notebook may have different preferences for displays, so by providing different ways to render output, the best possible rendering can be chosen for each interface.

Instead of always using the data types, `IHaskell.Display` exports the following convenience functions:

    -- Construct displays from raw strings of different types.
    plain :: String -> DisplayData
    html :: String -> DisplayData
    svg :: String -> DisplayData
    latex :: String -> DisplayData
    javascript :: String -> DisplayData

    -- Encode into base 64.
    encode64 :: String -> Base64
    decode64 :: ByteString -> Base64

    -- Display images.
    png :: Int -> Int -> Base64 -> DisplayData
    jpg :: Int -> Int -> Base64 -> DisplayData

    -- Create final Displays.
    Display :: [DisplayData] -> Display
    many :: [Display] -> Display

## Creating a Display

In order to create a display for some data type, we must first import the main IHaskell display module:

    import IHaskell.Display

This package contains the following typeclass:

    class IHaskellDisplay a where
      display :: a -> IO Display

In order to display a data type, create an instance of `IHaskellDisplay` for your data type – then, any expression that results in your data type will generate a corresponding display.

Let's go ahead and do this for `CanvasFree a` from the `static-canvas` package.

In [12]:

    -- Start with necessary imports.
    import IHaskell.Display -- From the 'ihaskell' package.
    import IHaskell.IPython.Types(MimeType(..))
    import Graphics.Static  -- From the 'static-canvas' package.

    -- Text conversion functions.
    import Data.Text.Lazy.Builder(toLazyText)
    import Data.Text.Lazy(toStrict)

Now that we have the imports out of the way, we can define the core instance necessary:

In [24]:

    -- Since CanvasFree is a type synonym, we need a language pragma.
    {-# LANGUAGE TypeSynonymInstances #-}

    instance IHaskellDisplay (CanvasFree ()) where
      -- display :: CanvasFree () -> IO Display
      display canvas = return $
        let src = toStrict $ toLazyText $ buildScript width height canvas
        in Display [DisplayData MimeHtml src]

        where (height, width) = (200, 600)

We can now copy and paste the examples from the `static-canvas` Github page, and see them appear right in the notebook!

In [34]:

    {-# LANGUAGE OverloadedStrings #-}
    import Graphics.Static.ColorNames

    text :: CanvasFree ()
    text = do
      font "italic 60pt Calibri"
      lineWidth 6
      strokeStyle blue
      fillStyle goldenrod
      textBaseline TextBaselineMiddle
      strokeText "Hello" 150 100
      fillText "Hello World!" 150 100

    text

As we play with this a little more, we see that this is a little bit unsatisfactory. Specifically, the width and the height of the resulting canvas are fixed in the `IHaskellDisplay` instance! I would solve this by creating a custom `Canvas` data type that stores these:

In [26]:

    data Canvas = Canvas {
        width :: Int,
        height :: Int,
        canvas :: CanvasFree ()
      }

Then we could define an `IHaskellDisplay` that respects this width and height:

In [27]:

    {-# LANGUAGE TypeSynonymInstances #-}
    instance IHaskellDisplay Canvas where
      -- display :: Canvas -> IO Display
      display cnv = return $
        let src = toStrict $ toLazyText $ buildScript (width cnv) (height cnv) (canvas cnv)
        in Display [DisplayData MimeHtml src]

Then when we use this we can specify how to display our canvases:

    Canvas 200 600 $ do
      font "italic 60pt Calibri"
      lineWidth 6
      strokeStyle blue
      fillStyle goldenrod
      textBaseline TextBaselineMiddle
      strokeText "Hello" 150 100
      fillText "Hello World!" 150 100

Sadly, it seems that the `static-canvas` library currently only supports having one generated canvas on the page – if you try to add another one, it simply modifies the pre-existing one. This is probably a bug that should be fixed, though!

## Packaging IHaskell Display Addons

Once you've made an IHaskell display instance, you can easily package it up and stick it on Hackage. Specifically, for a package named `package-name`, you should take everything before the `-`. Then, prepend `ihaskell-` to the package name. Finally, make sure there exists a module `IHaskell.Display.Package`, where `Package` is the first word in `package-name` capitalized. If this is done, then IHaskell will happily load your package and instance upon startup, making it very easy for your users to install the display addon!

For example, the `hatex` library is exposed as an addon through the `ihaskell-hatex` display package and the `IHaskell.Display.Hatex` module in that package. The `juicypixels` library has an addon package called `ihaskell-juicypixels` with a module `IHaskell.Display.Juicypixels`.

As I write this now, I realize that this protocol is a little bit weird. Specifically, I think that perhaps the rule that you take the first thing before the `-` is not too great, but rather that perhaps the `-` should be a word separator, and thus `package-name` would get translated to `ihaskell-package-name` and `IHaskell.Display.PackageName`. (We do need some standard!)

If you have any opinions about this, or suggestions for how to improve this process, please let me know!

Anyway, I hope that this brief tutorial / guide can show someone how to write small IHaskell addons. Perhaps someone will find this useful, and please get in touch if you have any questions, comments, or suggestions!