Updating Elm’s Flickr Example

Nov 03 2014

Recently Elm received a new library called elm-html, which allows for external HTML/CSS to be used with Elm code. I very much prefer this style of writing programs as it allows existing HTML/CSS designs and talent to be used, lowers the barrier for entry into Elm programming. On top of that, elm-html supports updating the DOM via reference-equals, a strategy which allows for highly performant UI, at least an order of magnitude faster than conventional MVC frameworks. Using elm-html comes with caveats, notably the inability to use the Graphics.Input interfaces. In this post I’ll update Elm’s Flickr image search example to use elm-html.

Since we’re only using Elm to build the JavaScript now, the first thing we need to do is build an HTML container for our program. This container links out to a CSS file (which we’ll create later), the JS file (which comes from the Elm compiler), and links the Elm program to the document using the Elm.fullscreen method.

<!DOCTYPE html>
        <title>Flickr Image Search</title>
        <link rel="stylesheet" type="text/css" href="flickr.css" />
        <script type="text/javascript" src="flickr.js"></script>
        <script type="text/javascript">

Next, let’s rebuild the markup in elm-html. When we import the elm-html library, we need to be careful to disambiguate Graphics.Input.input from Html.Tags.input. I prefer to import Html.Tags fully, and qualify Graphics.Input, but ultimately the choice is up to you.

-- Add this at the beginning, so the Elm.fullscreen can find what to embed
module Flickr where
-- import Graphics.Input (Input, input)
-- import Graphics.Input.Field as Field
import Graphics.Input as Input
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import Html.Tags (..)
import Html.Optimize.RefEq as Ref

Get rid of the old scene and main functions. Instead we’ll build our HTML markup as modular views (cf. Architecture in Elm) which is also the technique used in the Elm TodoMVC example.

tag : Input.Input String
tag = Input.input ""
searchView : Html
searchView =
        [ class "searchbar" ]
        [ input [ type' "text", placeholder "Flickr Instant Search", on "input" getValue tag.handle identity ] [] ]
resultView : Maybe String -> Html
resultView imgSrc =
    case imgSrc of
        Just source -> div [ class "result" ] [ img [ src source ] [] ]
        Nothing -> div [ class "result" ] []
view : Maybe String -> Html
view imgSrc =
        [ class "container" ]
        [ searchView
        , resultView imgSrc
scene : (Int, Int) -> Maybe String -> Element
scene (w, h) imgSrc = toElement w h (view imgSrc)
main : Signal Element
main = scene <~ Window.dimensions
              ~ getSources (dropRepeats tag.signal)

In elm-html, HTML nodes are of type Html, and can be built using the node function, whose signature is

node : String -> [Attribute] -> [Html] -> Html

For example:

node "ul" [ class "container" ] [ node "li" [] [ text "Item 1" ], node "li" [] [ text "Item 2" ] ]
-- Generates:
-- <ul class="container">
--   <li>Item 1</li>
--   <li>Item 2</li>
-- </ul>

Furthermore, Html.Tags contains aliases such as div = node "div" to make the Elm markup more seamless. In the code given, I’ve divided the views into the upper search bar portion, and the lower results portion, both of which are container in div.container. The scene function then just converts the Html into an Element ready to embed.

Note also the change in the tag signal. As we’re no longer using Graphics.Input.Field, I’ve converted the signal into a plain String signal. Linking this signal to our searchbar requires that we use the on function, which you can read more about in the documentation. A few more minor changes to accommodate our signal change, and the conversion is complete. Compile using the following:

elm --make --only-js --bundle-runtime --build-dir=. flickr.elm

Open up flickr.html in your browser and you should see the exact same functionality as the original demo, only now built with elm-html! As a bonus, you can also now style the HTML markup using regular CSS in flickr.css.

Full code download here.

No responses yet

Engineering salary by discipline

Nov 09 2011

I created this chart to summarize the information I found on engineersalary.com.

The green bars on the left show relative number of jobs for each discipline. The bars in the chart show the salary ranges of the bottom 10%, middle 50%, and top 10%, respectively. The bars are cut off at $40k on the left and $150k on the right, so the bottom 10% and top 10% bars may extend beyond those bounds.

No data was given for average starting salary by degree level, for Aerospace Engineering.

No responses yet

Faux function type signatures in Python

Aug 10 2011

A recent post on stackoverflow.com piqued my interest — how can we implement a sort of type signature for functions in Python? I.e., a function like

@signature(int, int)
def foo(x, y):
    print x+y

A call to foo(3,4) should result in 7 and not '34'. This arguably goes against Python’s duck typing principles, but let us humor the idea for just a moment. The following Python features will be helpful:

  • Reified types: types are first class citizens in Python…try print int to see for yourself.
  • inspect: we need some code reflection to get the function parameter names, in order of application.

Here’s what I came up with (and you can view my original submission on the StackOverflow):

import inspect
def signature(*sig):
    def __decorator__(fn):
        def __wrapped__(*args, **kwargs):
            arglist = inspect.getargspec(fn).args
            mod_args = dict([(vid, ctype(rval)) for vid, ctype, rval in zip(arglist, sig, args)])
                for k, v in kwargs.items():
                    mod_args[k] = sig[arglist.index(k)](v)
            except ValueError, e:
                raise TypeError("%s() got an unexpected keyword argument." % fn.__name__)
        return __wrapped__
    return __decorator__

The breakdown is as follows. Inside the function decorator, we use inspection to get arglist — a list of the arguments to fn our function, in the order they should be applied. Then we zip arglist, sig (containing the types following the order of the arguments), and args (the actual values of the arguments passed to fn) together in our list comprehension; this allows us to apply a type cast to each of the args:

[(vid, ctype(rval)) for vid, ctype, rval in zip(arglist, sig, args)]

As you can see, I also tacked on the argument name (vid here is supposed to stand for “variable identifier”), which will come in handy soon.

The next step is to merge and convert the passed keyword arguments to the arguments we’ve already constructed. If the user passes a keyword argument that isn’t supposed to be passed, we will raise a more appropriate TypeError as opposed to having a more obtuse ValueError come up.

And that’s about it! Let’s try it out:

@signature(int, int, str)
def foo(x, y, z):
    print x+y
    print type(z), z
foo(3, '4', z=["testing", "123"])

And indeed the output is:

 ['testing', '123']

2 responses so far