These are the code snippets used during the talk "Hope is postponed disappointment".
To test the Elm code, you can either do it online following the links below, or running these commands to test them locally
git clone https://github.com/lucamug/hope.git
cd hope
npx elm reactor
Then you can browse the *.elm
files from http://localhost:8000/
To test the TypeScript code, you can do it online following the links below.
module Main exposing (main)
import Html exposing (text)
import Json.Decode exposing (Error, decodeString, errorToString, field, map, string)
-- type Result error value = Ok value | Err error
type alias User =
{ name : String }
parseJSON : String -> Result Error User
parseJSON =
field "name" string |> map User |> decodeString
json =
"{ \"name\": \"John\" }"
result =
parseJSON json
main =
case result of
Ok user ->
text ("Hi " ++ user.name)
Err error ->
text ("Error: " ++ errorToString error)
type Result <Err, Ok> = { kind: 'Ok'; value: Ok } | { kind: 'Err'; error: Err };
type User = { name: string };
function parseJSON (json: string | undefined): Result <string, User> {
try {
if (json === undefined) {
return { kind: 'Err', error: 'JSON is undefined' };
}
const user = JSON.parse(json) as User;
if (!user || !user.name) {
return { kind: 'Err', error: '"name" key is missing' };
}
return { kind: 'Ok', value: user };
} catch (error) {
return { kind: 'Err', error: `Invalid JSON ${error}` };
}
}
const json = '{ "name": "John" }';
const result = parseJSON(json);
switch (result.kind) {
case 'Ok':
console.log("Hi " + result.value.name);
break;
case 'Err':
console.log("Error: " + result.error);
break;
default:
const exhaustiveCheck: never = result;
break;
}
Using map
and andThen
to concatenate functions that may fail divideBy
and not fail multiplyBy
.
type Result<X, A> = { tag: 'Ok'; value: A } | { tag: 'Err'; error: X };
const divideBy = (a: number, b: number): Result<string, number> => {
if (a === 0) {
return { tag: 'Err', error: 'Divided by 0' };
} else {
return { tag: 'Ok', value: b / a };
}
}
const multiplyBy = (a: number, b: number): number => {
return a * b;
}
const map = <X, A, B>(callback: (value: A) => B, result: Result<X, A>): Result<X, B> => {
switch (result.tag) {
case 'Err': return { tag: 'Err', error: result.error };
case 'Ok' : return { tag: 'Ok', value: callback(result.value) };
}
}
const andThen = <X, A, B>(callback: (value: A) => Result<X, B>, result: Result<X, A>): Result<X, B> => {
switch (result.tag) {
case 'Err': return { tag: 'Err', error: result.error };
case 'Ok' : return callback(result.value);
}
}
// 9 / 3 * 10 / 5 * 2
const total =
map ( res => multiplyBy ( 2, res ),
andThen ( res => divideBy ( 5, res ),
map ( res => multiplyBy ( 10, res ),
andThen ( res => divideBy ( 3, res ),
{ tag: 'Ok', value: 9 } ))));
console.log(total);
// Logged value: { tag: 'Ok', value: 12 }
Adding partial application.
module Main exposing (main)
import Html exposing (text)
import Result exposing (andThen, map)
divideBy a b = if a == 0 then Err "Divided by 0" else Ok (b / a)
multiplyBy a b = a * b
-- 9 / 3 * 10 / 5 * 2
total =
Ok 9
|> andThen ( divideBy 3 )
|> map ( multiplyBy 10 )
|> andThen ( divideBy 5 )
|> map ( multiplyBy 2 )
main = text <| Debug.toString total
-- Logged value: Ok 12
type Result<X, T> = { tag: 'Ok'; value: T } | { tag: 'Err'; error: X };
const map = <A, B>(callback: (value: A) => B) => (result: Result<string, A>): Result<string, B> => {
switch (result.tag) {
case 'Ok': return { tag: 'Ok', value: callback(result.value) };
case 'Err': return result;
}
};
const andThen = <A, B>(callback: (value: A) => Result<string, B>) => (result: Result<string, A>): Result<string, B> => {
switch (result.tag) {
case 'Ok': return callback(result.value);
case 'Err': return result;
}
};
const divideBy = (a: number) => (b: number): Result<string, number> => {
if (a === 0) {
return { tag: 'Err', error: 'Divided by 0' };
} else {
return { tag: 'Ok', value: b / a };
}
};
const multiplyBy = (a: number) => (b: number): number => {
return a * b;
}
// 9 / 3 * 10 / 5 * 2
const total =
map ( multiplyBy ( 2 ) )(
andThen ( divideBy ( 5 ) )(
map ( multiplyBy ( 10 ) )(
andThen ( divideBy ( 3 ) )(
{ tag: 'Ok', value: 9 }))));
console.log(total);
// Logged value: { tag: 'Ok', value: 12 }