Prelude. I'm new to Haskell and functional programming at all, but I can't help coding in Haskell, because it' so exiciting! Thus, my question could be very simple and/or trivial. I'm still reading "Learn You a Haskell for Great Good", and Monads are still in the way.
Consider following behaviour:
mousemove
events, when event fired, push them into stackrequestAnimationFrame
to start loopWhat I can't realize is how to get interested value from event, e.g. pageX or clientX, it's looks like I have to do something with data definitions, and I'm stuck with it. Please help.
P.S. I also can't understand how to check result of foreign function, e.g. when I try to get DOM element the result may be element or null
, thus, say, getElById
is impure, and I need to check what I've got in result. How to realize it? Seems I can use something like: data Element = Null | Element ...
and then pattern match the result, but how to make foreign function to be aware of expected result?
UPDATE
Here working example of desired logics:
module MouseTracker where
import FFI
import Prelude
data Element
data Event
type EventStack = [Event]
data Frame
consLog :: a -> Fay ()
consLog = ffi "console.log(%1)"
reqFrame :: (Frame -> Fay ()) -> Fay ()
reqFrame = ffi "requestAnimationFrame(%1)"
getDoc :: Fay Element
getDoc = ffi "document"
createEl :: String -> Fay Element
createEl = ffi "document.createElement(%1)"
elById :: String -> Fay Element
elById = ffi "document.getElementById(%1)"
elSetId :: Element -> String -> Fay ()
elSetId = ffi "%1.setAttribute('id',%2)"
elSetHtml :: Element -> String -> Fay ()
elSetHtml = ffi "%1.innerHTML = %2"
docBodyAppend :: Element -> Fay ()
docBodyAppend = ffi "document.body.appendChild(%1)"
elListenWith :: Element -> String -> (Event -> Fay ()) -> Fay ()
elListenWith = ffi "%1.addEventListener(%2, %3)"
winListenWith :: String -> (Event -> Fay ()) -> Fay ()
winListenWith = ffi "window.addEventListener(%1,%2)"
docListener :: String -> (Event -> Fay ()) -> Fay ()
docListener evtType fn = do
doc <- getDoc
elListenWith doc evtType fn
createTrace :: Fay ()
createTrace = do
div <- createEl "div"
docBodyAppend div
elSetId div "trace"
createStack :: Fay ()
createStack = ffi "window._eventStack = []"
getStack :: Fay EventStack
getStack = ffi "window._eventStack"
updateStack :: EventStack -> Fay ()
updateStack = ffi "window._eventStack = %1"
emptyStack :: Fay ()
emptyStack = createStack
listenMice :: Fay()
listenMice = docListener "mousemove" trackMouse
where trackMouse :: Event -> Fay ()
trackMouse evt = do
stk <- getStack
updateStack (evt:stk)
renderFrame :: Frame -> Fay ()
renderFrame frame = do
stack <- getStack
if length stack > 0
then (do
let e = head stack
traceEventCoord e)
else (do return ())
reqFrame renderFrame
emptyStack
where traceEventCoord :: Event -> Fay ()
traceEventCoord e = do
eX <- pageX e
eY <- pageY e
let str = show eX ++ " x " ++ show eY
trace <- elById "trace"
elSetHtml trace str
pageX :: Event -> Fay Int
pageX = ffi "%1['pageX']"
pageY :: Event -> Fay Int
pageY = ffi "%1['pageY']"
main = do
docListener "DOMContentLoaded" $ \ _ -> do
putStrLn "DOM ready"
createTrace
createStack
listenMice
reqFrame renderFrame
See this answer for the different ways to declare data types and their tradeoffs. Are you creating the events yourself or are you wrapping DOM events? If you are wrapping I'd suggest to use an EmptyDataDecl. That would mean that you don't have constructors for each kind of event but you can add functions that check that type for you. If you create your own events it makes more sense to use a normal data declaration with constructors.
When wrapping DOM events you'd then end up with:
data Event
pageX :: Event -> Fay Int
pageX = ffi "%1['pageX']"
Fay has the Nullable type to deal with nullable values:
getElementById :: String -> Nullable Element
getElementById = ffi "document['getElementById'](%1)"
This is described in more detail on the wiki