Arnaud LEMAIRE
June 29, 2018
560

# Créons un système de type en Javascript – SunnyTech 2018

Qu'est-ce qu'un type ? Comment fonctionne un système de type ?

Essayons de répondre brièvement à ces deux questions via cette rapide présentation où nous allons construire un petit système de type à partir de zéro et dans un langage qui n'est pas connu pour ses types : JavaScript.

June 29, 2018

## Transcript

1. A type system in plain
javascript
@lilobase lgo.group

2. What’s the type ?
const count_word =
string => string.split(" ").length;

3. A function is deﬁned by its
domain & co-domain
function
Domain
Co-
Domain

4. A function is deﬁned by its
domain & co-domain
String Integer
count_word

5. A function’s domain &
codomain are sets
String Integer
count_word
Set of all Strings Set of all Integers

6. Knowing about domain & co-domain
Check correctness
Int count_word

7. Enables safe composition
f g
h
h = g•f

f
{ }
This is « pattern matching »

9. Types can be considered as
« Sets »

10. How do we check types ?
const count_word = string => {
if(typeof string !== 'string')
throw new TypeError;
return string.split(" ").length;
}

11. How can we deﬁne a set ?
using a predicate :
- is x a member of E

12. How do we check types ?
const assert = predicate => value => {
if(!predicate(value)) throw new TypeError;
}
const count_word = string => {
assert(e => typeof e === 'string')(string);
return string.split(" ").length;
}

13. How do we check types ?
const assert = predicate => value => {
if(!predicate(value)) throw new TypeError;
}
const Str = assert(e => typeof e === 'string');
const count_word = string => {
Str(string);
return string.split(" ").length;
}

14. How can we create a set ?
using a constructor :
- put x in E if x can be a member of E

15. How do we check types ?
const assert = predicate => value => {
if(!predicate(value)) throw new TypeError;
}
const Str = value => {
assert(e => typeof e === 'string')(value);
return value;
};
const count_word =
string => string.split(" ").length;
const typed_count_word =
value => count_word(Str(value));

16. Type reﬁnement
Integer
count_word

17. Type reﬁnement
Positive
Integer
count_word

18. Type reﬁnement
Positive
Integer
Integer

19. Type reﬁnement
const Int = value => {
assert(e =>
e === parseInt(e, 10) && !isNaN(e)
)(value);
return value;
}

20. Let’s create a type
constructor
const Type = predicate => value => {
assert(predicate)(value);
return value;
}
const Int = Type(
e => e === parseInt(e, 10) && !isNaN(e)
);

21. Type reﬁnement
const Int = Type(
e => e === parseInt(e, 10) && !isNaN(e)
);
const refinement = (type, predicate) => value =>
Type(predicate)(type(value));
const PositiveInt = refinement(Int, e => e > 0);

22. What’s the type ?
const count_each_word = string =>
string.split(" ").reduce(
(acc, item) => {
acc[item] = (acc[item] || 0) + 1;
return acc;
}
, {});

23. What’s the the type ?
String ?
count_each_word

24. Type combinator
String count_each_word
Dictionary :
String -> Int

25. Type combinator
const dict = (domain, codomain) => values => {
Object.entries(values)
.forEach(([key, value]) => {
domain(key);
codomain(value);
});
return values;
};
const Phonebook = dict(Str, Int);
const phonebook = Phonebook({
'Andrew Parson': 8806336,
'Emily Everett': 6784346
});

26. You don’t need to code it
yourself
tcomb
github.com/gcanti/tcomb/

27. Enums
const Vegetable = t.enums.of([
'Tomato',
'Onion',
], 'Vegetable');
const Meat = t.enums.of([
'Mutton'
], 'Meat');
const SeaFood = t.enums.of([
'Shrimp',
'Fish'
], 'SeaFood');

28. Type union
const Ingredient = t.union([
Vegetable,
Meat,
SeaFood
], 'Ingredient');

29. Struct (& list)
const Ingredient = t.union([
Vegetable,
Meat,
SeaFood
], 'Ingredient');

const Kebab = t.struct({
ingredients: t.list(Ingredient),
}, 'Kebab');

30. Typed function
const Vegetable = t.enums.of([
'Tomato',
'Onion',
], 'Vegetable');
const Kebab = t.struct({
ingredients: t.list(Ingredient),
}, 'Kebab');

const isVegetarian = t.func([Kebab], t.Boolean).of(
({ingredients}) => ingredients.every(Vegetable.is)
);
Type deﬁnition
Our function

31. Sub-typing (reﬁnement)
const isVegetarian = t.func([Kebab], t.Boolean).of(
({ingredients}) => ingredients.every(Vegetable.is)
);

const VeggieKebab =
t.refinement(Kebab, isVegetarian, 'VegetarianKebab');

32. Is my kebab vegetarian ?
VegetarianKebab.is(kebab);

33. Type safety
const veggiKebab = VegetarianKebab([
'Tomato',
]);

34. And many other
• Dictionary
• Tuple
• Recursive types
• Maybe
• Intersection
• Interface
• …

35. And, they are available
at runtime…

import fromJSON from 'tcomb/lib/fromJSON';
axios
.get('/api/kebab/veggie/5')
.then(({data}) =>
fromJSON(data, VegetarianKebab));

37. Pattern matching
const result = t.match(myCurrentKebab,
//do something if the kebab is vegetarian
VegetarianKebab, (kebab) => ... ,
//do something else for all other kebab
Kebab, (kebab) => ... ,
//this is definitely not a kebab
t.Any, (x) => ...
);
The value to match against
Type to match
Function to execute if
the associated type is
matched

38. Forms (with react)
ref={form => this.form = form}
type={Kebab}
value={values}
/>

39. Take away
• Types are not data nor behavior
they can be seen as sets
• There is a set algebra we can use to combine types
(algebraic typing)
• Use tComb
(we do it, everyday, on all production projets)
• And, moreover, it enables a better domain
representation

40. Thanks !
@lilobase lgo.group
a fair, secure by design &
transparent exchange