home.


Tagged: elm


Elm-lang: Auto refresh Elm sources when you embed in an index.html file

elm-react works wonderfully when your entire project is in elm. However, you often embed Elm into a larger application.

Your index.html will look something like this:

<div id="main"></div>
<script src="main.js"></script>
<script>
    var node = document.getElementById('main');
    var app = Elm.Main.embed(node);
</script>

And you use elm-make --output main.js to generate the javascript.

But you need to return the elm-make command on every modification of your elm sources. And that’s shi… less that optimal for productivity.

But Linux/Unix/whatever can help you there with its inotifywait command. With it, you can run a command on file modifications.

Go to the directory where your elm sources are and run this:

inotifywait -e modify -m . | while read file; do elm-make Main.elm --output SOME_DIRECTORY_SOMEWHERE/main.js; done

You’re telling it to wait for modify events in the current directory. And when it gets them it’ll run the elm-make command.

Voila. Edit your elm code and refresh your browser. All is well in the world. Or at least your edit -> compile -> use cycle is shorter. Whatever.

elm inotifywait

Elm-lang: Create a new component

Let’s create a component named Component.elm. It is very similar to your main program. It can have a Msg union type, update, view and init functions. It can have its own commands and subscriptions.

Here’s a very simple version without its own model for demonstrative purposes.

module Component exposing (..)

import ...

type Msg  = YesPressed | NoPressed

update : Msg -> Cmd Msg
update msg = 
  case msg of 
    YesPressed -> Cmd.none
    NoPressed -> Cmd.none

view : Html Msg
view = 
  div [] [
    button [ onClick YesPressed ] [ text "yes" ]
    , button [ onClick NoPressed ] [ text "no" ]
  ]

It displays two buttons, which send local messages, which in turn do nothing.

Let’s hook it up to our main program. In our main program’s Msg union type add in our component’s union type:

import Component exposing (..)

type Msg =
  ComponentMsg Component.Msg
  ..

Our main app’s view will create messages of the type ComponentMsg by calling our component’s view function:

view : Model -> Html Msg
view model =
  div []
      [
      Html.App.map ComponentMsg (Component.view)
      ...
      ]

We’re using Html.App.map to map our component’s view type, Component.Msg, to our main app’s type, Msg.

Now when we press the yes or no button, it will send out a ComponentMsg. Let’s deal with that now.

This means in our main program’s update function:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  case msg of
    ComponentMsg m -> case m of 
                        YesPressed -> ( model, ... )
                        NoPressed -> ( model, ... )
    ...

When we receive a ComponentMsg via the view above, we case over the Component.Msg values and act accordingly.

We haven’t called the component’s update function. This is mainly because it does nothing: it neither returns a model or an interesting command.

If it did, and it had its own model and the update method took that in, we could capture those using

  case msg of
    ComponentMsg m -> let ( componentModel, componentCmd ) = Component.update model.component
                      in case m of 
                        ...

The model.component would be available via type alias Model = { component: Component.Model, ... } which our component’s update function would update and return.

The componentCmd would be of the type Cmd Component.Msg. To map it to our application’s Cmd Msg, use Cmd.map ComponentMsg componentCmd.

You could then return that and its Cmd would be executed and returned back to the main app’s update as the ComponentMsg type.

I’m using 0.17.1.

elm

Elm-lang: Basics of Json parsing

First import Json.Decode exposing (..). I’m on 4.0.5 of Elm’s core library.

int is a function parses an Int value. There are similar functions for other json types, float for example. These are examples of Decoder a types.

You can have more advanced decoders that work upon the above simple decoders.

  • (:=) takes a string and a decoder type itself. It gets a field, named in the first paramtere, in a json object and applies the decoder on it, to extract that type.
  • list takes in a decoder itself and looks for a list of items confirming the the decoder type.

Decoders that take decoders allow for embedding: list ("thing" := int) says we should find a list of objects, and in those objects there should be a “thing” field which should be an integer.

More advanced decoders are ones that work with objects with multiple fields. object2 takes in two decoders. It also takes in a function that takes the two decoder values and returns something that combines them.

What’s an example of “a function that takes the two decoder values and returns something”? If you have type alias Hiya = {field1: String, field2: String} it works as Hiya "one" "two" when used as a function.

So object2 Hiya ("field1":=string) ("field2":= string) would decode two string fields and return Hiya.

So we’re both have simple and complex decoders now. What can we do with them?

decodeString will take a decoder such as the ones above and returns a Result String a. This returns either an error as a string or the value: a string value in the case of decodeString.

For example, given the type type alias Decoded = {id:Int, company:String} and the json:

{
    "companies": [
        {
            "id": 2,
            "name": "Thing"
        },
        {
            "id": 1,
            "name": "Another Thing"
        }
  ]
}

Then decodeString ("companies" := list (object2 Decoded ("id":=int) ("name":=string))) theJson would return you these Elm values wrapped in a Result: Ok ([{ id = 2, company = "Thing" },{ id = 1, company = "Another Thing" }])

But Http.get is probably the main use of this. It takes a Json.Decoder and decodes the Json values and returns Elm values according to the decoder passed.

elm

Elm-lang: Basics of native modules

A native module is one that communicates with javascript. And it communicates with events, success and failure events. I’m using 0.17.

In our normal Elm code, import our yet to be created native module, create our function that calls the javascript function and create three union types that will receive the success, receive the failure and request the function be called:

import Native.MyModule -- this can't be aliased, etc

addOne : Int -> Task Int Int
addOne = Native.MyModule.addOne 

type Msg = ...
  | GoodNative Int | BadNative Int | SendNative

Native modules are prefixed with Native and will live in our Native source directory, and our module will be called MyModule. The two union types with constructor values will receive the results (an Int - and we’ll also get one on failure).

The function itself is in ‘points free’ style. It takes no parameters, but we’ll still call it via addOne 807. It will return a Task which we will later perform which will turn it into a Cmd Msg to be returned in our update Elm function.

Now in our update function, we’ll call our addOne function when we receive the SendNative event (in a onClick call for example). The result of this is a Task, as we see above, which we will perform with our failure and success union type value constructors.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  case msg of
    SendNative     -> ( model, addOne 807 |> Task.perform BadNative GoodNative )
    GoodNative i   -> ( { model | num = i }, Cmd.none )
    BadNative i    -> ( model, Cmd.none )
    ...

The final two value constructors take the Int values we’ll receive and use it somehow, in the view via the model normally. That’s all for our Elm code.

Let’s create a Native directory in our source file and create a file called MyModule.js. And in our elm-package.json ensure you have "native-modules": true. And in our javascript file:

var _user$project$Native_MyModule = function() {

  ...

  return {
    addOne: addOne
  };

}();

The variable _user$project$ appears define the module for our local project. After that we have Native_MyModule, which relates to our import Native.MyModule import in our Elm code. We return an object that has a key naming our function, addOne and it has a value of a function that we’ll look at now:

function addOne(num) {
  return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
    callback(_elm_lang$core$Native_Scheduler.succeed(num+1));
  })
}

It takes in a single variable and returns _elm_lang$core$Native_Scheduler, which relates to a javascript file you can find in your packages directory. We call nativeBinding function and pass in a function that takes a callback as a parameter.

Within that function, we use the callback, again using the scheduler, but this time call succeed, with the parameter plus one. This will finally relate to the GoodNative Int type in your Elm code, with the attached integer variable.

In the same way, you could also use _elm_lang$core$Native_Scheduler.fail(someValue) which would then invoke BadNative Int

elm

Elm-lang: The very basic concepts of effect managers in 0.17

Imagine we want use a network time server to retreive the time, and return that to the update function in our Elm app. (For the purposes of this explanation, ignore the fact we can use a http call for this.)

First our application has the type AppType which is the type that Elm’s update takes. And TheTime Int is a value constructor of that type. The Int parameter should hold the network time when we fetch.

We want to call something like fetchNetworkTime TheTime, so the network time module has a function to convert an Int representing the network time to our app defined type.

In the network time module, fetchNetworkTime fn = NetworkTime fn would take in a function (i.e. our TheTime value constructor) and with such then construct an internal type, NetworkTime.

We now have a network time module that has a type, type NetworkTime msg = NetworkTime (\time -> msg), which has a value constructor that allows us to convert from a fetched networked time Int to a type used in our Elm application.

Now imagine after we call fetchNetworkTime, Elm intercepted the return value, NetworkTime, and sent it to another function in the module.

This other function would extract the function from NetworkTime, pass it an Int value fetched from a network time server, grab the resulting TheTime value and pass it to something that will route it through to Elm’s main update method.

This is essentially, and very, very basically, how effects modules in Elm work.

Except the NetworkTime msg type is converted by the compiler, via ...where { command = NetworkTime } ... in the effects module, to a Cmd msg type, so Elm’s update function can accepet Cmd AppType, thereby hiding all the various different types in the various different effects modules.

elm

Page 1 of 1