Above, Below, and Beyond Tech Talk

by Rahel Lüthy

November 11, 2015

Play Audio via Elm

In preparation for a MaKey-MaKey-workshop, I needed a simple HTML page which plays a sound whenever any key is pressed. I am using all sorts of excuses to write Elm lately, so here’s how I ended up triggering the audio clip (full sources on GitHub, deployed version here).

First, we need a signal that indicates whether any key is currently down:

isAnyKeyDown : Signal Bool
isAnyKeyDown =
  Signal.map (\keys -> not (Set.isEmpty keys)) Keyboard.keysDown

For debugging, let’s visualize our events by displaying a small note image:

view : Bool -> Html
view isAnyKeyDown =
  if | isAnyKeyDown -> img [ src "note.png" ] []
     | otherwise -> div [] []

main : Signal Html
main =
  Signal.map view isAnyKeyDown

Next, we need to actually play the audio. Elm itself does not support this out-of-the-box, but triggering it via HTML5 and JavaScript is quite straight forward:

    <audio id="audio" src="piano.ogg" preload="auto"></audio>
var audio = document.getElementById('audio')
function playAudio() {
    // reset if already running
    audio.currentTime = 0;

    // go!

Finally, we need to wire our JS function to our Elm signal. This is where Elm ports come in:

port playAudio : Signal ()
port playAudio =
  Signal.map (\_ -> ()) (Signal.filter identity False isAnyKeyDown)

There are two things to note here: First, we’re filtering our isAnyKeyDown to no longer contain key release events. Second, we’re mapping our signal of boolean values to a signal of unit types () to match the signature of our parameter-less JS function.

All there is left, is to actually trigger the JS callback whenever Elm sends an event:

    var app = Elm.fullscreen(Elm.Main, {});

July 25, 2015

Mac OSX Tips & Tricks #8: Finding Files via Terminal

This is how you recursively search all notes.txt files in your home directory (~) for the text snippet foo (from an OSX terminal):

mdfind -name notes.txt -onlyin ~ -0 | xargs -0 grep -i "foo"

It has at least 3 advantages over the traditional find/grep combo:

Happy searching!

May 29, 2015

Partial Function Application in Haskell

Learning myself a Haskell should be for great good, so grasping the basics is essential. I am forcing myself to go slowly, taking baby steps in the REPL.

Today, I came across this really simple example:

map (>10) [1,42,99]

Figuring out that this will give [False,True,True] is not exactly rocket science, but what exactly is this (>10) term? Given that we’re talking about Haskell, it must be a function, right? Also, inferring from the types involved, it must be a function that accepts a Num and returns a Bool. But how does this come about?

Back to more basics – here’s another simple function:

greaterThan :: Int -> Int -> Bool
greaterThan x y = x > y

greaterThan 10 42 -- False

Confusingly, the type signature does not distinguish between parameters and the return type. This has been my most important Haskell epiphany yet: Every function in Haskell actually only takes one parameter

Calling greaterThan 10 42 just looks as if we were passing two parameters, but we’re actually calling the function with one parameter, getting a function from Int -> Bool in return, and then immediately calling this function with the second parameter to get the result. Let’s make this explicit by breaking the call down to the individual steps:

tenGreaterThan = greaterThan 10 -- returns a function with Int -> Bool signature
tenGreaterThan 42 -- returns the final result (still False)

Now back to the (>10) term: Pretty much everything in Haskell is a function, so > is a function, too. It is usually called with infix notation, e.g. 10 > 42, but it can actually be converted to a postfix function by putting it in parenthesis:

(>) 10 42 -- still False

Obviously, we can also partially apply here:

tenGreaterThan = (>) 10
tenGreaterThan 42 -- also still False

Back to our initial example – here it is again:

map (>10) [1,42,99] -- [False,True,True]

Applying (>10) to 42 correctly gives True, but applying (>)10 to 42 did give False in the example before, right? Things that make you go hmmmm…

Turns out the parenthesis are really crucial here! In Haskell there is a syntax called sectioning, which is special for partial application on infix operators. The term (>10) corresponds to a right section, which is equivalent to flip (>) 10. No wonder the semantics seemed flipped!

Long story short: While it is quite intuitive that the (>10) corresponds to a \x -> x > 10 in the initial map example, the magic happens through Haskell’s special sectioning syntax.

Older Posts » Archive