I am an elm beginner and I want to provide an elm practical course to my coworkers.
I will do a short presentation of elm, then we will move to practical lessons.
The first lesson is to read together the following file to discover elm basis and syntax:
Basis.elm
{-
Here we will discover some elm basis and syntax.
You can hack this file and run the tests by running in your terminal:
elm reactor
And go to http://localhost:8000/
Then select Basis.elm file
______________________________________________________________________
First of all, we are going to import some testing packages.
We import the Expect package so we can use Expect package functions.
For example we will be able to call Expect.equal to check equality.
-}
import Expect
{-
Then we import Test.Runner.Html and expose TestProgram to our program.
Since we exposed TestProgram we can call this function without prefixing it with the package name.
-}
import Test.Runner.Html exposing (TestProgram)
-- Finally we import Test and expose every functions of the package
import Test exposing (..)
{-
Let's talk about functions as it's a key component of elm.
This is a function:
-}
add x y = x + y
{- ^ ^ ^^^^^
| | |
| | |--- there is no return keyword, everything is a returning expression
| |
|-|--------- first and second arguments
To call a function, you write:
myResult = myFunction myFirstArgument mySecondArgument
-}
result = add 4 5
{-
To write an anonymous function we use the following syntax
(\myFirstArgument mySecondArgument -> myFirstArgument * mySecondArgument)
so we can probably write something like that:
-}
resultComputedByAnonymousFunction = (\a b -> a + b) 4 5
{-
The signature of our add function is:
number -> number -> number
If translated in Javascript, our function looks like:
function add (x) {
return function(y) {
return x + y;
}
}
It's a function taking one number (x) returning
a function expecting another number (y) returning
a number which is the result of the operation x + y.
Functions are curried,
so we can create a new function from our add function:
-}
add3 = add 3
seven = add3 4
{-
We are going to unit test our previously written adding function.
The signature of the test function is:
String -> (() -> Expectation) -> Test
It takes a String and a function returning an Expectation and finally returns a Test.
Let's test the add function.
-}
testAddFunction = test "add function should addition the arguments" (\_ -> Expect.equal 4.12 (add 2 2.12))
-- Now let's write a dividing function
divide x y =
x / y
{-
We want a function that divide by 2, so we need to flip the arguments of the function.
So we can use a core function for that purpose:
http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Basics#flip
-}
divideBy2 = flip divide 2
-- let's compute ( 4 + 6 ) / 2
mean = divideBy2 (add 4 6)
testFunctionComposition = test "composition of divideBy2 and add functions should return the mean value" (\_ -> Expect.equal 5 mean)
{-
Finally let's find a more natural way to express what we are doing here.
We can use backward/forward function application:
http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Basics#|>
-}
meanByPipe = add 4 6 |> divideBy2
testPipe = test "composition and pipe should return the same value" <|
\_ -> Expect.equal mean meanByPipe
{-
Strings are always double-quoted.
Single quote is used for Char.
The operator to append a String is ++.
-}
-- So this is a Char
myChar = 'l'
-- and those are Strings
myString = "abc"
mySecondString = "def"
testAppendOperator = test "append operator should append strings" <|
\_ -> Expect.equal "abcdef" (myString ++ mySecondString)
{-
A record is a kind of logic-less object.
This is a record:
-}
johnDoe =
{ firstName = "John"
, lastName = "Doe"
}
-- We can access a record field with the dot notation
testRecordAccessor = test "accessor .firstName should return the firstName of a record" <|
\_ -> Expect.equal "John" johnDoe.firstName
-- To get an updated copy of a record we use the pipe operator
lioDoe = { johnDoe | firstName = "Lio" }
testUpdateRecord = test "Pipe operator should 'update' the firstName of the record" <|
\_ -> Expect.equal "Lio" lioDoe.firstName
-- With the pipe operator we can change multiple fields at once
lioDa =
{ johnDoe |
firstName = "Lio",
lastName = "Da"
}
testUpdateMultipleFieldsRecord =
test "Pipe operator should 'update' multi fields" <|
\_ -> Expect.equal {firstName = "Lio", lastName = "Da"} lioDa
{-
A record is a type, so johnDoe, lioDoe and lioDa are typed { firstName: String, lastName: String }.
We can clarify typing with type annotation.
-}
janeDoe : { firstName: String, lastName: String }
janeDoe = { firstName = "Jane", lastName = "Doe" }
{-
And to simplify type usage we can alias a type.
Let's define an alias to our type { firstName: String, lastName: String }.
-}
type alias Identity =
{ firstName : String
, lastName : String
}
saraDoe : Identity
saraDoe = { firstName = "Sara", lastName = "Doe" }
-- Identity is also a record constructor
claireDoe : Identity
claireDoe = Identity "Claire" "Doe"
testRecordConstructor = test "Record constructor should return an record" <|
\_ -> Expect.equal "Claire" claireDoe.firstName
{-
Since we discovered some basic types, let's move to union type.
Union type is a flexible feature used for many things in elm.
For example, Bool is the union type of True and False.
Bool = True | False
^ ^ ^
| | |
| |-------|--- constructors
|
|------------------ the union type
With union types we can naturally express things such as an answer to a survey:
-}
type Answer = Yes | No | Other String
yes : Answer
yes = Yes
no : Answer
no = No
iDoNotUnderstand : Answer
iDoNotUnderstand = Other "i don't understand the question"
{-
We can do pattern matching with the case of instruction.
-}
testPatternMatching =
test "yes should be Yes" (\_ ->
case yes of
Yes -> Expect.pass
No -> Expect.fail "yes is not a No"
Other response -> Expect.fail "yes is not an Other"
)
main : Test.Runner.Html.TestProgram
main =
[ testAddFunction
, testAppendOperator
, testPipe
, testRecordAccessor
, testUpdateRecord
, testUpdateMultipleFieldsRecord
, testRecordConstructor
, testPatternMatching
]
|> concat
|> Test.Runner.Html.run
elm-package.json
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-community/elm-test": "4.1.1 <= v < 5.0.0",
"elm-community/html-test-runner": "1.0.3 <= v < 2.0.0",
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}
Then we will write some unit tests to discover List and Tuple, write a simple application to discover Elm Architecture and finally build a more complex application with Commands.
Could you review the Basis.elm
file to improve this lesson?
lang-elm
yet. Feel free to change the highlighting to something else if you think it's more appropriate. \$\endgroup\$lang-hs
, which should probably help with all future Elm questions. \$\endgroup\$