Skip to content

Commit

Permalink
make type checking optional
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchambers committed Feb 9, 2016
1 parent 612f6f0 commit e196bc1
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 41 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ const env = $.env.concat([Integer, NonZeroInteger]);
The next step is to define a `def` function for the environment:

```javascript
const def = $.create(env);
const def = $.create(true, env);
```

The first argument to `$.create` determines whether type checking is enabled.
This allows one to only pay the performance cost of run-time type checking
during development. For example:

```javascript
const def = $.create(process.env.NODE_ENV === 'development', env);
```

`def` is a function for defining functions. For example:
Expand Down Expand Up @@ -650,7 +658,10 @@ For example:
// TimeUnit :: Type
const TimeUnit = $.EnumType(['milliseconds', 'seconds', 'minutes', 'hours']);

const def = $.create($.env.concat([TimeUnit, $.ValidDate, $.ValidNumber]));
// env :: [Type]
const env = $.env.concat([TimeUnit, $.ValidDate, $.ValidNumber]);

const def = $.create(true, env);

// convertTo :: TimeUnit -> ValidDate -> ValidNumber
const convertTo =
Expand Down
64 changes: 36 additions & 28 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -671,8 +671,8 @@
);
};

// create :: [Type] -> Function
$.create = function(_env) {
// create :: (Boolean, [Type]) -> Function
$.create = function(checkTypes, _env) {
// env :: [Type]
var env = map(_env, function(x) {
return typeof x === 'function' ?
Expand Down Expand Up @@ -795,11 +795,13 @@
var curry = function(name, constraints, expArgTypes, expRetType,
_typeVarMap, _values, _indexes, impl) {
return arity(_indexes.length, function() {
var delta = _indexes.length - arguments.length;
if (delta < 0) {
throw invalidArgumentsLength(name,
expArgTypes.length,
expArgTypes.length - delta);
if (checkTypes) {
var delta = _indexes.length - arguments.length;
if (delta < 0) {
throw invalidArgumentsLength(name,
expArgTypes.length,
expArgTypes.length - delta);
}
}
var $typeVarMap = {};
for (var typeVarName in _typeVarMap) {
Expand All @@ -816,14 +818,16 @@
arguments[idx]['@@functional/placeholder'] === true)) {

var value = arguments[idx];
var expType = expArgTypes[index];
if (!expType.test(value) ||
isEmpty(satisfactoryTypes(name, constraints, $typeVarMap,
expType, value, index))) {
throw invalidValue(name,
replaceTypeVars($typeVarMap)(expType),
value,
index);
if (checkTypes) {
var expType = expArgTypes[index];
if (!expType.test(value) ||
isEmpty(satisfactoryTypes(name, constraints, $typeVarMap,
expType, value, index))) {
throw invalidValue(name,
replaceTypeVars($typeVarMap)(expType),
value,
index);
}
}
values[index] = value;
} else {
Expand All @@ -832,11 +836,13 @@
}
if (isEmpty(indexes)) {
var returnValue = impl.apply(this, values);
if (!expRetType.test(returnValue)) {
throw invalidReturnValue(name, [expRetType], returnValue);
if (checkTypes) {
if (!expRetType.test(returnValue)) {
throw invalidReturnValue(name, [expRetType], returnValue);
}
satisfactoryTypes(name, constraints, $typeVarMap,
expRetType, returnValue, NaN);
}
satisfactoryTypes(name, constraints, $typeVarMap,
expRetType, returnValue, NaN);
return returnValue;
} else {
return curry(name, constraints, expArgTypes, expRetType,
Expand All @@ -846,15 +852,17 @@
};

return function def(name, constraints, expTypes, impl) {
if (arguments.length !== def.length) {
throw invalidArgumentsLength('def', def.length, arguments.length);
}
if (checkTypes) {
if (arguments.length !== def.length) {
throw invalidArgumentsLength('def', def.length, arguments.length);
}

var Type = RecordType({test: $.Function});
var types = [$.String, $.Object, $.Array(Type), $.Function];
for (var idx = 0; idx < types.length; idx += 1) {
if (!types[idx].test(arguments[idx])) {
throw invalidArgument('def', [types[idx]], arguments[idx], idx);
var Type = RecordType({test: $.Function});
var types = [$.String, $.Object, $.Array(Type), $.Function];
for (var idx = 0; idx < types.length; idx += 1) {
if (!types[idx].test(arguments[idx])) {
throw invalidArgument('def', [types[idx]], arguments[idx], idx);
}
}
}

Expand All @@ -867,7 +875,7 @@
);
}

assertExpectedTypesInEnvironment(name)(expTypes);
if (checkTypes) assertExpectedTypesInEnvironment(name)(expTypes);

return curry(name,
constraints,
Expand Down
49 changes: 38 additions & 11 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var errorEq = R.curry(function(type, message, error) {
});


var def = $.create($.env);
var def = $.create(true, $.env);

var a = $.TypeVariable('a');
var b = $.TypeVariable('b');
Expand Down Expand Up @@ -159,7 +159,7 @@ var $Pair = $.BinaryType(

describe('def', function() {

it('type checks its arguments', function() {
it('type checks its arguments when checkTypes is true', function() {
throws(function() { def(); },
errorEq(TypeError,
'‘def’ requires four arguments; received zero arguments'));
Expand All @@ -186,6 +186,24 @@ describe('def', function() {
'as its fourth argument; received null'));
});

it('does not type check its arguments when checkTypes is false', function() {
var def = $.create(false, $.env);

// add :: Number -> Number -> Number
var add =
def('add',
{},
[$.Number, $.Number, $.Number],
function(x, y) { return x + y; });

eq(add(42, 1), 43);
eq(add(42)(1), 43);
eq(add(1, 2, 3, 4), 3);
eq(add(1)(2, 3, 4), 3);
eq(add('XXX', {foo: 42}), 'XXX[object Object]');
eq(add({foo: 42}, 'XXX'), '[object Object]XXX');
});

it('returns a function whose length matches that of given list', function() {
eq($0.length, 0);
eq($1.length, 1);
Expand Down Expand Up @@ -427,7 +445,8 @@ describe('def', function() {
});

it('supports custom types', function() {
var def = $.create($.env.concat([Integer, $Pair]));
var env = $.env.concat([Integer, $Pair]);
var def = $.create(true, env);

var T = $.Array($Pair($.String, Maybe($.Number)));

Expand Down Expand Up @@ -468,7 +487,8 @@ describe('def', function() {
// TimeUnit :: Type
var TimeUnit = $.EnumType(['milliseconds', 'seconds', 'minutes', 'hours']);

var def = $.create($.env.concat([TimeUnit, $.ValidDate, $.ValidNumber]));
var env = $.env.concat([TimeUnit, $.ValidDate, $.ValidNumber]);
var def = $.create(true, env);

// convertTo :: TimeUnit -> ValidDate -> ValidNumber
var convertTo =
Expand All @@ -495,7 +515,8 @@ describe('def', function() {
// SillyType :: Type
var SillyType = $.EnumType(['foo', true, 42]);

var _def = $.create($.env.concat([SillyType]));
var _env = $.env.concat([SillyType]);
var _def = $.create(true, _env);

// id :: a -> a
var id = _def('id', {}, [a, a], R.identity);
Expand Down Expand Up @@ -529,7 +550,8 @@ describe('def', function() {
// Line :: Type
var Line = $.RecordType({start: Point, end: Point});

var def = $.create($.env.concat([Point, Line]));
var env = $.env.concat([Point, Line]);
var def = $.create(true, env);

// dist :: Point -> Point -> Number
var dist = def('dist', {}, [Point, Point, $.Number], function(p, q) {
Expand Down Expand Up @@ -587,7 +609,8 @@ describe('def', function() {
});

it('supports "nullable" types', function() {
var def = $.create($.env.concat([$.Nullable]));
var env = $.env.concat([$.Nullable]);
var def = $.create(true, env);

// toUpper :: Nullable String -> Nullable String
var toUpper =
Expand Down Expand Up @@ -632,7 +655,8 @@ describe('def', function() {
});

it('supports the "ValidDate" type', function() {
var def = $.create($.env.concat([$.ValidDate]));
var env = $.env.concat([$.ValidDate]);
var def = $.create(true, env);

// sinceEpoch :: ValidDate -> Number
var sinceEpoch = def('sinceEpoch',
Expand Down Expand Up @@ -857,7 +881,8 @@ describe('def', function() {
});

it('supports polymorphism via type variables', function() {
var def = $.create($.env.concat([Either, Maybe, $Pair]));
var env = $.env.concat([Either, Maybe, $Pair]);
var def = $.create(true, env);

// aa :: a -> a -> (a, a)
var aa = def('aa', {}, [a, a, $Pair(a, a)], Pair);
Expand Down Expand Up @@ -946,7 +971,8 @@ describe('def', function() {
});

it('does not allow heterogeneous arrays', function() {
var def = $.create($.env.concat([Either]));
var env = $.env.concat([Either]);
var def = $.create(true, env);

// concat :: [a] -> [a] -> [a]
var concat =
Expand Down Expand Up @@ -981,7 +1007,8 @@ describe('def', function() {
});

it('supports type-class constraints', function() {
var def = $.create($.env.concat([Integer, Maybe, Either]));
var env = $.env.concat([Integer, Maybe, Either]);
var def = $.create(true, env);

// hasMethods :: [String] -> a -> Boolean
var hasMethods = R.curry(function(names, x) {
Expand Down

0 comments on commit e196bc1

Please sign in to comment.