One of the guarantees when we write programs in static-typing
environments is that we will be able to avoid seeing the program run into errors. In addition to the static type reason, another part is because we are allowed to define program exceptions just like any other meaningful data type. And from there, we can create logic to give the necessary responses to exceptions that arise during operation.
Suppose when interacting with user input and collecting age information, we will be able to define the data type like this:
1 2 3 4 5 6 | <span class="token keyword">type</span> <span class="token constant">MaybeAge</span> <span class="token operator">=</span> <span class="token constant">Age</span> <span class="token constant">Int</span> <span class="token operator">|</span> <span class="token constant">InvalidInput</span> <span class="token hvariable">toAge</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token operator">-></span> <span class="token constant">MaybeAge</span> <span class="token hvariable">toAge</span> <span class="token hvariable">userInput</span> <span class="token operator">=</span> <span class="token operator">...</span> |
And in all cases, the input from the user can be passed to a valid toAge
program. Appropriate input values will result in Age 21
or Age 1001
, while values that do not match will result in InvalidInput
. We can then use Pattern Matching
to ensure that both Age Int
and Invalid Input
cases are met properly and so the program won’t stop unexpectedly.
This type of processing will appear frequently. For example, when writing a single-page SPA
application to interface a blog site and encapsulate the Post
article content entered by the user to send to the server
; Obviously, we don’t want the data cases like the user forgetting to enter the Title
or sending the draft without entering the Content
content to wait for sending/receiving from the server
. To recognize and provide immediate response logic to the user in these cases, we can define a MaybePost
type similar to MaybeAge
in the above example.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">type</span> <span class="token constant">MaybePost</span> <span class="token operator">=</span> <span class="token constant">Post</span> <span class="token punctuation">{</span> <span class="token hvariable">title</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token punctuation">,</span> <span class="token hvariable">content</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token punctuation">}</span> <span class="token operator">|</span> <span class="token constant">NoTitle</span> <span class="token operator">|</span> <span class="token constant">NoContent</span> <span class="token hvariable">toPost</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token operator">-></span> <span class="token constant">String</span> <span class="token operator">-></span> <span class="token constant">MaybePost</span> <span class="token hvariable">toPost</span> <span class="token hvariable">title</span> <span class="token hvariable">content</span> <span class="token operator">=</span> <span class="token operator">...</span> <span class="token hvariable">viewPreview</span> <span class="token operator">:</span> <span class="token constant">MaybePost</span> <span class="token operator">-></span> <span class="token constant">Html</span> <span class="token hvariable">msg</span> <span class="token operator">...</span> |
1 2 3 4 | <span class="token comment">-- toPost "hi" "sup?" == Post { title = "hi", content = "sup?" }</span> <span class="token comment">-- toPost "" "sup?" == NoTitle</span> <span class="token comment">-- toPost "hi" "" == NoContent</span> |
With a viewPreview
to preview the contents of valid Post
, we will now be able to display case-specific error messages and prompt the user.
Exception handling like this is extremely common, and performance is best when you fully define the exact exception types with the context to be handled. However, in many cases of simple exception handling, you can use Elm
‘s built-in exception descriptors. So now we will learn about Maybe
and Result
.
Maybe
Module:
elm/core/Maybe
We’ve met Maybe
in previous posts. and as we become familiar with Elm
, we will see Maybe
again very often.
1 2 | <span class="token keyword">type</span> <span class="token constant">Maybe</span> <span class="token hvariable">a</span> <span class="token operator">=</span> <span class="token constant">Just</span> <span class="token hvariable">a</span> <span class="token operator">|</span> <span class="token constant">Nothing</span> |
This is a Union
type union that has two variants, Just
and Nothing
. Where Just
is a simple wrapper type that can hold a value of any data type including primitive
and structured. Nothing
is a special value that represents the case of meaningless data.
Two basic exception handling cases with Maybe
are programs with partial logic for handling Partial Function
and when you want to describe data record structures with optional Optional Field
.
A Partial Function
is when we write a sub-program
that only wants to give results for certain data cases and say Nothing
for other cases. Take for example Elm
‘s built-in String.toFloat
converter.
1 2 3 4 5 6 7 8 9 | <span class="token constant">String</span> <span class="token punctuation">.</span> <span class="token builtin">toFloat</span> <span class="token comment">-- <function> : String -> Maybe Float</span> <span class="token constant">String</span> <span class="token punctuation">.</span> <span class="token builtin">toFloat</span> <span class="token string">"10.01"</span> <span class="token comment">-- Just 10.01 : Maybe Float</span> <span class="token constant">String</span> <span class="token punctuation">.</span> <span class="token builtin">toFloat</span> <span class="token string">"one"</span> <span class="token comment">-- Nothing : Maybe Float</span> |
And Optional Field
are optional fields when we create data structures that describe records like this for example.
1 2 3 4 5 | <span class="token keyword">type</span> <span class="token keyword">alias</span> <span class="token constant">User</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token hvariable">name</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token punctuation">,</span> <span class="token hvariable">age</span> <span class="token operator">:</span> <span class="token constant">Maybe</span> <span class="token constant">Int</span> <span class="token punctuation">}</span> |
The point worth mentioning is that Elm
‘s compiler and language design creates the constraint that: If we write a sub-program
that processes the data of these records, Maybe
will inevitably cause us to have Pattern Matching
. but can’t ignore possible user input cases.
Result
Module:
elm/core/Result
The Maybe
type can help us create the constraint that we need to write code to handle the situation for Exception
. But it can’t help us to describe the cause when an exception occurs. For example, when we try to compile the code and the compiler simply displays a Nothing
result, we will have a lot of trouble figuring out where and why this result is generated in the code.
This is what makes the Result
type appear and be useful. We can use Result
to type-hint
for potentially unsuccessful operations.
1 2 3 | <span class="token keyword">type</span> <span class="token constant">Result</span> <span class="token hvariable">error</span> <span class="token hvariable">value</span> <span class="token operator">=</span> <span class="token constant">Ok</span> <span class="token hvariable">value</span> <span class="token operator">|</span> <span class="token constant">Err</span> <span class="token hvariable">error</span> |
If the processing logic succeeds, the result will be a value
wrapped in Ok
, and if the processing logic fails, the return result will be an Err
value.
1 2 3 4 5 6 7 | <span class="token hvariable">isValidAge</span> <span class="token operator">:</span> <span class="token constant">String</span> <span class="token operator">-></span> <span class="token constant">Result</span> <span class="token constant">String</span> <span class="token constant">Int</span> <span class="token hvariable">isValidAge</span> <span class="token hvariable">input</span> <span class="token operator">=</span> <span class="token keyword">case</span> <span class="token hvariable">String.toInt</span> <span class="token hvariable">input</span> <span class="token keyword">of</span> <span class="token constant">Nothing</span> <span class="token operator">-></span> <span class="token constant">Err</span> <span class="token string">"That is Not a Number!"</span> <span class="token constant">Just</span> <span class="token hvariable">age</span> <span class="token operator">-></span> <span class="token keyword">if</span> <span class="token hvariable">age</span> <span class="token operator"><</span> <span class="token number">0</span> <span class="token keyword">then</span> <span class="token constant">Err</span> <span class="token string">"Invalid for Age!"</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token hvariable">age</span> <span class="token operator">></span> <span class="token number">1001</span> <span class="token keyword">then</span> <span class="token constant">Err</span> <span class="token string">"Legendary Being?"</span> <span class="token keyword">else</span> <span class="token constant">Ok</span> <span class="token hvariable">age</span> |
Now we can not only check the validity of age
, but also give specific error message depending on different input cases by user. This type of response is clearly better than Nothing
.
The Result
type can also help us recover the program’s processing logic for retry at a later time. A good example is when we send an HTTP
request to query data from a certain source. Query results may not be available in a variety of ways.
1 2 3 4 5 6 7 | <span class="token keyword">type</span> <span class="token constant">Error</span> <span class="token operator">=</span> <span class="token constant">BadUrl</span> <span class="token constant">String</span> <span class="token operator">|</span> <span class="token constant">Timeout</span> <span class="token operator">|</span> <span class="token constant">NetworkError</span> <span class="token operator">|</span> <span class="token constant">BadStatus</span> <span class="token constant">Int</span> <span class="token operator">|</span> <span class="token constant">BadBody</span> <span class="token constant">String</span> |
We should certainly be able to display the appropriate error messages as seen in the previous example. But we can also recover the processing logic to try sending the data query request again if we see the Timeout
because most likely the error generated is just random and temporary. If it’s BadStatus 404
then obviously we don’t need to bother trying to send the request again.
Maybe & Result in JS
Because of JavaScript
‘s language design and operating environment, we won’t get the binding mechanism that requires exception handling logic like Maybe
. Instead, we’ll need to get in the habit of writing configurations first to enumerate possible data cases and then design Error
types to describe in detail when null
results are received.
The only caveat about writing exception handling code is that we should focus on the top layer, closest to the code that responds to the request, in any route
handling. Suppose we have a sub-program
that is called first and then that sub-program
will again delegate some of the work to another sub-program
and so on until the n
sub-program
. As such, we should only try to write try..catch
once in the first sub-program
and create handling logic for the defined Error
types.
Ah.. that is scattered for the case of complex processing logic and really necessary. As for the inputAge
example above, the most important thing is that we need to carefully read the configuration of the supporting sub-program
that JS
provides to be able to predict the input results and change the data type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token comment">// -- main : any -> null</span> <span class="token keyword">const</span> <span class="token function-variable function">main</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">_</span> <span class="token punctuation">)</span> <span class="token operator">=></span> window <span class="token punctuation">.</span> <span class="token function">alert</span> <span class="token punctuation">(</span> <span class="token function">tellAge</span> <span class="token punctuation">(</span> <span class="token function">parseAge</span> <span class="token punctuation">(</span> <span class="token function">inputAge</span> <span class="token punctuation">(</span> _ <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// -- tellAge : maybe age -> string</span> <span class="token keyword">const</span> <span class="token function-variable function">tellAge</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">age</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span> Number <span class="token punctuation">.</span> <span class="token function">isNaN</span> <span class="token punctuation">(</span> age <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"Invalid Input"</span> <span class="token operator">:</span> <span class="token punctuation">(</span> age <span class="token operator"><=</span> <span class="token number">12</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"Being built by Heaven"</span> <span class="token operator">:</span> <span class="token punctuation">(</span> age <span class="token operator"><=</span> <span class="token number">21</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"Unpacking old Story"</span> <span class="token operator">:</span> <span class="token punctuation">(</span> age <span class="token operator"><</span> <span class="token number">101</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token string">"Learning new Lessons"</span> <span class="token operator">:</span> <span class="token string">"Finished Learning"</span> <span class="token comment">// -- parseAge : string -> maybe age</span> <span class="token keyword">const</span> <span class="token function-variable function">parseAge</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">input</span> <span class="token punctuation">)</span> <span class="token operator">=></span> Number <span class="token punctuation">.</span> <span class="token function">parseInt</span> <span class="token punctuation">(</span> input <span class="token punctuation">)</span> <span class="token comment">// -- inputAge : any -> string</span> <span class="token keyword">const</span> <span class="token function-variable function">inputAge</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">_</span> <span class="token punctuation">)</span> <span class="token operator">=></span> window <span class="token punctuation">.</span> <span class="token function">prompt</span> <span class="token punctuation">(</span> <span class="token string">"How old are you?"</span> <span class="token punctuation">,</span> _ <span class="token punctuation">)</span> <span class="token comment">// -- start program</span> <span class="token function">main</span> <span class="token punctuation">(</span> <span class="token number">Infinity</span> <span class="token punctuation">)</span> |
(unpublished) [Declarative Programming + Elm] Lesson 10 – Elm Architecture