[TOC]
"You only understand what you can build by yourself => use no dependencies"
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.fillStyle = "black";
context.fillRect(0, 0, canvas.width, canvas.height);
const rightArrow = 39;
const leftArrow = 37;
window.onkeydown = evt => {
(evt.keyCode === rightArrow) ? … : … ;
};
setInterval( () => {
nextBoard();
display(context);
}, 1000 / 5);
A function plus
that returns the sum of its arguments:
const plus = x => y => x + y;
Only use let
and const
:
let x = ...; // mutable, local scope
const x = ...; // immutable, local scope
immediately invoked function expression
function foo() {..}; foo()
( function foo() {..} ) ()
( function() {..} ) ()
( () => {..} ) ()
const id = x => x
const id = y => y
( f => x => f(x) ) (id) (1)
( x => id(x)) (1)
( id(1))
(x => x) (1)
1
x => y => plus (x) (y)
x => plus (x)
plus
F3
is a proper eta reduction of F2
const id = x => x;
const konst = x => y => x;
const F1 = x => y => konst (id) (x) (y);
const F2 = x => konst (id) (x);
const F3 = konst (id);
a1
is a proper beta expansion of a2
const id = x => x;
const a1 = y => id(y);
const a2 = y => y;
a2
is a proper beta reduction of a1
const id = x => x;
const a1 = y => id(y);
const a2 = y => y;
id1
and id2
are alpha-equivalent
const id1 = x => x;
const id2 = y => y;
"I recommend that you write programs as though JavaScript had been designed correctly."
- Douglas Crockford, How JavaScript works, p. 6.2
// atoms
const id = x => x;
const konst = x => y => x;
// derived true, false, and, or, equals, …
const F = …;
const T = …;
const pair = x => y => f => f(x)(y);
const fst = p => p(T);
const snd = p => p(F);
const person =
firstname =>
lastname =>
age =>
pair (pair(firstname)(lastname)) (age);
const firstn = p => fst(fst(p));
const lastn = p => snd(fst(p));
const age = p => snd(p);
// dual of the product
const pair = x => y => f => f(x)(y); // one ctor
const fst = p => p(T); // accessor 1
const snd = p => p(F); // accessor 2
// here we have now the basic sum type
const Left = x => f => g => f(x); // ctor 1
const Right = x => f => g => g(x); // ctor 2
const either = e => f => g => e(f)(g); // accessor
// go around null / undefined
const Nothing = Left ();
const Just = Right;
const maybe = either;
maybe (expressionThatMightGoWrong)
(handleBad)
(handleGood);
// error handling with either
const eShow = result => result (msg => msg) (val => "Result is:" + val);
"Developers seem to love those languages most, in which they understood the value of higher-order functions."
- @ProfDKoenig
const times = a => b => a * b;
const twoTimes = times(2);
[1, 2, 3].map(x => times(2)(x));
[1, 2, 3].map(times(2));
[1, 2, 3].map(twoTimes);
const odd = x => x % 2 === 1;
[1, 2, 3].filter(x => x % 2 === 1);
[1, 2, 3].filter(x => odd(x));
[1, 2, 3].filter(odd);
const plus = (accu, cur) => accu + cur;
[1, 2, 3].reduce((accu, cur) => accu + cur);
[1, 2, 3].reduce(plus);
// variant with initial accu value as 2nd argument
// then cur starts at first element
[1, 2, 3].reduce(plus, 0);
// function that checks which numbers in an array are divideable
// by the given argument
const divides = x => y => y % x === 0;
// function that joins values of an array together with given argument
const join = (a) => (b, c) => b + a + c;
"Unfortunately, JS has a misfeature called Automated Semicolon Insertion. It can fail in bad ways, so write like a professional."
- Douglas Crockford, "How JavaScript works."
- Command Line
- Automation
- Build System
- Templating
- Code Distribution
- Formulae
- Business Rules
- Smart Configuration
- Product Lines
- DSL
- Self Modifying Code
- ...
document.write('<script src=...');
Works as if the code was copied verbatim in the place of the eval. => You share the scope.
eval('some code');
Function()
is like eval()
but declares parameters and executes in the global scope. It creates a reference.
const add = Function('x', 'y', 'return x+y');
add(1, 2);
add(2, 3); // no need to re-parse
In JavaScript you cannot exclude possibly harmful side effects from scripts that are loaded from foreign sources...
=> Privacy, Security, Stability
Calculate the bonus with given attributes:
const bonusCalculation = x => x.bonus = eval(x.revenue) * factor_;
Calculate the bonus with given attributes using Function():
const bonusCalculation = Function('x', 'return x.bonus = x.revenue *' + factor_);
"It's called object-oriented to tell you what you should do with it: object!"
- Phil Wadler, quoted from memory
JS "Objects".
const good = {
firstname : "Good",
lastname : "Boy",
getName : function() {
return this.firstname + " " + this.lastname
}
};
// no safety but super dynamic
// unobvious how to share structure
// beware of "this"! See Adam Breindl last week.
Closure scope, no "this".
function Person(first, last) {
let firstname = first; // optional
let lastname = last;
return {
getName: function() {
return firstname + " " + lastname }
}
}
}
// best safety, easy to share structure, but no class
Depends on "new". Is the "default" construction.
Still dynamic but all "instances" can be changed at once by changing the prototype!
const Person = ( () => { // lexical scope
function Person(first, last) { // ctor, binding
this.firstname = first;
this.lastname = last;
}
Person.prototype.getName = function() {
return this.firstname + " " + this.lastname;
};
return Person;
}) (); // IIFE
// new Person("Good", "Boy") instanceof Person
Check wether two Arrays are equal:
Array.prototype.eq = function(array) {
if (this.length !== array.length) return false;
for (let i = 0; i < array.length; i++) {
if (this[i] !== array[i]) return false;
}
return true;
}
"Classes tend to be bad modules."
- D. Crockford, How JS works, p. 17.0
class Person {
constructor(first, last) {
this.firstname = first;
this.lastname = last
}
getName() {
return this.firstname + " " + this.lastname
}
}
// new Person("Good", "Boy") instanceof Person
class Student extends Person {
constructor (first, last, grade) {
super(first, last); // never forget
this.grade = grade;
}
}
const s = new Student("Top","Student", 5.5);
const s = new Student()
// s.__proto__ === Student.prototype;
// Object.getPrototypeOf(s) === Student.prototype;
// => s instanceof Student
Function composition -> it must work for all functions!
Function.prototype.then = function(bracketFunc) {
// this(number -> result of the first function
// with the value at the end f.e. ..(1))
const compose = bracketFunc => number => bracketFunc( this(number) );
return compose(bracketFunc);
}
"Live's a dance you learn as you go. Sometimes you lead, sometimes you follow."
- John Michael Montgomery
- You must learn the moves.
- Then you can combine the moves and adapt to the situation at hand.
- Become aware what you do.
- We program collaboratively and look for moves.
- More structure.
- Better reports when tests fail.
- smooth transition.
- What is the expected result?
- What is the simplest thing that could possibly work?
- Technical feasibility, hypotheses, border cases.
- The goal is to learn and verify, delete when finished.
- Give yourself a timebox.
- Make static "sketch" of the result before adding dynamic features.
- Dynamic sketches: all JS, CSS in a single HTML file.
- Replace static values with variables.
- Replace repetitions with mappings and loops.
- Discover the concept behind what you have extracted. Give it a name.
- It should work for itself and in combination.
- Revert if you cannot find one.
- Organize and re-factor to make your future work easier.
- Facilitate extensions or improvements.
- Prepare for release.
- The solution must stand on its own without tacit knowledge or external help.
- Tests, documentation, examples.
- Before every push to the Repository.
- What to keep?
- What to try differently next time?
- Balance & distance
- Which technologies support my moves: dynamic exploration, refactoring
- Per feature, per project, whole career, ...
Failsafe callback:
const failSafe = defaultValue => callback => argToCallback => {
return argToCallback === null ? defaultValue : callback(argToCallback);
}
"Frameworks and APIs change fast. Software design principles are evergreen. Learn principles that translate across language barriers."
- Eric Elliot
Higher-order Function.
function test(name, callback) {
const assert = Assert(); // prework
callback(assert); // callback
report(name, assert.getOk()); // postwork
}
const Observable = value => {
const listeners = []; // many
return {
onChange: callback => listeners.push(callback),
getValue: () => value,
setValue: val => {
if (value === val) return; // protection
// ordering
value = val;
listeners.forEach(notify => notify(val));
}
}
};
Using Observable that it keeps track of the sum of all values:
trackable.onChange( _ => sum += trackable.getValue() );
"Don't call us. We call you."
- Hollywood
test("todo-memory-leak", assert => {
const todoController = TodoController();
todoController.onTodoAdd(todo => {
todoController.onTodoRemove( (todo, removeMe) => {
removeMe(); // idea: self remove
});
});
for (let i=0; i<10000; i++){
const todo = todoController.addTodo();
todoController.removeTodo(todo);
}
});
function start() {
//...
window.onkeydown = evt => {
// doSomething();
};
setInterval(() => {
// doSomething();
}, 1000 / 5);
}
Most prominent use:
fetch ('http://fhnw.ch/json/students/list')
.then(response => response.json())
.then(students => console.log(students.length))
.catch (err => console.log(err)
Success / Failure callbacks:
// definition
const processEven = i => new Promise( (resolve, reject) => {
if (i % 2 === 0) {
resolve(i);
} else {
reject(i);
}
});
// use
processEven(4)
.then ( it => {console.log(it); return it} ) // auto promotion
.then ( it => processEven(it+1))
.catch( err => console.log( "Error: " + err))
const foo = async i => {
const x = await processEven(i).catch( err => err);
console.log("foo: " + x);
};
foo(4);
Other variant:
async function foo(i) {
try {
const x = await processEven(i);
console.log("foo: " + x);
}
catch(err) { console.log(err); }
};
foo(4);
A NullSafe construction in the style of a Promise
:
const NullSafe = x => {
const isNullSafe = y => y && y.then;
const maywrap = y => isNullSafe(y) ? y : NullSafe(y);
return {
then: fn => (x !== null && x != undefined)
? maywrap(fn(x))
: NullSafe(x)
}
};
Similar to concurrency
- No coordination needed
- Sequence (of side effects)
- Dependeny on former results
Nothing to do !
- Execution model: confined
- All actions run independently
Actor
- In a sequence of actions, each action can only start if the preceding one has finished
- How to achieve this => Delegated Coordination => Scheduler
- Action B and C need the result of action A
- A must be executed exactly once before B and C
- How to do this => Implicit Coordination => DataFlowVariable
- Queue (FIFO) of functions that are started with a lock
- Callback unlocks
- Function, that sets a value if it is not already set. Returns the value.
- Lazy: Access to variables that will become available later
- Trick: Do not set the value, but a function that returns the value
"Well-structured software is easy to write and to debug, and provides a collection of modules that can be reused to reduce future programming costs."
- John Hughes
- Organize the Code
- Clear dependencies
- Avoid errors: Globals, Scoping, Namespace
// avoid something like this in your html document
<script src="fileA.js">
<script src="fileB.js">
<script src="fileC.js">
// if fileA.js has a reference on fileC.js it won't work !!!
- How I want to edit the code
- How I want to deliver the code
They are not...
- Packages (those have versions)
- Dependencies, Libraries, Releases
- Units of publication
- Objects
webpack
, npm
, bower
, yarn
, ...
webpack
, npm
, grunt
, gulp
, ...
CommonJS
, AMD
, UMD
, ...
RequireJS
, SystemJS
, browserify
, ...
// Use URI format as follows: "./myFile.js"
<script src="./myFile.js" type="module"> // type implies "defer"
import ("./myFile.js").then( modules => ... )
// invasive, transitive impact
Always explicit!
// most used
import "module-name";
import { export1, export2 } from "module-name";
// other variants
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
var promise = import("module-name");
Always explicit!
// most used
export { name1, name2, ... , nameN };
// other variants
export function FunctionName() { .. }
export const name1, name2, ... , nameN; // or let
export class ClassName { .. }
export default expression;
export { name1 as default, .. };
export * from .. ;
export { name1, name2, ... , nameN } from .. ;
- implicit "use-strict" exports are read-only !
- no Global object, no Global "this", no Global hoisting
- implicit "defer" mode =>
document.writeln
is no longer useful - Modules are Namespaces
- Modules are Singletons
- Modules are subject to the SOP
- Problem at construction time: the File System is a null origin
- Developer Mode (suppress SOP) -> don't forget to set back!
- Local Webserver
- Disable cache!
- Bundler (Rollup, Parcel, Webpack, ...)
- Start Browser in Debug mode
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
- http://exploringjs.com/es6/ch_modules.html
"The limits of my language are the limits of my world."
- Ludwig Wittgenstein, Tractatus Logico-Philosophicus
- All are transpiling to JavaScript !
- Allow React / Elm Architecture for functional JavaScript / SPA
- TypeScript + React
- PureScript + Pux / Halogen
- Elm
View -> Action -> State -> State ___________|
^ |
|__________________________________________|
- Typed state
- Actions as functions / lamdas
- State immutability with discipline
- Object / component abstraction
- Typed state
- Action type with values
- State is immutable
- Update function is a fold
- Function composition
- Like Elm
- But even more Haskell-ish
- https://try.purescript.org/
// signature
// name quantifier type variables
konst :: forall a b . a -> b -> a
// definition
konst x y = x
TypeScript | Type declaration |
---|---|
PureScript | Type declaration |
Elm | Port / Flag |
TypeScript | JS Environment |
---|---|
PureScript | JS Environment |
Elm | Browser |
TypeScript | OO with Generics |
---|---|
PureScript | Functional |
Elm | Functional |
TypeScript | Language |
---|---|
PureScript | Language & Tools |
Elm | Programming System |
TypeScript | Sum (union) type , String Literal type |
---|---|
PureScript | Eff Monad, [GADT] |
Elm | Time travel debug , SemVer guarantee |
ClojureScript | Clojure (Lisp) |
---|---|
GHCJS | Haskell |
Babel | JS |
CoffeeScript | JS++ |
GrooScript | Groovy |