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_liston a type checkerisashould determine whether a given argumentx(as inisa.empty_list x) satisfies bothisa.list xandx.length is 0, or that isa.integer_or_numerical_text xis satisfied whenisa.integer xistrueor, alternatively, bothisa.text xand/[0-9]+/.test xhold.
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
declareclass property or the (private)__declare()method. - set class property
declareto 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_prompteris a derivative ofWord_prompter.- Like
Word_prompter,Phrase_promptertoo will split property names into words by splitting on spaces and underscores. UnlikeWord_prompter,Phrase_prompterassumes 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
ofandor.
- nouns like
- The connectives
ofandorare 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_listordeclare.integer_textregardless whether any ofempty,list,integer,textare already known or not, butdeclare.integer_or_textis forbidden because it containsor. Same restriction on use ofofmay 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.
emptymakes only sense when the noun describes something that can contain values, andnegativemakes only sense for numbers). - Nouns are turned into functions and made properties (of the same name) of their base object (here shown as
isafor the sake of exposition); so typeintegeris accessible as mthodisa.integer(). - Adjectives are likewise turned into functions, but are made properties of the nouns they are declared on,
so if adjective
positiveis 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 xisa.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 ) )orhas lowest precedence soisa.nonempty_empty_list_or_text xis satisfied even whenxis the empty stringisa.hinky_dinky_dong x: holds when bothisa.dong.hinky xandisa.dong._dinky xhold. The call toisa.dong.hinky ximplicitly callsisa.dong x, the call toisa.dong._dinky xskips 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 returnstrueif its argument isnullorundefined; if it does returntrue, evaluation is shortcut at that point (soisa.optional_list nullistruealthough the argument is not a list). In effect,isa.optional_T xbehaves likeisa.null_or_undefined_or_T xand, 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 xandisa.empty_integer xproduce 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. orhas 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 ofnullorundefinedwill satisfy the condition. Sinceoptionalvlaues are essentially 'typeless' in the sense that anullvalue could stand in for any kind of missing value (much like an empty list satisfies bothempty_list_of_stringsandempty_list_of_numbers), anoptionalpresent 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
ofand a phrase that describes its elements (an 'element phrase'). - A phrase that follows an
ofphrase to which it is connected with anoris understood to describe the 'outer' value, not the element value; this is becauseorhas lowest priority. Therefore,isa.nonempty_list_of_integers_or_text xholds whenxis either a list of whole numbers or, alternatively,xis a text, possibly the empty string. - To describe alternatives for elements, declare a custom type:
declare.frob 'integer_or_text'; isa.list_of_frobs xwill hold when all (if any) elements in listxare 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,
alternativesandoptional. alternativesis a non-empty list oforclauses ('alternatives'); in case noorwas 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
elementssub-clause (initiated by theofconnective) which in itself is a clause (and may have its ownelementssub-clause). Likeadjectives,elementswill be absent where not needed.
- a mandatory
optionalistrueifalternativeshas more than one element, andfalseotherwise.
Note we do not currently support alternatives in
elementssub-clauses; if that should be implemented, then theelementsproperty 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 Prompterthat 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_baris the accessor key,[ 'foo', 'bar', ]is the accessor phrase, and3, 4, 5are 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 xis called, it will hold whenxis 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 0instead 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_txtvsphrase_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 nullshould 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_textis parsed; subsequent uses will only cause a lookup—which means that usingisa.nonempty_list_of_integer_or_text xwill 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_textorchoose_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,oris the problem
- + can we use instance as the cache instead of using a seperate one? Then one could check for
targethaving 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