Skip to content
Iñaki edited this page Jan 10, 2018 · 7 revisions

Overview

haskell-gi has extensive support for overloading (this works best together with the OverloadedLabels extension in GHC 8.0), so code like the following works:

button <- new Button [#label := "Hi"]
on button #clicked Gtk.mainQuit

There is a simple rule for determining which overloaded labels exist: for any method ― widgetDoSomething, for example ― there is an associated overloaded label #doSomething. So

widgetDoSomething w arg1 arg2 ...

becomes

#doSomething w arg1 arg2 ...

Similarly for signals, and attributes, you just need to remove the object prefix.

All the types are checked at compile time: for example it is not possible to construct a read-only property

m <- new MessageDialog [#messageArea := undefined]

fails at compile time with

    • Attribute ‘message-area’ is not constructible.
    • In the expression: #messageArea := undefined
      In the second argument of ‘new’, namely
        ‘[#messageArea := undefined]’
      In the first argument of ‘GHC.GHCi.ghciStepIO ::
                                  forall a. IO a -> IO a’, namely
        ‘new MessageDialog [#messageArea := undefined]’

while getting the attribute works

λ> :t m `get` #messageArea
m `get` #messageArea
  :: Control.Monad.IO.Class.MonadIO m => m Widget

Functions using overloading syntax

One slightly tricky point in this whole business concerns how to write your own functions using the overloading syntax. The most straightforward attempt

f :: IsWidget w => w -> IO ()
f w = set w [#tooltipText := "Test"]

fails to compile with a scary error message. The reason is that we are requiring that the type w satisfies the IsWidget constraint, and in addition to have a property tooltipText of type compatible with "Test" (which is, by itself, overloaded if one uses OverloadedStrings). Neither of these properties implies the other, as far as the Haskell type system is concerned.

This is easily solved. One can either specialize the argument to Gtk.Widget, and then set the property

f :: IsWidget w => w -> IO ()
f w = do
    w' <- toWidget w
    set w' [#tooltipText := "Test"]

or alternatively one can keep the function as general as possible, and make it accept any type with the right property, using AttrSetC in Data.GI.Base.Attributes

import Data.GI.Base.Attributes (AttrSetC)

f :: AttrSetC info obj "tooltipText" v => obj -> v -> IO ()
f w v = set w [#tooltipText := v]

Notice that the type for values of the property tooltipText may differ for different objects, so we need to make the function parametric. For example, the relevant type may be Text, as in the Gtk+ bindings, indicating the text to show for some widget. But, conceivably, for some other type in some other library it may be a Bool, indicating whether to show the property. The design of overloaded attributes in haskell-gi is such that no assumptions on the type of an attribute are made until the attribute is associated with a specific type by new/set/get/clear (this is good, since things like #child may mean very different things for different parent types), so this forces parametricity on us.

Disabling overloading

Using overloading can slow down your build times, in that case you might want to disable overloading when building haskell-gi packages.

haskell-gi-0.21.0 or newer

If using haskell-gi version 0.21.0 or newer, just add a build-depends: haskell-gi-overloading == 0.0.* to the cabal file of your project.

Versions of haskell-gi older than 0.21.0

Before haskell-gi-0.21.0 things were a little more complicated. One would need the following stack.yaml

resolver: lts-9.3
packages:
  - . 
extra-deps:
  - gi-atk-2.0.14
  - gi-cairo-1.0.14
  - gi-gdk-3.0.14
  - gi-gdkpixbuf-2.0.14
  - gi-gio-2.0.14
  - gi-glib-2.0.14
  - gi-gobject-2.0.15
  - gi-gtk-3.0.17
  - gi-gtk-hs-0.3.5.0
  - gi-pango-1.0.15
  - haskell-gi-overloading-0.0
extra-package-dbs: []
flags:
  gi-atk:
    enable-overloading: false
  gi-cairo:
    enable-overloading: false
  gi-gdk:
    enable-overloading: false
  gi-gdkpixbuf:
    enable-overloading: false
  gi-gio:
    enable-overloading: false
  gi-glib:
    enable-overloading: false
  gi-gobject:
    enable-overloading: false
  gi-gtk:
    enable-overloading: false
  gi-gtk-hs:
    enable-overloading: false
  gi-pango:
    enable-overloading: false

And then, in .cabal file in the build-depends put haskell-gi-overloading == 0.0.*, for example:

  build-depends:
      base
    , gi-gio
    , gi-gtk
    , haskell-gi-base
    , haskell-gi-overloading == 0.0.*