I am trying to make a counter widget in Reflex with the following qualities:
It has a minimum value of 0 -- and hitting "decrement" while at 0 results in nothing happening.
(solved) The increment button is to the right of the decrement button.
It has Bulma CSS styles applied.
This is the code I currently have:
bodyElementIncrRounds :: ObeliskWidget js t route m => m ()
bodyElementIncrRounds = do
-- el "h2" $ text "Using foldDyn with function application"
evIncr <- button "Increment"
evDecr <- button "Decrement"
-- evReset <- button "Reset"
dynNum <- foldDyn ($) (0 :: Int) $ leftmost [(+ 1) <$ evIncr, (+ (-1)) <$ evDecr]
el "h3" $ display dynNum
return ()
This is what the result is:
When I try to swap the buttons by flipping these to values: (+ 1) <$ evIncr, (+ (-1)) <$ evDecr, it has no effect at all on the location of the buttons. (I.e. increment remains on the left.)
When I try to apply Bulma code like this:
bodyElementIncrRounds :: ObeliskWidget js t route m => m ()
bodyElementIncrRounds = do
-- el "h2" $ text "Using foldDyn with function application"
evIncr <- elAttr "button" ("class" =: "button") $ button "Increment"
evDecr <- button "Decrement"
-- evReset <- button "Reset"
dynNum <- foldDyn ($) (0 :: Int) $ leftmost [(+ 1) <$ evIncr, (+ (-1)) <$ evDecr]
el "h3" $ display dynNum
return ()
It duplicates the button for some reason and also places the existing (ugly) button inside of the Bulma widget (edit: the duplication problem has been solved, but not the "button inside the button" problem):
I am trying to print the label text when I click on a listBoxRow.
This is the link to my gist: https://gist.github.com/bigos/6cf02ff27231cfc0352394da00023f67
the relevant code looks like this:
_ <- onListBoxRowSelected listbox2 (\(Just r) -> do
-- rn <- listBoxRowGetIndex r
cc <- containerGetChildren r
dd <- widgetGetName (head cc)
-- how do I print label text?
-- I get the error:
-- Required ancestor ‘GI.Gtk.Objects.Label.Label’ not found for type GI.Gtk.Objects.Widget.Widget’.
ee <- labelGetText (head cc)
putStrLn ("Clicked " ++ (show ee)))
The error
• Required ancestor ‘GI.Gtk.Objects.Label.Label’ not found for type
‘GI.Gtk.Objects.Widget.Widget’. • In a stmt of a 'do' block: ee <-
labelGetText (head cc) In the expression:
do { cc <- containerGetChildren r;
ee <- labelGetText (head cc);
putStrLn ("Clicked " ++ (show ee)) } In the second argument of ‘onListBoxRowSelected’, namely
relevant documentation
https://hackage.haskell.org/package/gi-gtk-3.0.18/docs/doc-index.html
Dan Robertson has provided the most helpful answer. After obtaining the label object you have to cast it to Label type. That was the missing link.
working code
_ <- onListBoxRowSelected listbox2 (\(Just r) -> do
cc <- containerGetChildren r
mlabel <- castTo Label (head cc)
case mlabel of
Nothing -> putStrLn "Not a label!"
Just label -> (labelGetText label) >>= putStrLn . unpack)
Thank you very much for your help.
related post
How do I cast Widget to Label in Haskell's GI-Gtk?
I want to do something like this:
getValue :: Element -> String
getValue x = do
v <- get UI.value x
v
However, an error is thrown; the expected type of get UI.value x is [String] but the actual type is UI String?
But if I change the type signature to getValue :: Element -> UI String, my last v gets the error of expected type UI String while its actual type is String.
I'm trying to implement something like this:
myfunction window = do
words <- getElementsByClassName window "word"
let strs = map getValue words
Since I can't say let strs = map (\x -> v <- get UI.value x) words.
When I only have one element to deal with, I'm fine:
filename <- chooser # get UI.value
liftIO $ print filename
unless (null filename) $ do
prevRows <- getElementsByClassName w "row"
mapM_ delete prevRows
elems <- liftIO $ readJSON filename
mapM_ (element table # addRow) elems
Since get UI.value x has type UI String as opposed to String, the correct definition of getValue needs to be in the UI monad as well:
-- Still not well-typed
getValue :: Element -> UI String
getValue x = do
v <- get UI.value x
v
However, then your next problem is that after you bind get UI.value x to v, v has type String, not UI String, so you need to return it, leading to the correct version
getValue :: Element -> UI String
getValue x = do
v <- get UI.value x
return v
which of course can be simplified as
getValue :: Element -> UI String
getValue x = get UI.value x
or η-reduced further to
getValue :: Element -> UI String
getValue = get UI.value
Since UI is a monad, you can use standard monad combinators like mapM to turn getValue :: Element -> UI String into mapM getValue :: [Element] -> UI [String]:
myfunction window = do
words <- getElementsByClassName window "word"
strs <- mapM (get UI.value) words
-- ... rest of `myfunction` can use `strs`
Note that myfunction of course is still in UI.
I have a little example using IORef in threepenny-gui (TPG):
testIORef :: IORef String -> Window -> UI ()
testIORef ref window = void $ do
return window # set title "Test IORef"
inCell <- UI.input
outCell <- UI.input
-- When value changes write to IORef
on UI.valueChange inCell $ \_ -> do
inValue <- get value inCell
liftIO $ writeIORef ref inValue
-- function that reads the IORef and sets the value of an element with it
let setValue oc = do
newVal <- liftIO $ readIORef ref
element oc # set value newVal
-- When enter is pressed update the value of the output
on UI.keydown inCell $ \c -> if (c==13) then setValue outCell else return outCell
getBody window #+ [ column [ grid [[string "In cell :", element inCell]
,[string "Out cell:", element outCell]
]
, string "Cells should update while typing."
]
]
I am trying to change this to use the Reactive stuff in TPG. I have got as far as making the Behaviors from the events valueChange and keyDown:
inValue <- stepper "0" $ UI.valueChange inCell
inEnter <- stepper "0" $ fmap show $ filterE (\kc-> kc==13) $ UI.keydown inCell
But I am stuck on how to use these Behaviors to save/get the value to/from the IORef. The problem is that the IORef calls will be in the UI monad, so if I use them the Behaviour will be Behavior (UI a), but then they won't work with sink. I know in this case I needn't use an IORef (eg. the currency conversion example) - but in my actual case I do.
EDIT:
I tried writing my own attribute:
valueUI :: ReadWriteAttr Element (UI String) String
so that I can set an attribute with a Behavior (UI String):
inEnter <- stepper "0" $ fmap show $ filterE (\kc-> kc==13) $ UI.keydown inCell
let getValue = fmap (const $ liftIO $ readIORef ref) inEnter
element outCell # sink valueUI getValue
The code compiles but doesn't work.
HLint suggests that I use forM_ rather than forM. Why? I see they have different type signatures but haven't found a good reason to use one over the other.
forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)
forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
The forM_ function is more efficient because it does not save the results of the operations. That is all. (This only makes sense when working with monads because a pure function of type a -> () is not particularly useful.)
Ok,
forM is mapM with its arguments flipped.
forM_ is mapM_ with its arguments flipped.
Let's see in mapM and mapM_ :
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM mf xs takes a monadic function mf (having type Monad m => (a -> m b)) and applies it to each element in list xs; the result is a list inside a monad.
The difference between mapM and mapM_ is, that mapM returns a list of the results, while mapM_ returns an empty result. The result of each action in mapM_ is not stored.
To understand the difference between (A): forM xs f and (B): forM_ xs f, it might help to compare the difference between the following:
-- Equivalent to (A)
do
r1 <- f x1
r2 <- f x2
...
rn <- f xn
return [r1, r2, ..., rn]
-- Equivalent to (B)
do
_ <- f x1
_ <- f x2
...
_ <- f xn
return ()
The crucial difference being that forM_ ignores the results r1, ... rn and just returns an empty result via return (). Think of the underscore as meaning "don't care" ... forM_ doesn't care about the results. forM however, does care about the results and returns them in as a list via return [r1, r2, ... rn].
Example 1
The code below asks for your name three times and prints the results of the forM.
import Control.Monad (forM, forM_)
main = do
let askName i = do
putStrLn $ "What's your name (" ++ (show i) ++ ")"
name <- getLine
return name
results <- forM [1,2,3] askName
putStrLn $ "Results = " ++ show results
An example execution with forM:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ["James", "Susan", "Alex"]
But if we change the forM to a forM_, then we would have instead:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ()
In your case, the linter is telling you that you're not using the return values of your forM (you don't have foo <- forM xs f, you probably have forM xs f by itself on a line) and so should use forM_ instead. This happens, for
example, when you are using a monadic action like putStrLn.
Example 2 The code below asks for your name and then says "Hello" – repeating three times.
import Control.Monad (forM, forM_)
main = do
let askThenGreet i = do
putStrLn $ "What's your name (" ++ (show i) ++ ")"
name <- getLine
putStrLn $ "Hello! " ++ name
forM [1,2,3] askThenGreet
An example execution with forM:
What's your name? (1)
> Sarah
Hello! Sarah
What's your name? (2)
> Dick
Hello! Dick
What's your name? (3)
> Peter
Hello! Peter
[(), (), ()]
The overall result of main comes from the result of the forM: [(), (), ()]. It's pretty useless and annoyingly, it appears in the console. But if we change the forM to a forM_, then we would have instead:
What's your name? (1)
> Sarah
Hello! Sarah
What's your name? (2)
> Dick
Hello! Dick
What's your name? (3)
> Peter
Hello! Peter
With that change, the overall result comes from the mapM_ and is now (). This doesn't show up in the console (a quirk of the IO monad)! Great!
Also, by using mapM_ here, it's clearer to other readers of your code – you're indirectly explaining / self-documenting that you don't care about the results [r1, ..., rn] = [(), (), ()] – and rightly so as they're useless here.