Elm - the why guide
Why should I waste spend my time learning yet another javascript framework/language/thing?
First I want to set the tone of this post. We are taking an objective look at the javascript landscape and taking a bet on a tool.
Lets define this stuff:
The program does what the stakeholders want it to do
More than one person, different opinions and experience levels that need to work together effectively
To answer that question let's first check out the landscape.
mutates state
you can write code that does not mutate state
you must write code that does not mutate state
does not require you explicity define types
you must explicity define types
your team doesn't have to waste time on style
Elm and Purescript (and others) fall into the same category, but I will be choosing Elm purely on the basis that it is simpler for now.
---- comparison
If you made it this far, I'll go ahead and show you the table of contents.
why guide (you already did this, good job!) installation syntax the elm architecture tips tricks make a thing make a thing using laravel
First, we need to install the Elm tools, more details can be found on https://guide.elm-lang.org/get_started.html but this is what you will need for this article -- The “Elm Platform” which includes all the command line tools you will need to work with Elm.
undefined is not a function
)You cannot get a runtime error, because you cannot write a program that allows a state that would create one. Its as simple as that.
Here is an example of what I mean:
import Html exposing (text)
main =
text 123
This simple program demonstrates something that the compiler just won't allow:
Detected errors in 1 module.
-- TYPE MISMATCH ---------------------------------------------------------------
The argument to function `text` is causing a mismatch.
5| text 123
^^^
Function `text` is expecting the argument to be:
String
But it is:
number
Lets sort this out (pretty easy as the compiler gave us the answer!)
import Html exposing (text)
main =
text "123"
or
import Html exposing (text)
main =
text (toString 123)
As you can see, Elm forces you to be explicit, and while it may seem trivial here, it is such a great feeling for a larger app to know that if it compiles, then it will do what you explicitly instructed it to do!
currying
. I promise it isn't as scary as it sounds!Lets say we wanted a couple functions, but they were super similar...
import Html exposing (text)
main =
text (toString(add5(1)))
add a b =
a + b
add5 a =
5 + a
We can re-use functionality!
import Html exposing (text)
main =
text (toString(add5(1)))
add a b =
a + b
add5 =
add 5
If you take a look above, you can see that we have preloaded
or curried
the add
function with 5
to create add5
. Imagine if it were more complex!
Elm has an operator (borrowed from F#, inspired by Unix pipes) that you can use to make your code oh so smooth. Meet the |>
operator.
import Html exposing (text)
main =
1
|> add5
|> toString
|> text
add a b =
a + b
add5 =
add 5
Now you can read the program just like you would a sentence!
take 1, add 5 to it, then make it a string, then make it some html text
To install our NPM dependencies, run npm install --save-dev laravel-elixir-elm
.
Next, head into the gulpfile.js
at the root of your project and replace require('laravel-elixir-vue');
with require('laravel-elixir-elm');
, and add elm to your build process.
gulpfile.js
const elixir = require('laravel-elixir');
require('laravel-elixir-elm');
elixir(mix => {
mix.sass('app.scss')
.elm('example');
});
This package will handle using elm-make to transpile your Elm code into a single example.js
file that can be served by your application (this does not need to be manually linked to, laravel-elm will embed the script in the page).
Next lets grab a composer package to help integrate it into our laravel app.
Run this from your project root
composer install tightenco/laravel-elm
And add the service provider to your application.
config/app.php
...
'providers' => [
'...',
Tightenco\Elm\ElmServiceProvider::class
];
...
Let's create a simple Elm program! This will create the directory and file: resources/assets/elm/example/Main.elm
:
Run this from your project root
php artisan elm:create example
Time to use our basic program in laravel!
First update your HomeController@index
method to read:
app/Http/Controllers/HomeController.php
use Elm;
...
return view('home', [
'example' => Elm::make('example')
]);
...
Finally, we'll want to update our example program to make an API call to fetch this data. resources/assets/elm/example/Main.elm
module Main exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.App exposing (programWithFlags)
import List
import Platform.Cmd
import Http
import Task
import Json.Decode exposing ((:=))
type alias User =
{ id : Int
, name : String
, email : String
}
type alias Model =
{ name : String
, users : List User
}
type Msg
= FetchFail Http.Error
| FetchSucceed (List User)
main : Program { name : String }
main =
programWithFlags { init = init, view = view, update = update, subscriptions = \_ -> Sub.none }
init : { name : String } -> ( Model, Cmd Msg )
init flags =
( { name = flags.name, users = [] }, fetchUsers )
renderUser : User -> Html a
renderUser user =
tr []
[ td [] [ text <| toString user.id ]
, td [] [ text user.name ]
, td [] [ text user.email ]
]
view : Model -> Html.Html a
view model =
div [] []
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
FetchSucceed users ->
( { model | users = users }, Cmd.none )
FetchFail error ->
( model, Cmd.none )
user : Json.Decode.Decoder User
user =
Json.Decode.object3 User
("id" := Json.Decode.int)
("name" := Json.Decode.string)
("email" := Json.Decode.string)
fetchUsers : Cmd Msg
fetchUsers =
Task.perform FetchFail FetchSucceed (Http.get (Json.Decode.list user) "/api/users")