intertype
Advanced tools
Comparing version 201.1.0 to 202.0.0
209
lib/main.js
(function() { | ||
'use strict'; | ||
var E, Intertype, Intertype_minimal, WG, _TMP_basetype_names, _TMP_basetype_names_matcher, _TMP_isa_minimal_type/* TAINT unfortunate choice of name */, _TMP_minimal_types, __type_of, _isa, basetypes, debug, deepmerge, default_declarations, default_types, hide, nameit, rpr, set, types, walk_prefixes; | ||
var E, Intertype, Intertype_minimal, WG, _TMP_basetype_names, _TMP_basetype_names_matcher, _TMP_isa_minimal_type/* TAINT unfortunate choice of name */, _TMP_minimal_types, __type_of, _isa, basetypes, debug, deepmerge, default_declarations, default_types, hide, nameit, rpr, set, types, walk_prefixes, | ||
modulo = function(a, b) { return (+a % (b = +b) + b) % b; }; | ||
@@ -98,2 +99,14 @@ //=========================================================================================================== | ||
return x instanceof Set; | ||
}, | ||
nan: (x) => { | ||
return Number.isNaN(x); | ||
}, | ||
finite: (x) => { | ||
return Number.isFinite(x); | ||
}, | ||
/* TAINT make sure no non-numbers slip through */integer: (x) => { | ||
return Number.isInteger(x); | ||
}, | ||
/* TAINT make sure no non-numbers slip through */safeinteger: (x) => { | ||
return Number.isSafeInteger(x); | ||
} | ||
@@ -123,3 +136,3 @@ }; | ||
//----------------------------------------------------------------------------------------------------------- | ||
default_declarations = { | ||
/* TAINT make sure no non-numbers slip through */ default_declarations = { | ||
basetype: { | ||
@@ -197,2 +210,188 @@ test: _isa.basetype | ||
} | ||
}, | ||
nan: { | ||
test: _isa.nan, | ||
template: 0/0 | ||
}, | ||
finite: { | ||
test: _isa.finite, | ||
template: 0 | ||
}, | ||
integer: { | ||
test: _isa.integer, | ||
template: 0 | ||
}, | ||
safeinteger: { | ||
test: _isa.safeinteger, | ||
template: 0 | ||
}, | ||
//......................................................................................................... | ||
'empty': { | ||
role: 'qualifier' | ||
}, | ||
'nonempty': { | ||
role: 'qualifier' | ||
}, | ||
'empty.list': function(x) { | ||
return (this.isa.list(x)) && (x.length === 0); | ||
}, | ||
'empty.text': function(x) { | ||
return (this.isa.text(x)) && (x.length === 0); | ||
}, | ||
'empty.set': function(x) { | ||
return (this.isa.set(x)) && (x.size === 0); | ||
}, | ||
'empty.object': function(x) { | ||
return (this.isa.object(x)) && ((Object.keys(x)).length === 0); | ||
}, | ||
'nonempty.list': function(x) { | ||
return (this.isa.list(x)) && (x.length > 0); | ||
}, | ||
'nonempty.text': function(x) { | ||
return (this.isa.text(x)) && (x.length > 0); | ||
}, | ||
'nonempty.set': function(x) { | ||
return (this.isa.set(x)) && (x.size > 0); | ||
}, | ||
'nonempty.object': function(x) { | ||
return (this.isa.object(x)) && ((Object.keys(x)).length > 0); | ||
}, | ||
//......................................................................................................... | ||
'positive': { | ||
role: 'qualifier' | ||
}, | ||
'negative': { | ||
role: 'qualifier' | ||
}, | ||
'posnaught': { | ||
role: 'qualifier' | ||
}, | ||
'negnaught': { | ||
role: 'qualifier' | ||
}, | ||
'positive.float': function(x) { | ||
return (this.isa.float(x)) && (x > 0); | ||
}, | ||
'positive.integer': function(x) { | ||
return (this.isa.integer(x)) && (x > 0); | ||
}, | ||
'positive.infinity': function(x) { | ||
return (this.isa.infinity(x)) && (x > 0); | ||
}, | ||
'negative.float': function(x) { | ||
return (this.isa.float(x)) && (x < 0); | ||
}, | ||
'negative.integer': function(x) { | ||
return (this.isa.integer(x)) && (x < 0); | ||
}, | ||
'negative.infinity': function(x) { | ||
return (this.isa.infinity(x)) && (x < 0); | ||
}, | ||
'posnaught.float': function(x) { | ||
return (this.isa.float(x)) && (x >= 0); | ||
}, | ||
'posnaught.integer': function(x) { | ||
return (this.isa.integer(x)) && (x >= 0); | ||
}, | ||
'posnaught.infinity': function(x) { | ||
return (this.isa.infinity(x)) && (x >= 0); | ||
}, | ||
'negnaught.float': function(x) { | ||
return (this.isa.float(x)) && (x <= 0); | ||
}, | ||
'negnaught.integer': function(x) { | ||
return (this.isa.integer(x)) && (x <= 0); | ||
}, | ||
'negnaught.infinity': function(x) { | ||
return (this.isa.infinity(x)) && (x <= 0); | ||
}, | ||
'cardinal': 'posnaught.integer', | ||
//......................................................................................................... | ||
'frozen': { | ||
role: 'qualifier' | ||
}, | ||
'sealed': { | ||
role: 'qualifier' | ||
}, | ||
'extensible': { | ||
role: 'qualifier' | ||
}, | ||
'frozen.list': function(x) { | ||
return (this.isa.list(x)) && (Object.isFrozen(x)); | ||
}, | ||
'sealed.list': function(x) { | ||
return (this.isa.list(x)) && (Object.isSealed(x)); | ||
}, | ||
'extensible.list': function(x) { | ||
return (this.isa.list(x)) && (Object.isExtensible(x)); | ||
}, | ||
'frozen.object': function(x) { | ||
return (this.isa.object(x)) && (Object.isFrozen(x)); | ||
}, | ||
'sealed.object': function(x) { | ||
return (this.isa.object(x)) && (Object.isSealed(x)); | ||
}, | ||
'extensible.object': function(x) { | ||
return (this.isa.object(x)) && (Object.isExtensible(x)); | ||
}, | ||
//......................................................................................................... | ||
'odd': { | ||
role: 'qualifier' | ||
}, | ||
'even': { | ||
role: 'qualifier' | ||
}, | ||
'odd.positive': { | ||
role: 'qualifier' | ||
}, | ||
'odd.negative': { | ||
role: 'qualifier' | ||
}, | ||
'odd.posnaught': { | ||
role: 'qualifier' | ||
}, | ||
'odd.negnaught': { | ||
role: 'qualifier' | ||
}, | ||
'even.positive': { | ||
role: 'qualifier' | ||
}, | ||
'even.negative': { | ||
role: 'qualifier' | ||
}, | ||
'even.posnaught': { | ||
role: 'qualifier' | ||
}, | ||
'even.negnaught': { | ||
role: 'qualifier' | ||
}, | ||
'odd.integer': function(x) { | ||
return (this.isa.integer(x)) && (modulo(x, 2)) === 1; | ||
}, | ||
'even.integer': function(x) { | ||
return (this.isa.integer(x)) && (modulo(x, 2)) === 0; | ||
}, | ||
'odd.positive.integer': function(x) { | ||
return (this.isa.positive.integer(x)) && (this.isa.odd.integer(x)); | ||
}, | ||
'even.positive.integer': function(x) { | ||
return (this.isa.positive.integer(x)) && (this.isa.even.integer(x)); | ||
}, | ||
'odd.negative.integer': function(x) { | ||
return (this.isa.negative.integer(x)) && (this.isa.odd.integer(x)); | ||
}, | ||
'even.negative.integer': function(x) { | ||
return (this.isa.negative.integer(x)) && (this.isa.even.integer(x)); | ||
}, | ||
'odd.posnaught.integer': function(x) { | ||
return (this.isa.posnaught.integer(x)) && (this.isa.odd.integer(x)); | ||
}, | ||
'even.posnaught.integer': function(x) { | ||
return (this.isa.posnaught.integer(x)) && (this.isa.even.integer(x)); | ||
}, | ||
'odd.negnaught.integer': function(x) { | ||
return (this.isa.negnaught.integer(x)) && (this.isa.odd.integer(x)); | ||
}, | ||
'even.negnaught.integer': function(x) { | ||
return (this.isa.negnaught.integer(x)) && (this.isa.even.integer(x)); | ||
} | ||
@@ -532,2 +731,3 @@ }; | ||
//..................................................................................................... | ||
/* deal with sum types (tagged unions, variants) */ | ||
case role === 'qualifier': | ||
@@ -546,2 +746,3 @@ return nameit(method_name, function(x) { | ||
//..................................................................................................... | ||
/* deal with product types (records) */ | ||
case role === 'basetype' || role === 'usertype': | ||
@@ -837,4 +1038,5 @@ return nameit(method_name, function(x) { | ||
(() => { | ||
var create, isa, type_of, validate; | ||
var create, isa, testing, type_of, validate; | ||
({isa, validate, create, type_of} = types); | ||
testing = {_isa}; | ||
return module.exports = { | ||
@@ -851,2 +1053,3 @@ Intertype, | ||
walk_prefixes, | ||
testing, | ||
__type_of | ||
@@ -853,0 +1056,0 @@ }; |
{ | ||
"name": "intertype", | ||
"version": "201.1.0", | ||
"version": "202.0.0", | ||
"description": "A JavaScript typechecker", | ||
@@ -5,0 +5,0 @@ "main": "lib/main.js", |
108
README.md
@@ -17,2 +17,3 @@ | ||
- [`evaluate` methods](#evaluate-methods) | ||
- [Sum Types (Variants) and Product Types (Records)](#sum-types-variants-and-product-types-records) | ||
- [Declaration Values (Test Method, Type Name, Object)](#declaration-values-test-method-type-name-object) | ||
@@ -136,2 +137,45 @@ - [Namespaces and Object Fields](#namespaces-and-object-fields) | ||
## Sum Types (Variants) and Product Types (Records) | ||
* Variants can be defined with or without a *qualifier*, a syntactic element that precedes a type name | ||
* Variants *without* qualifiers ('unqualified variants') can be defined by using a logical disjunction | ||
(`or`, `||`) in the test method. For example, one could declare a type `boolordeep` like this: | ||
```coffee | ||
declarations: | ||
boolordeep: ( x ) -> ( @isa.boolean x ) or ( x is 'deep' ) | ||
``` | ||
which will be satisfied by any one of the three values `true`, `false`, and (the string) `'deep'`, to the | ||
exclusion of any other values. | ||
* '*Qualified* variants' do use an explicit qualifier that is meant to be used in conjunction with other | ||
types, for example: | ||
```coffee | ||
declarations: | ||
nonempty: { role: 'qualifier', } | ||
'nonempty.list': ( x ) -> ( @isa.list x ) and ( x.length isnt 0 ) | ||
'nonempty.set': ( x ) -> ( @isa.set x ) and ( x.size isnt 0 ) | ||
``` | ||
* Having declared the above types—the qualifier `nonempty` with its two branch types `nonempty.list` and | ||
`nonempty.set`—we can now test for any of: | ||
* `isa.nonempty.list x`: whether `x` is a `list` with non-zero `x.length` | ||
* `isa.nonempty.set x`: whether `x` is a `set` with non-zero `x.size` | ||
* `isa.nonempty x`: whether `x` satisifes either `isa.nonempty.list x` or `isa.nonempty.set x` | ||
* In other words, the qualifier is what becomes the variant—`isa.nonempty.list` and `isa.nonempty.set` are | ||
ordinary tpes (though conceivably one could declare them as unqualified variants, as shown above) | ||
* 'Product types' or 'records', on the other hand, are types that mandate the presence not of alternatives, | ||
but of named fields each of which must satisfy its own test method for the record to be valid | ||
* Clarification of terminology: | ||
* an explicit *qualifier* is what goes in front of a *qualified typename*; | ||
* a *variant* is a type that has several equivalent alternatives to choose from; | ||
* an *implicit variant* is a type that has several equivalent alternatives to choose from that are however | ||
not formally declared to the InterType API but inside a test method that contains disjunctions (`foo: ( | ||
x ) -> ( @isa.a x ) or ( @isa.b x )`) | ||
* an *explicit variant* comes about by using a (chain of) qualifier(s) as a typename; so when we have | ||
declared `empty` as a `qualifier` and set up tests for both `empty.text` and `empty.list`, then the | ||
explicit variant `empty` can be tested for with `isa.empty x`, which will return `true` for exactly the | ||
values `''` and `[]`. | ||
### Declaration Values (Test Method, Type Name, Object) | ||
@@ -247,3 +291,2 @@ | ||
* **[–]** implement `fields` | ||
* **[–]** in `_compile_declaration_object()`, add validation for return value | ||
@@ -258,3 +301,2 @@ * **[–]** implement using `optional` in a declarations, as in `{ foo: 'optional.text', }` | ||
* **[–]** test `create()` method for the recursive case | ||
* **[–]** test that incorrect templates are rejected | ||
* **[–]** acquire deep-freezing method | ||
@@ -270,2 +312,5 @@ * **[–]** use deep-freezing for declaration | ||
* **[–]** there's `template` but it's a function | ||
* **[–]** `evaluate.cardinal Infinity` returns `{ cardinal: false }`; `evaluate.posnaught.integer Infinity` | ||
returns `{ 'posnaught.integer': false }`; in both cases, more details would be elucidating (`Infinity` | ||
satisfies `posnaught` but not `integer`) | ||
* **[–]** consider to enrich result of `evaluate` methods with length-limited `rpr()` and type of values | ||
@@ -277,27 +322,24 @@ encountered | ||
implemented 'qualifiers' | ||
* **[–]** consider to implement `nonempty.text()`, `nonempty.list()`, `empty.text()`, `empty.list()`; here, | ||
`empty` and `nonempty` are not types names of an object with fields, and the names after the dots are not | ||
field names; also, `isa.nonempty x` does not necessarily have to make sense so either it shouldn't be a | ||
function or, if it is one, it should throw an error when called, something that has always been ruled out | ||
so far | ||
* **[–]** `nonempty` etc. could be autogenerated: go through each enumerable property `T` of | ||
`isa.nonempty` and add a test `( @isa[ T ] x ) and ( @isa.nonempty[ T ] x` | ||
* **[–]** need a term for the 'sub-methods' that get attached as props to the 'target methods'(??), e.g. | ||
after `isa.quantity()` has been set 'sub-methods' `isa.quantity.q()`, `isa.quantity.u()` will be set as | ||
properties of their 'target' `isa.quantity`; the current terminology is unfortunate and obfuscates more | ||
than it elicits | ||
* **[–]** clarify difference between basetypes and meta/quasitype `optional`, provide a type for the union | ||
of both | ||
* **[–]** Type roles: | ||
* `basetype` | ||
* `optional` | ||
* `usertype` | ||
* `qualifier` | ||
* `negation` (?) | ||
* **[–]** implement 'qualifiers' (as in `nonempty.text`) that look a lot like object fields but have a | ||
different `isa` method implementation | ||
* **[–]** qualifiers should be distinguished from `optional` which is and remains a prefix that can before | ||
any other legal (known, declared) (fully quylified) type name, so `isa.optional.nonempty.text x` may be | ||
legal but `isa.nonempty.optional.text x` won't | ||
* **[–]** `nonempty` etc. could be autogenerated: go through each enumerable property `T` of | ||
`isa.nonempty` and add a test `( @isa[ T ] x ) and ( @isa.nonempty[ T ] x` | ||
* **[–]** need a term for the 'sub-methods' that get attached as props to the 'target methods'(??), e.g. | ||
after `isa.quantity()` has been set 'sub-methods' `isa.quantity.q()`, `isa.quantity.u()` will be set as | ||
properties of their 'target' `isa.quantity`; the current terminology is unfortunate and obfuscates more | ||
than it elicits | ||
* **[–]** clarify difference between basetypes and meta/quasitype `optional`, provide a type for the union | ||
of both | ||
* **[–]** Type roles: | ||
* `basetype` | ||
* `optional` | ||
* `usertype` | ||
* `qualifier` | ||
* `negation` (?) | ||
* **[–]** in addition to setting `role`, allow users to set `{ test: '@qualifier' }` which then can be | ||
shortened to `empty: '@qualifier'` in dotted field enumerations (maybe really the preferred way to | ||
differentiate qualifier trees from nested records) | ||
* **[–]** implement method to supply all types that are present in `_isa` but missing from | ||
`default_declarations` | ||
* **[–]** consider to relegate private module exports to sub-key `testing` or similar | ||
## Is Done | ||
@@ -401,2 +443,16 @@ | ||
* **[+]** test that a declaration with fields defaults to `{ test: 'object', }` | ||
* **[+]** implement `fields` | ||
* **[+]** test that incorrect templates are rejected | ||
* **[+]** consider to implement `nonempty.text()`, `nonempty.list()`, `empty.text()`, `empty.list()`; here, | ||
`empty` and `nonempty` are not types names of an object with fields, and the names after the dots are not | ||
field names; also, `isa.nonempty x` does not necessarily have to make sense so either it shouldn't be a | ||
* **[+]** implement 'qualifiers' (as in `nonempty.text`) that look a lot like object fields but have a | ||
different `isa` method implementation | ||
* **[+]** qualifiers should be distinguished from `optional` which is and remains a prefix that can before | ||
any other legal (known, declared) (fully quylified) type name, so `isa.optional.nonempty.text x` may be | ||
legal but `isa.nonempty.optional.text x` won't | ||
function or, if it is one, it should throw an error when called, something that has always been ruled out | ||
so far | ||
* **[+]** use prototypes of test methods `throws()` &c for new version of `guy-test` | ||
* **[+]** use prototype of set equality for `equals()` implementation in `webguy` | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
192414
1174
452