Personal tools

Talk:Phooey

From HaskellWiki

Revision as of 04:27, 20 August 2007 by Conal (Talk | contribs)

Jump to: navigation, search

1 Getting Phooey to Run

This is very interesting. Getting this apparently simple proof of concept working is difficult for me. I've tried on Suse 10.2 and Windows XP. files I used on XP:

wxMSW-2.4.2-setup.zip

ghc-6-4-bld2.msi

wxhaskell-bin-msw2.4.2-ghc6.4-0.9.4-1.zip

The recommended wxwindows 2.4.2 requires a wxhaskell dependent on ghc640. The problem is that arrows require base >=2.0 I could update Cabal to 1.1.7 using darcs etc. But I find no instructions on how to update the base-1.0 which comes with GHC 6.4.0. Even with instructions I think it requires a C compiler or more?


I use WinXP, GHC 6.6, and wxWidgets 2.4.2. I compiled wxWidgets and wxHaskell from sources. Conal 23:47, 20 February 2007 (UTC)


Yes, this looks nice; I can report partiual success on linux. I was able to build using ghc6.6 on Fedora 5, using wxGTK version 2.6.3 (built from source) and then wxHaskell. Phooey examples ran, however titles (window and panel) did not appear correctly, nor did text output, e.g. on the "shopping list" example the "total" field remains blank. On checking the wxHaskell demos, it seems the problem is in there. User:Djd 04:54, 14 May 2007

Thanks for the report. I expected wxHaskell to take care of cross-platform compatibility, but I guess not. Hm. Conal 17:26, 14 May 2007 (UTC)

2 Adding additional compound widgets

Hi Conal, I am looking to extend Phooey to include a record editor widget, a record list widget, as well as a tree widget. The first two being based on Grid. This is so that I can continue my work on HGene. i.e. the record editor would allow users to edit Person records, the list widget would show the children of a person and the tree to allow them to view a tree of Persons. For the editor I have so far (in the imperative world so far)

-- List of field name, field getter, field setter
type EditorProps a = [ ( String, a -> String, a -> String -> a )]
 
 
-- Create an editor for 'a'. 
-- Returns a grid, a means of reseting the record being edited, an extractor and notifier 
mkEditor :: Show a => Window b -> EditorProps a -> a  -> IO ( (Grid (), a -> IO(), IO a, IO () -> IO () ))

Is this in line with where you think Phooey can go and is this on the right track? Mark_Wassell

Hi Mark. Your design looks good to me. I'd love so hear/see how it goes. Conal 15:39, 14 August 2007 (UTC)
Hi Conal, Take a look at Extending Phooey for what I have so far. Mark_Wassell
Wonderful! Hey, I see you're using Phooey's Monad interface. Have you tried doing your extensions with the Applicative interface? I'm planning to eliminate the Monad & Arrow interfaces and just leave Applicative. I can instead keep Monad and continue to layer Applicative on top of it (
A.UI = M.UI `O` Source
), if there's value in doing so. Conal 14:53, 18 August 2007 (UTC)
I see your UI is defined recursively (
mdo
), which works for the monadic interface, but not for the applicative one. I don't even know what the interface for would be for applicatives. Perhaps
afix :: f (a -> a) -> f a
. Conal 16:07, 18 August 2007 (UTC)

3 Intermediate results and self-updating widgets

Hi Conal, I have been experimenting with Phooey for a little bit now. I think it is very cool how you separate the layout of elements and the wiring of which widgets affects other widgets, while keeping the library functional (as opposed to imperative). Thus, if I sound negative below it is just because I am playing the devils advocate.

There seem to be no easy way of representing intermediate results. For example, if you have apples, bananas, shovels, and spades. And you want to have two intermediate results, namely fruits and tools. I came up with the following program:

-- Have to make phantom1 & phantom2, otherwise the intermediate results are not shown. We also
-- need to reference them from the last line in the UI-monad.
basket :: UI (Source ())
basket
    = title "Shopping List" $
      do a <- apples
         b <- bananas
         let fruitsTotal = liftA2 (+) a b
         phantom1 <- title "Fruit" $ showDisplay fruitsTotal
         c <- shovels
         d <- spades
         let toolsTotal = liftA2 (+) c d
         phantom2 <- title "Tools" $ showDisplay toolsTotal
         title "Basket" $ showDisplay $ liftA2 (+) fruitsTotal (liftA3 (const . const) toolsTotal phantom1 phantom2)
         -- The code below is not enough to show the intermediate results:
         -- title "Basket" $ showDisplay $ liftA2 (+) fruitsTotal toolsTotal

apples, bananas, shovels, spades :: UI (Source Int)
apples  = title "apples"  $ sl0 3
bananas = title "bananas" $ sl0 7
shovels = title "shovels" $ sl0 2
spades  = title "spades"  $ sl0 5

It seems to be more troublesome than it should be.

I was thinking that if we had an intermediate result widget defined as:

type IRWidget a = a -> Source a -> UI (Source a)

then we could make the basket program simpler. I have tried to do just that:


{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -Wall #-}

module Examples.IRWidget where

import qualified Graphics.UI.WX as WX
import Control.Applicative
import Graphics.UI.Phooey.Monad
import Control.DataDriven
import Control.Monad.Writer
import Graphics.UI.Phooey.Imperative
import Control.Compose

-- IR = Intermediate Result
type IRWidget a = a -> Source a -> UI (Source a)

run :: IO ()
run = runUI basket

basket :: UI (Source ())
basket
    = title "Shopping List" $
      do a <- apples
         b <- bananas
         fruits <- title "Fruit" $ showDisplayIR 0 (liftA2 (+) a b)
         c <- shovels
         d <- spades
         tools  <- title "Tools" $ showDisplayIR 0 (liftA2 (+) c d)
         title "Basket" $ showDisplay $ liftA2 (+) fruits tools

apples, bananas, shovels, spades :: UI (Source Int)
apples  = title "apples"  $ sl0 3
bananas = title "bananas" $ sl0 7
shovels = title "shovels" $ sl0 2
spades  = title "spades"  $ sl0 5

sl0 :: IWidget Int
sl0 = islider (0,10)

-- An output widget, which also returns a value
--
-- However, it unfortunately do not allow recursiveness. Could be usefull for creating a
-- sorted list.
irWidget :: (WX.Commanding widget, WX.Widget widget) => MkWidget widget a (IRWidget a)
irWidget layf mkWid attr initial mySrc = mkWidget layf $ \ win ->
  do wid  <- mkWid win [ attr WX.:= initial ]
     src  <- commandSource wid attr
     return (wid, (src <* attachSink attr wid mySrc, mempty))

-- Copied from Graphics.UI.Phooey.Monad
commandSource :: WX.Commanding widget =>
                 widget         -- ^ widget
              -> WX.Attr widget a  -- ^ attribute
              -> IO (Source a)
commandSource wid attr = fmap (dd (WX.get wid attr)) (commandNews wid)

-- Attaches a Source to an WX.Attr (attribute)
attachSink ::
     WX.Attr w a
  -> w
  -> Control.Compose.O ((,) (News IO)) IO a
  -> DataDriven IO ()
attachSink attr wid src = joinDD (sink attr wid <$> src)

-- Makes a sink from an attribute
sink :: WX.Attr w a -> w -> a -> IO ()
sink attr wid x = WX.set wid [ attr WX.:= x ]

-- Better if it did not use read, but in stead read directly from the attaches source, as it would
-- 1) not require that (Read a)
-- 2) no risk that the value was not-readable (which make a runtime error). It could be not
--    readable if the user wrote something in the textEntry or if the initial value was bad.
--
-- We should properly also get rid of the initial value. It should just get it from the source.
showDisplayIR :: (Read a, Show a) => a -> Source a -> UI (Source a)
showDisplayIR = irWidget WX.hfill WX.textEntry (WX.mapAttr read (\_ b -> show b) WX.text)

What do you think of this approach? Is this compatible with the idea behind Phooey?

Also, how would you do self-updating widgets? For example a list that sorted itself whenever the GUI-user added a new element to the list. One should be careful not to end up with eternal recursion.

Also mutually recursive widgets could be interesting. I realise that you do some recursion in your examples, but that is not to the value of the widgets, just to there "limits" (e.g. how low/high can the slider go).

Mads 15:16, 19 August 2007 (GMT)


Hi Mads. I'm delighted to hear about your experiments with Phooey. You definitely ran into an awkward spot, and I like your solution. Below is another solution and then an explanation and a change I'm already making, about which I'd like your feedback.

You're right that the displays have to be part of the return value to work. Here's a fairly direct way to accomplish that.

 basket :: UI (Source ())
 basket
     = title "Shopping List" $
       do a <- apples
          b <- bananas
          let fruitsTotal = liftA2 (+) a b
          phantom1 <- title "Fruit" $ showDisplay fruitsTotal
          c <- shovels
          d <- spades
          let toolsTotal = liftA2 (+) c d
          phantom2 <- title "Tools" $ showDisplay toolsTotal
          -- the grand total
          phantom3 <- title "Basket" $ showDisplay $ liftA2 (+) fruitsTotal toolsTotal
          -- and joing all of the displays together.
          return $ phantom1 `mappend` phantom2 `mappend` phantom3
The problem is that a
Source ()
is an important thing, so it must not be discarded. Since Phooey is a functional UI library, the displaying does not happen as a side-effect. Instead, the UI must yield the effect to be done, which is represent as
Source ()
. By the way, I wish
(>>)
had a more restricted type, namely
Monad m => m () -> m a -> m a
, so that it would be an error if one failed to make bindings like
phantom1
above. Then at least the programmer would know s/he had to do something with the
Source ()
. Now for the API change. I want to make it more explicit yet that displaying returns something important and what is going on. Specifically, I want to use
Source (IO ())
instead of
Source ()
. I hope it's then clearer that a UI yields a time-varying effect, which is executed whenever it updates, rather than somehow happening.

Reactions?

I'm intrigued with your question on self-updating widgets, and I'm not sure I understand it. It's easy to put up a UI with input list and sorted output list, having the sort function re-run whenever the input list is edited. I'm guessing you mean something more than that, right?

Finally, I'm very curious about your comment for recursive widgets, beyond my contrived slider-bound examples. Do you have a concrete example in mind?

Cheers, Conal 04:27, 20 August 2007 (UTC)