Skip to content

Instantly share code, notes, and snippets.

@chreekat
Last active December 25, 2015 05:29
Show Gist options
  • Select an option

  • Save chreekat/6924735 to your computer and use it in GitHub Desktop.

Select an option

Save chreekat/6924735 to your computer and use it in GitHub Desktop.
Fay FFI Hello World / Tutorial
{-# LANGUAGE EmptyDataDecls #-}
-- This is a literate file, for some value of 'literate'.
--
-- Read the comments. Read the code. Enjoy. :)
import Prelude
import FFI
-- | Meme-appropriate starting point. We use javascript to make fay which
-- makes javascript!
hw1 :: Fay ()
hw1 = ffi "window.addEventListener('load', function () { alert('Hello, world!'); })"
-- Let's try harder. Let's abstract out some types and APIs. We have
-- objects and events:
data Object
data Event = Load | Etc
-- We have an addEventListener.
addEventListener
:: Event -- ^ Being listened for
-> Object -- ^ The "listener". Often a function.
-> Object -- ^ Whom to register the event with.
-> Fay () -- ^ No return value.
addEventListener event l r = _addEventListener e l r
where
-- (Can we do better than this?)
e = case event of
Load -> "load"
Etc -> "banana banana"
_addEventListener :: String -> Object -> Object -> Fay ()
_addEventListener = ffi "%3.addEventListener(%1, %2)"
-- Here's an alternate that relies on "features" of Javascript that I am
-- not actually familiar with. I was just clicking around the Chrome Dev
-- Toolbar. This gets rid of the Object-to-String conversion mucking up the
-- previous implementation.
--
-- addEventListener = ffi "%3.addEventListener(%1.constructor.name, %2)"
-- Anyway. Now hows'about alert. My brief reading leads me to understand
-- that one should qualify names when possible.
alert :: String -> Fay Object
alert = ffi "window.alert(%1)"
-- Need to get the window. Don't try to use ffi functions as text
-- templates; you must stick an object in where an object is required. One
-- such place is '%3' in _addEventListener. It would not be sufficient to
-- have the third argument to _addEventListener be a String.
getWindow :: Fay Object
getWindow = ffi "window"
-- So!
hw2 = do
win <- getWindow
fn <- alert "Hello world!"
addEventListener Load fn win
main = hw2
-- Compile this file with "fay --html-wrapper hello_fay.hs", then open
-- hello_fay.html in a browser.
@chreekat
Copy link
Author

My fear of going down that path is I'd end up shoehorning JS' complete prototype heirarchy into Fay, which sounds like a bad idea. Is "alert(%1)" a Function, an Object, an EventListener, ...? Will I have to make a new instance declaration every time I need to use a new interface?

Not that I disagree, of course. There's no sense using Fay if we just retreat to the un(i)typed style. This seems like an area for thought and care. I merely punted on all that to finish the example. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment