Difference between revisions of "LGtk"

From HaskellWiki
Jump to navigation Jump to search
(7 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
[[Category:User interfaces]]
 
[[Category:User interfaces]]
== What is it? ==
 
   
LGtk is a lens-based API for Gtk.
+
LGtk is a GUI Toolkit.
LGtk is built on Gtk2Hs.
 
   
  +
Main goals of LGtk:
Most Haskellers would like to use a mature FRP-based API for creating graphical user interfaces.
 
FRP may not be the best tool for special user interfaces, like interfaces which consists of buttons, checkboxes, combo boxes, text entries and menus only.
 
LGtk has a lens-based API which fits better these user interfaces.
 
   
  +
* Provide a Haskell EDSL for declarative description of interactive graphical applications
== Semantics overview ==
 
  +
* Provide an API for custom widget design
 
  +
* Provide a playground for high-level declarative features like derived state-save and undo-redo operations and type-driven GUI generation
The semantics of LGtk is given by a reference implementation.
 
The reference implementation is given in the following stages.
 
 
=== Lenses ===
 
 
LGtk use simple lenses defined in the data-lens package:
 
 
<haskell>
 
newtype Lens a b = Lens { runLens :: a -> Store b a }
 
</haskell>
 
 
This data type is isomorphic to <hask>(a -> b, b -> a -> a)</hask>, the well-know lens data type. The isomorphism is established by the following operations:
 
 
<haskell>
 
getL :: Lens a b -> a -> b
 
</haskell>
 
 
<haskell>
 
setL :: Lens a b -> b -> a -> a
 
</haskell>
 
 
<haskell>
 
lens :: (a -> b) -> (b -> a -> a) -> Lens a b
 
</haskell>
 
 
==== Lens laws ====
 
 
The three well-known laws for lenses:
 
 
* get-set: <hask>set k (get k a) a</hask> === <hask>a</hask>
 
* set-get: <hask>get k (set k b a)</hask> === <hask>b</hask>
 
* set-set: <hask>set k b2 (set k b1 a)</hask> === <hask>set k b2 a</hask>
 
 
Impure lenses, i.e. lenses which
 
brake lens laws are allowed in certain places. Those places are explicitly marked and explained in this overview.
 
 
=== References ===
 
 
It is convenient to use lenses with the state monad
 
([http://www.haskellforall.com/2013/05/program-imperatively-using-haskell.html see this use case]) and the reader monad.
 
 
Let's call a value with type <hask>Lens s a</hask> a '''reference''' of the state monad <hask>State s</hask> and the reader monad <hask>Reader s</hask>.
 
 
References are distinguished from lenses, because we would like to make the program state <hask>s</hask> unaccessible to guarantee linear use of the state for example.
 
 
There are several ways to model references in Haskell.
 
LGtk models them as type classes with associated types, but that is just a technical detail.
 
 
==== Types ====
 
 
Instead of giving a concrete implementation in Haskell, suppose that
 
 
* <hask>s</hask> is a fixed type,
 
* <hask>Ref :: * -> *</hask> ~ <hask>Lens s</hask>, references are lenses from <hask>s</hask> to the type of the referred value,
 
* <hask>R :: * -> *</hask> ~ <hask>Reader s</hask>, the '''reference reading monad''' is the reader monad over <hask>s</hask>,
 
* <hask>S :: * -> *</hask> ~ <hask>State s</hask>, the '''reference reading and writing monad''' is the state monad over <hask>s</hask>.
 
 
The three equality constraints are not exposed in the API.
 
 
==== Operations ====
 
 
Exposed operations of <hask>Ref</hask>, <hask>S</hask> and <hask>R</hask>:
 
 
* The <hask>Monad</hask> instance of <hask>S</hask> and <hask>R</hask>
 
 
* The monad morphism between <hask>R</hask> and <hask>S</hask>
 
 
<haskell>
 
liftReadPart :: R a -> S a
 
liftReadPart = gets . runReader
 
</haskell>
 
 
* Reference read
 
 
<haskell>
 
readRef :: Ref a -> R a
 
readRef = reader . getL
 
</haskell>
 
 
* Reference write
 
 
<haskell>
 
writeRef :: Ref a -> a -> S ()
 
writeRef = modify . setL r
 
</haskell>
 
 
* Lens application on a reference
 
 
<haskell>
 
lensMap :: Lens a b -> Ref a -> Ref b
 
lensMap = (.)
 
</haskell>
 
 
* Reference join
 
 
<haskell>
 
joinRef :: R (Ref a) -> Ref a
 
joinRef = Lens . join . (runLens .) . runReader
 
</haskell>
 
 
* The unit reference
 
 
<haskell>
 
unitRef :: Ref ()
 
unitRef = lens (const ()) (const id)
 
</haskell>
 
 
Note that the identity lens is '''not''' a reference because that would leak the program state <hask>s</hask>.
 
 
==== Reference creation ====
 
 
There should be a possibility for new reference creation too.
 
 
New reference creation with a given initial value extends the state. For example, if the state is <hask>(1, 'c') :: (Int, Char)</hask>, extending the state with <hask>True :: Bool</hask> would yield the state <hask>((1, 'c'), True) :: ((Int, Char), Bool)</hask>.
 
 
We could model the type change of the state with an [http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Monad-Indexed.html indexed monad], but that would complicate both the API and the implementation too.
 
 
Instead of changing the type of the state, we use an '''extensible state''', a data type <hask>S</hask> with the operations
 
 
<haskell>
 
empty :: S
 
</haskell>
 
 
<haskell>
 
extend :: a -> State S (Lens S a)
 
</haskell>
 
 
such that the following laws hold:
 
 
* <hask>extend v >> return ()</hask> === <hask>return ()</hask>
 
 
* <hask>extend v >>= liftReadPart . readRef</hask> === <hask>return v</hask>
 
 
The first law sais that <hask>extend</hask> has no side-effect, i.e. extending the state does not change the values of existing references.
 
The second law sais that extending the state with value <hask>v</hask> creates a reference with inital value <hask>v</hask>.
 
 
Is there a state with a well-behaved extend function?
 
 
The answer is '''yes''', but we should guarantee linear usage of the state. This is great, because linear usage is guaranteed with the restricted API!
 
 
How can such an <hask>S</hask> be implemented? And how can such an <hask>S</hask> be implemented efficiently? These questions are not relevant for the semantics, but there is an efficient implementation with <hask>IORef</hask>s.
 
 
==== Reference creation API ====
 
 
Let <hask>S</hask> be an extensible state as specified before.
 
Let <hask>s</hask> = <hask>S</hask> in the definition of references.
 
 
* <hask>C :: (* -> *) -> * -> *</hask> ~ <hask>StateT S</hask>, the '''reference creating monad''' is the state monad transformer over <hask>S</hask>.
 
 
The equality constraint is not exposed in the API.
 
The following functions are exposed:
 
 
* New reference creation
 
 
<haskell>
 
newRef :: Monad m => a -> C m (Ref a)
 
newRef v = mapStateT (return . runIdentity) $ extend v
 
</haskell>
 
 
* Lift reference read and write operations
 
 
<haskell>
 
liftWrite :: Monad m => S a -> C m a
 
liftWrite = mapStateT (return . runIdentity)
 
</haskell>
 
 
* Derived <hask>MonadTrans</hask> instance of <hask>C</hask> to be able to lift operations in the <hask>m</hask> monad.
 
 
Note that <hask>C</hask> is parameterized by a monad because in this way it is easier to add other effects later.
 
 
==== Running ====
 
 
The last missing function in the API is the running of the <hask>C m</hask> monad.
 
 
* Run the reference creation monad
 
 
<haskell>
 
runC :: Monad m => C m a -> m a
 
runC x = runStateT x empty
 
</haskell>
 
 
=== Lens-trees ===
 
 
TODO
 
 
=== Effects ===
 
 
TODO
 
 
== Examples ==
 
 
=== Hello World ===
 
 
<haskell>
 
main = runWidget $ label $ return "Hello World!"
 
</haskell>
 
 
<hask>return</hask> is neded because labels may be dynamic, see the next examples.
 
 
=== Copy ===
 
 
The following applications presents an entry and a label below of it.
 
When a text is entered in the entry, the label is changed to the entered text.
 
 
<haskell>
 
main = runWidget $ action $ do
 
r <- newRef "enter text here"
 
return $ vcat
 
[ entry r
 
, label $ readRef r
 
]
 
</haskell>
 
 
<hask>action</hask> gives acces to a monad in which new references can be made by <hask>newRef</hask>.
 
A crutial feature of LGtk is that you cannot change the value of references in this monad (you can read them though).
 
 
<hask>label</hask> gives acces to a monad in which you can read references but no reference creation or write is possible.
 
 
=== Addition ===
 
 
Two entries of integers and a label which shows the sum:
 
 
<haskell>
 
main = runWidget $ action $ do
 
r1 <- newRef (12 :: Integer)
 
r2 <- newRef 4
 
return $ vcat
 
[ entryShow r1
 
, entryShow r2
 
, label $ liftM show $ liftM2 (+) (readRef r1) (readRef r2)
 
]
 
</haskell>
 
 
=== Complex examples ===
 
 
You can find more complex examples in the source code of LGtk.
 
More examples will be presented here also.
 
   
 
[[Image:Lgtkdemo.png]]
 
[[Image:Lgtkdemo.png]]
   
== Status ==
+
== Internal links ==
 
Features of lgtk-0.5
 
 
* The API is closed, you can safely use any constructs as far as you obey the documented laws.
 
* Support for asynchronous events. Using LGtk is a safe way for writing multithreaded Gtk applications.
 
* The semantics is getting stable but it is not yet documented.
 
 
TODO list:
 
 
* Add an efficient implementation for LGtk. LGtk has only a reference implementation currently.
 
* Add support for styles (layout, colors, etc).
 
* Support more Gtk constructs.
 
 
 
== Demo application ==
 
 
You can try the demo application with the following commands:
 
 
cabal install gtk2hs-buildtools
 
cabal install lgtk
 
lgtkdemo
 
 
== Changelog ==
 
 
=== lgtk-0.5.1 ===
 
 
* Documentation fixes and cleanup
 
* Try to support Haskell Platform 2012.4.0.0
 
   
  +
Should be revised:
=== lgtk-0.5 ===
 
   
  +
* [[LGtk/Semantics]]
* Do not use monadic lenses any more.
 
  +
* [[LGtk/ADT_lenses]]
* Support for asynchronous events.
 
* Lazily created tabs.
 
* Unactive tabs are really unactive (event handlers are detached).
 
* File references watch the files. When the file changes, the GUI is updated.
 
* References with inherent identity (makes defining auto-sensitive buttons easier)
 
* Experimental support for colored buttons.
 
* More examples in the demo application.
 
* Lots of inner changes.
 
   
 
== External links ==
 
== External links ==
   
* [http://hackage.haskell.org/package/lgtk API documentation at HackageDB]
+
* [http://hackage.haskell.org/package/lgtk Haddock documentation on HackageDB]
* [http://hub.darcs.net/divip/lgtk Darcs repo on hub.darcs.net]
+
* [http://lgtk.wordpress.com/ Wordpress blog]
  +
* [https://github.com/divipp/lgtk GitHub repository]
 
* [http://people.inf.elte.hu/divip/LGtk/LGtk.html Initial announcement]
 
* [http://people.inf.elte.hu/divip/LGtk/LGtk.html Initial announcement]
  +
* [http://www.haskell.org/haskellwiki/LGtk haskell.org wiki page (this page)]
   
 
Related Stackoverflow questions:
 
Related Stackoverflow questions:
Line 306: Line 33:
 
Reddit comments:
 
Reddit comments:
   
* [http://www.reddit.com/r/haskell/comments/1cemr2/lgtk_lensbased_gtk_interface/ 15 April]
+
* [http://www.reddit.com/r/haskell/comments/1cemr2/lgtk_lensbased_gtk_interface/ 15 April 2013]
* [http://www.reddit.com/r/haskell/comments/1cj0o6/lgtk_api_fixed/ 17 April]
+
* [http://www.reddit.com/r/haskell/comments/1cj0o6/lgtk_api_fixed/ 17 April 2013]
* [http://www.reddit.com/r/haskell/comments/1f5m5j/ann_lgtk_05/ 27 May]
+
* [http://www.reddit.com/r/haskell/comments/1f5m5j/ann_lgtk_05/ 27 May 2013]
  +
* [http://www.reddit.com/r/haskell/comments/1fjerf/lens_chains_lgtk_semantics_first_part/ 2 June 2013]
  +
* [http://www.reddit.com/r/haskell/comments/25k9x1/ann_lgtk08/ 14 May 2014]

Revision as of 19:41, 14 May 2014


LGtk is a GUI Toolkit.

Main goals of LGtk:

  • Provide a Haskell EDSL for declarative description of interactive graphical applications
  • Provide an API for custom widget design
  • Provide a playground for high-level declarative features like derived state-save and undo-redo operations and type-driven GUI generation

Lgtkdemo.png

Internal links

Should be revised:

External links

Related Stackoverflow questions:

Reddit comments: