When I started programming with Javascript, I've wondered why I don't ever come across code like the following:
function typeOf(type) {
return function (x) {
return typeof x === type;
};
}
function num(n) {
return n === n && typeOf("number")(n);
}
function constraint(predicate) {
return function (errmsg) {
return function (x) {
if (predicate(x)) {
return x;
}
throw new Error(errmsg);
};
};
}
var numC = constraint(num)("number expected");
function add(x, y) {
numC(x), numC(y);
return numC(x + y);
}
add("a", 2); // throws "number expected"
In the libs I couldn't find such code. For smaller projects type checks were obviously not necessary. But how could that work with extensive projects?
With typeof, instanceof and getPrototypeOf there are three reflexive tools to distinguish primitives and object types. Duck typing, the equivalence of structural typing in object-oriented and dynamically typed languages like Javascript, is another technique for distinguishing objects. That's a lot to make it easy to ignore.
The type system and object model of JavaScript can be very confusing because it offers so much freedom. Later, the relationships became clear to me:
- If I spread type checks all over my code, I go against the principle of dynamically typed languages and establish parts of the functionality of a compiler at runtime* - for each request!
- Developers of dynamically typed languages use unit tests so excessively, in order to compensate the lack of a compiler and static type system.
*I know that compiler do much more than just check types
I concluded that type checks are only useful if they are required for a specific functionality, but not to guarantee types in general. Ad hoc polymorphism (aka function overloading) is an example of a functionality that relies on type reflection. Here is a more or less useful example:
function isEmpty(x) {
switch (typeof x) {
case "number": return isEmptyN(x);
case "object":
if (x !== null && typeof x.empty === "function") { // duck typing
return x.empty();
}
switch (Object.prototype.toString.call(x).slice(8, -1).toLowerCase()) {
case "arguments": return isEmptyA(x);
case "array": return isEmptyA(x);
case "function": return isEmptyO(x);
case "object": return isEmptyO(x);
default: break;
}
case "string": return isEmptyS(x);
default: break;
}
throw new Error("primitive/object doesn't support empty protocol");
}
However, there also seems to be a need for "stronger typing" in Javascript:
- Typescript
- flow static type checker
Are my conclusions still correct?
Aucun commentaire:
Enregistrer un commentaire