Overloading
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
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.
Using overloading can slow down your build times, in that case you might want to disable overloading when building haskell-gi packages.
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.
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.*