intervoke v0.6.0
InterVoke
Table of Contents generated with DocToc
InterVoke
Purpose
JavaScript API helper that allows to call methods on objects through properties.
Motivation
I have been using this to build a 'snappy' API for runtime typechecks in JavaScript. Included in this
package is a class Word_prompter
which splits property names into words (by looking for spaces and
underscores), then calls a method producer to retrieve a suitable method for the given phrase, caching
results on the way so only first-time accesses need to invoke phrase parsing.
Now, phrase parsing (which is outside of the scope of this package and will depend on one's use case) can allow the type checker to detect that eg.
- a property
empty_list
on a type checkerisa
should determine whether a given argumentx
(as inisa.empty_list x
) satisfies bothisa.list x
andx.length is 0
, or that isa.integer_or_numerical_text x
is satisfied whenisa.integer x
istrue
or, alternatively, bothisa.text x
and/[0-9]+/.test x
hold.
This can result in very readable APIs, for which InterVoke
provides the foundations, namely,
- providing proxied access to object properties, taking care of all the edge cases
- providing classes whose instantiations are callable functions (not very difficult but a little tricky)
- doing property name normalization
- caching
Name
The name 'InterVoke' is both in line with my /^inter[a-z]+$/
line of packages and, on the other hand,
symbolizes quite nicely that this library is all about intercepting method invocations—turning 'invocations'
into 'intervocations', in a manner of speaking. Thanks ChatGPT!
Notes
- all methods and other instance properties whose names starts with a double underscore
__
are not proxied and returned directly; this allows users to implement functionality in derived classes while keeping the system's namespace separated from the instances' proxied accessors.
Derived Classes
Word Prompter
- In itself probably not a very useful class.
- It serves a base class for
Phrase_prompter
. - All one can do is declaring functions either via the
declare
class property or the (private)__declare()
method. - set class property
declare
to an object with methods that will be__declare()
d on initialisation - Any access to non-declared properties will cause an error.
Phrase Prompter
- Intended for new version of InterType; the following notes betray that and are written with the use case of building a runtime type checking library in mind.
Phrase_prompter
is a derivative ofWord_prompter
.- Like
Word_prompter
,Phrase_prompter
too will split property names into words by splitting on spaces and underscores. UnlikeWord_prompter
,Phrase_prompter
assumes a certain grammar for its accessors, here termed 'sentences' and 'phrases'. - Words appearing in accessors are recognized as either
- nouns like
integer
,list
,text
; - adjectives like
empty
,positive
; - or connectives
of
andor
.
- nouns like
- The connectives
of
andor
are built-in, no nouns or adjectives are pre-defined.- Connectives, notably
or
, may not be used as words in new names (so it's OK todeclare.empty_list
ordeclare.integer_text
regardless whether any ofempty
,list
,integer
,text
are already known or not, butdeclare.integer_or_text
is forbidden because it containsor
. Same restriction on use ofof
may be lifted in the future).
- Connectives, notably
- Nouns can be added by
declare()
ing them, as ind.declare.mynoun ...
or, equivalently,d.declare 'mynoun', ...
. - Adjectives are either generic or must be declared on the nouns that they can modify (because e.g.
empty
makes only sense when the noun describes something that can contain values, andnegative
makes only sense for numbers). - Nouns are turned into functions and made properties (of the same name) of their base object (here shown as
isa
for the sake of exposition); so typeinteger
is accessible as mthodisa.integer()
. - Adjectives are likewise turned into functions, but are made properties of the nouns they are declared on,
so if adjective
positive
is declared for typeinteger
, then its correlated function may be accessed asisa.integer.positive()
(as well as byisa.positive_integer()
). isa.empty_list x
:isa.list.empty x
, which implictly starts withisa.list x
isa.nonempty_list_of_positive_integer x
:isa.list x
, thenisa.list.nonempty x
, then, for each elemente
,isa.integer_positive e
, must hold, that is,( isa.integer e ) and ( isa.integer.positive e )
isa.nonempty_list_or_nonempty_text x
: must satisfy( ( isa.list x ) and ( isa.list.nonempty x ) ) or ( ( isa.text x ) and ( isa.text.nonempty x ) )
or
has lowest precedence soisa.nonempty_empty_list_or_text x
is satisfied even whenx
is the empty stringisa.hinky_dinky_dong x
: holds when bothisa.dong.hinky x
andisa.dong._dinky x
hold. The call toisa.dong.hinky x
implicitly callsisa.dong x
, the call toisa.dong._dinky x
skips that test.
Generic and Specific Adjectives
- Generic adjectives can be used with all types and can not be overridden by a type declaration. They always resolve to the same test for values of all types.
- There's currently a single generic adjective,
optional
, which returnstrue
if its argument isnull
orundefined
; if it does returntrue
, evaluation is shortcut at that point (soisa.optional_list null
istrue
although the argument is not a list). In effect,isa.optional_T x
behaves likeisa.null_or_undefined_or_T x
and, equivalently,isa.nothing_or_T x
. - All other adjectives are specific to their types and can not be used with a type for which they are not
declared; thus,
isa.positive_list x
andisa.empty_integer x
produce errors at property access time.
Sentence Structure Diagram
┌──────┐ ┌──┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──┐
│ adj. │ │n.│ │ adj. │ │ noun │ │ adj. │ │n.│
│ │ │ │ │ │ │ │ │ │ │ │
nonempty_list_of_positive_integers_or_nonempty_text
│(top) │ │ (elements) │ │(top) │
│complement │ │ complement │ │complement │
└───────────┘ └───────────────┘ │ │
│ │ adjunct │ │ │
│ └────────────────────┘ │ │
│ disjunct │ │ disjunct │
└────────────────────────────────┘ └───────────┘
│ sentence │
└─────────────────────────────────────────────────┘
or
list text
nonempty nonempty
of
integers
positive
- A sentence consists of one ore more alternative (noun) phrases.
- Multiple alternatives can be linked with the connective
or
. or
has least precedence so whatever has been said in the phrase before it has no effect on the phrase that comes after it.- A single noun always comes last in a phrase.
- A noun may be preceded by one or more adjectives.
- A list of adjectives may start with the special global adjective
optional
, which indicates that a value ofnull
orundefined
will satisfy the condition. Sinceoptional
vlaues are essentially 'typeless' in the sense that anull
value could stand in for any kind of missing value (much like an empty list satisfies bothempty_list_of_strings
andempty_list_of_numbers
), anoptional
present in any alternative makes the entire compound optional, so there's no difference betweenoptional_text_or_float
,text_or_optional_float
, andoptional_text_or_optional_float
. - Sentences that contain one or more undeclared words cause an error.
- Adjectives that precede a given noun in a phrase must be declared for that noun.
- A phrase with a noun that is declared to be a collection (a 'collection phrase') may be followed by the
connective
of
and a phrase that describes its elements (an 'element phrase'). - A phrase that follows an
of
phrase to which it is connected with anor
is understood to describe the 'outer' value, not the element value; this is becauseor
has lowest priority. Therefore,isa.nonempty_list_of_integers_or_text x
holds whenx
is either a list of whole numbers or, alternatively,x
is a text, possibly the empty string. - To describe alternatives for elements, declare a custom type:
declare.frob 'integer_or_text'; isa.list_of_frobs x
will hold when all (if any) elements in listx
are either integers or texts; this is then equivalent to the longer( isa.list_of_integers x ) or ( isa.list_of_texts x )
.
AST Data Structure
- An AST is an object with a two properties,
alternatives
andoptional
. alternatives
is a non-empty list ofor
clauses ('alternatives'); in case noor
was used, the list will hold a single clause.- Each clause has
- a mandatory
noun
(a string which names the type); - an optional list of
adjectives
(missing where not needed), and - an optional
elements
sub-clause (initiated by theof
connective) which in itself is a clause (and may have its ownelements
sub-clause). Likeadjectives
,elements
will be absent where not needed.
- a mandatory
optional
istrue
ifalternatives
has more than one element, andfalse
otherwise.
Note we do not currently support alternatives in
elements
sub-clauses; if that should be implemented, then theelements
property would become a list of alternatives instead of a single clause.
element_clause = {
noun: 'integer',
adjectives: [ 'positive0', ], }
clause = {
noun: 'list',
adjectives: [ 'nonempty', ],
elements: element_clause, }
alternatives = [ clause, ]
ast = { alternatives, optional: true, }
Glossary
- Prompter: a
class Pr extends Prompter
that instantiatespr = new Pr()
as a function which allows to be accessed in two ways: classicalpr 'acc', p, q, r...
or compressedpr.acc p, q, r...
- Accessor: the key used as first argument to access an attributor as in
pr.acc()
, sometimes symbolized asacc
- Phrase: list of 'words'/keys resulting from splitting the accessor by whitespace and underscores. This
allows to build complex accessors like
isa.text_or_integer 42
(phrase:[ 'text', 'or', 'integer', ]
) --> Details: arguments used in a attributor after the accessor. Ex.: In
pr.foo_bar 3, 4, 5
,foo_bar
is the accessor key,[ 'foo', 'bar', ]
is the accessor phrase, and3, 4, 5
are the accessor details.Adjunct: the part(s) of a declaration that come after the noun and the introductory
of
, as in e.g.list_of_integers
. Whenisa.list_of_integers x
is called, it will hold whenx
is indeed a list; the adjunct,of_integers
, will hold if each element of that list, if any, is an integer."An adjunct is an optional, or structurally dispensable, part of a sentence, clause, or phrase that, if removed or discarded, will not structurally affect the remainder of the sentence.It is a modifying form, word, or phrase that depends on another form, word, or phrase ... The adjuncts of a predicate ... provide auxiliary information about the core ... meaning"—Wikipedia
Connective:
or
(and, if it gets implemented,and
).Conjunct: "In grammar, a conjunction (abbreviated conj or cnj) is a part of speech that connects words, phrases, or clauses that are called the conjuncts of the conjunctions."—Wikipedia
Disjunct: "The conjuncts of the conjunction ‘or’ are called disjuncts¹. They are words or phrases that are connected by ‘or’ and express a choice or an alternative between them. For example, in the sentence “You can have tea or coffee”, tea and coffee are disjuncts of the conjunction ‘or’. // 1. en.wikipedia.org"–Bing AI Chat
Complement: The adjectives and nouns of a declaration: "The part after ‘is’ in the sentence ‘x is a list of positive integers’ is called a subject complement. A subject complement is a word or phrase that follows a linking verb (such as ‘is’) and describes or identifies the subject. For example, in the sentence “She is a teacher”, teacher is a subject complement that identifies she. In your sentence, ‘a list of positive integers’ is a subject complement that describes x."—Bing AI Chat
Attribution
- project name as suggested by ChatGPT
- project logo as suggested by Nolibox
- plural guessing algorithm copied from Sindre Sorhus'
plur
To Do
- – docs
- – implement matching property names 'longest first' to allow for overrides that are
implementationally simpler than literal translations (eg. in
isa.empty_list x
, it will be simpler to check first forArray.isArray x
, then forx.length is 0
instead of dealing with the different ways that emptiness can be detected in JS (x.length
,x.size
, ...)) - – clarify terms clause, phrase, adjunct, sentence and so on; also, all of these terms may be
applied to strings of underscore-separated words as well as lists of words (so maybe always use
phrase_txt
vsphrase_lst
&c). - – implement phrase highlighting to be used in error messages \&c; use four colors to distinguish
(1) (green💚) tested and OK, (2) (red🍅) tested and not OK, (1) (yellow🍋) not tested, (blue🔵)
for structural parts. Example
isa.nonempty_list_of_positive_integers [ -4, ]
should give💚nonempty💚_💚list💚_🔵of🔵_🍅positive🍅_💚integers💚
with reverse-colored stretches;isa.optional_list null
should give💚optional💚_🍋list🍋
.
Is Done
- + name generated functions using the NCC
- + find a good name
- + In the above example where
declare.frob 'integer_or_text'
is used to declare a choice type to be used likeisa.list_of_frobs x
. According to the rules so far, it would indeed be possible todeclare.integer_or_text 'integer_or_text'
, where the tricky part is that this declaration will be the last time thatinteger_or_text
is parsed; subsequent uses will only cause a lookup—which means that usingisa.nonempty_list_of_integer_or_text x
will mean something different prior to the declaration than it does following the declaration. Solutions:- 1) disallow re-using existing names as parts of new names
- 2) less strictly, mandate use of at least one novel word in new names (a word that is not in itself
already a known name) (so could use
either_integer_or_text
orchoose_integer_text
) - 3) disallow using
or
(or other connectives, soof
) in new names, treating them like PL keywords - solution 3) seems reasonable; adjectives + nouns (
empty_list
) or chains of nouns (integer_text
) are not the problem,or
is the problem
- + can we use instance as the cache instead of using a seperate one? Then one could check for
target
having the property and just return it when found. Maybe use a map or set to simplify lookups. - + collect all declarations in the prototype chain
- + do not return instances-as-functions as it is a useless complication
- + do not use phrase normalization as it is expendible. If anything, one could later support a method
to translate 'natural texts' like
'validate that x is an integer or a text of digits'
into very similar API calls likevalidate.integer_or_text_of_digits x