Difference between revisions of "LGtk"
Jump to navigation
Jump to search
m (→Types) |
(→External links: remove dead link) |
||
(9 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
[[Category:User interfaces]] |
[[Category:User interfaces]] |
||
− | == What is it? == |
||
− | LGtk is a |
+ | 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 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]] |
||
− | == |
+ | == 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 |
+ | * [http://hackage.haskell.org/package/lgtk Haddock documentation on HackageDB] |
− | * [ |
+ | * [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 32: | ||
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] |
Latest revision as of 21:08, 22 July 2023
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
Internal links
Should be revised:
External links
- Haddock documentation on HackageDB
- GitHub repository
- Initial announcement
- haskell.org wiki page (this page)
Related Stackoverflow questions:
Reddit comments: