lang.mjs provides tools essential for all other code. Stuff that should be built into the language.
- Type checks and assertions.
- Terse.
- Performant.
- Minifiable.
- Descriptive.
- Sensible type conversions.
Port and rework of https://github.com/mitranim/fpx.
- #Usage
- #API
- #
function isNil
- #
function isSome
- #
function isBool
- #
function isNum
- #
function isFin
- #
function isFinNeg
- #
function isFinPos
- #
function isInt
- #
function isNat
- #
function isIntNeg
- #
function isIntPos
- #
function isNaN
- #
function isInf
- #
function isBigInt
- #
function isStr
- #
function isSym
- #
function isKey
- #
function isStructKey
- #
function isPk
- #
function isJunk
- #
function isComp
- #
function isPrim
- #
function isFun
- #
function isFunSync
- #
function isFunGen
- #
function isFunAsync
- #
function isFunAsyncGen
- #
function isObj
- #
function isDict
- #
function isStruct
- #
function isArr
- #
function isTrueArr
- #
function isReg
- #
function isDate
- #
function isValidDate
- #
function isInvalidDate
- #
function isSet
- #
function isMap
- #
function isPromise
- #
function isIter
- #
function isIterAsync
- #
function isIterator
- #
function isIteratorAsync
- #
function isGen
- #
function isCls
- #
function isList
- #
function isSeq
- #
function isVac
- #
function isScalar
- #
function isEmpty
- #
function isInst
- #
function req
- #
function opt
- #
function reqInst
- #
function optInst
- #
function only
- #
function onlyInst
- #
function render
- #
function renderLax
- #
function show
- #
function toTrueArr
- #
function is
- #
function truthy
- #
function falsy
- #
function nop
- #
function id
- #
function val
- #
function panic
- #
function vac
- #
function bind
- #
function not
- #
function hasOwn
- #
function hasOwnEnum
- #
function hasInherited
- #
function hasMeth
- #
function setProto
- #
function Emp
- #
function add
- #
function sub
- #
function mul
- #
function div
- #
function rem
- #
function lt
- #
function gt
- #
function lte
- #
function gte
- #
function neg
- #
function inc
- #
function dec
- #Undocumented
- #
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
Links: source; test/example.
True for null
and undefined
. Same as value == null
. Incidentally, these are the only values that produce an exception when attempting to read a property: null.someProperty
.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
// Definition
function isNil(value) {return value == null}
l.isNil(null)
// true
l.isNil(undefined)
// true
l.isNil(false)
// false
Links: source; test/example.
Inverse of #isNil
. False for null
and undefined
, true for other values.
Links: source; test/example.
Same as typeof val === 'boolean'
.
Links: source; test/example.
Same as typeof val === 'number'
. True if the value is a primitive number, including NaN
and ±Infinity
. In most cases you should use #isFin
instead.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
l.isNum(1)
// true
l.isNum('1')
// false
l.isNum(NaN)
// true <-- WTF
Links: source; test/example.
Same as ES2015's Number.isFinite
. True if val
is a primitive number and is not NaN
or ±Infinity
. In most cases you should prefer isFin
over isNum
.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
l.isFin(1)
// true
l.isFin('1')
// false
l.isFin(NaN)
// false
Links: source; test/example.
True if the value is finite (via #isFin
) and < 0.
Links: source; test/example.
True if the value is finite (via #isFin
) and > 0.
Links: source; test/example.
True if the value is an integer: finite via #isFin
, without a fractional part.
Links: source; test/example.
True if the value is a natural number: integer >= 0. Also see #isIntPos
.
Links: source; test/example.
True if the value is integer < 0. Also see #isFinNeg
.
Links: source; test/example.
True if the value is integer > 0. Also see #isNat
, #isFinPos
.
Links: source; test/example.
Same as ES2015's Number.isNaN
. True if the value is actually NaN
. Doesn't coerce non-numbers to numbers, unlike global isNaN
.
Links: source; test/example.
True if the value is -Infinity
or Infinity
.
Links: source; test/example.
True if the value is a primitive BigInt
. False for all other inputs, including BigInt
object wrappers.
Links: source; test/example.
Same as typeof val === 'string'
. True if the value is a primitive string.
Links: source; test/example.
Same as typeof val === 'symbol'
. True if the value is a primitive symbol.
Links: source; test/example.
True if the value is primitive and usable as a map key. True for all primitives excluding garbage values via #isJunk
.
Links: source; test/example.
True if the value qualifies as an object property key: either a string or a symbol.
Links: source; test/example.
True for objects that implement method .pk
which must return a valid #primary. This interface is used internally by Coll
.
Links: source; test/example.
True for garbage values: #nil, #NaN, #±Infinity.
Links: source; test/example.
True if the value is "composite" / "compound" / "complex". Opposite of #isPrim
. Definition:
function isComp(val) {return isObj(val) || isFun(val)}
Links: source; test/example.
True if the value is a JS primitive: not an object, not a function. Opposite of #isComp
.
Links: source; test/example.
Same as typeof val === 'function'
. True if the value is any function, regardless of its type (arrow, async, generator, etc.).
Links: source; test/example.
True if the input is a normal sync function. False for generator functions or async functions.
Links: source; test/example.
True if the input is a sync generator function. False for normal sync functions and async functions.
Links: source; test/example.
True if the input is an async non-generator function. False for sync functions, generator functions, or async generator functions.
Links: source; test/example.
True if the input is an async generator function. False for sync functions and async non-generator functions.
Links: source; test/example.
Same as typeof val === 'object' && val !== null
. True for any JS object: plain dict, array, various other classes. Doesn't include functions, even though JS functions are extensible objects.
- Compare #
isComp
which returns true for objects and functions. - For plain objects used as dictionaries, see #
isDict
. - For fancy non-list objects, see #
isStruct
.
Links: source; test/example.
True for a "plain object" created via {...}
or Object.create(null)
. False for any other input, including instances of any class other than Object
.
See #isStruct
for a more general definition of a non-iterable object.
Links: source; test/example.
True if the value is a non-iterable object. Excludes both #sync_iterables and #async_iterables. Note that #dicts are automatically structs, but not all structs are dicts.
Links: source; test/example.
Alias for Array.isArray
. Used internally for all array checks.
True if the value is an instance of Array
or its subclass. False for all other values, including non-array objects whose prototype is an array.
Links: source; test/example.
Similar to Array.isArray
and #isArr
, but returns true only for instances of the exact Array
class, false for instances of subclasses.
At the time of writing, subclasses of Array
suffer horrible performance penalties in V8, and possibly in other engines. Using them can also cause deoptimization of code that would otherwise run much faster. We sometimes prioritize or even enforce "true" arrays for consistent performance.
Links: source; test/example.
True if the value is an instance of RegExp
or its subclass.
Links: source; test/example.
True of value is an instance of Date
. Most of the time you should prefer #isValidDate
.
Links: source; test/example.
True of value is an instance of Date
and its timestamp is #finite rather than NaN
or Infinity
.
Links: source; test/example.
True of value is an instance of Date
representing an invalid date whose timestamp is NaN
.
Links: source; test/example.
True if the value is an instance of Set
or its subclass.
Links: source; test/example.
True if the value is an instance of Map
or its subclass.
Links: source; test/example.
True if the value satisfies the ES2015 promise interface.
Links: source; test/example.
True if the value satisfies the ES2015 sync iterable interface. For iterator rather than iterable, use #isIterator
.
Links: source; test/example.
True if the value satisfies the ES2015 async iterable interface. For iterator rather than iterable, use #isIteratorAsync
.
Links: source; test/example.
True if the value satisfies the ES2015 sync iterator interface. For iterable rather than iterator, use #isIter
.
Links: source; test/example.
True if the value satisfies the ES2015 async iterator interface. For iterable rather than iterator, use #isIterAsync
.
Links: source; test/example.
True if the value is a #sync_iterator created by calling a generator function.
Links: source; test/example.
True if the input is a function with a prototype, likely to be a class. False for arrow functions such as () => {}
, which don't have a prototype.
Links: source; test/example.
True for any array-like such as: []
, arguments
, TypedArray
, NodeList
, etc. Used internally for most list checks. Note that primitive strings are not considered lists.
Links: source; test/example.
True for any of:
Many functions in iter.mjs
support arbitrary data structures compatible with values
, but some functions such as arr
allow only sequences, for sanity checking.
Links: source; test/example.
Short for "is vacuous" or "is vacated". Could also be called "is falsy deep". True if the input is #falsy
or a #list where all values are vacuous, recursively. Does not iterate non-lists. Also see complementary function #vac
.
Links: source; test/example.
True for a value that could be considered a single scalar, rather than a collection / data structure. Currently this is equivalent to the concept of an intentionally stringable value. In the future, we may consider renaming this function or splitting the concepts.
The following are included:
- Any #primitive except for those which are excluded below.
- Any #object with a special
.toString
method, distinct from bothObject.prototype.toString
andArray.prototype.toString
. Examples include #dates,URL
, and many more.
The following are excluded:
To include nil, use #isScalarOpt
.
Links: source; test/example.
True if the input is an empty collection such as list, set, map, or a primitive such as null
. False for any other non-primitive. Treating primitives as "empty" is consistent with various functions in iter.mjs
that operate on collections.
Links: source; test/example.
Signature: (val, Cls) => bool
.
Same as instanceof
but does not implicitly convert the operand to an object. True only if the operand is already an instance of the given class. Also unlike instanceof
, this is always false for functions, avoiding the insanity of fun instanceof Function
being true.
Links: source; test/example.
Signature: (val, test) => val
where test: val => bool
.
Short for "require". Minification-friendly assertion. If !test(val)
, throws an informative TypeError
. Otherwise, returns val
as-is.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
l.req({one: `two`}, l.isObj)
// {one: `two`}
l.req('str', l.isFun)
// Uncaught TypeError: expected variant of isFun, got "str"
Links: source; test/example.
Short for "optional". If val
is #non_nil, uses #req
to validate it. Returns val
as-is.
Links: source; test/example.
Signature: (val, Cls) => val
.
Short for "require instance". Asserts that val
is an instance of the given class. Returns val
as-is.
Links: source; test/example.
Short for "optional instance". If val
is #non_nil, uses #reqInst
to validate it. Returns val
as-is.
Links: source; test/example.
Signature: (val, test) => val
where test: val => bool
.
Type filtering utility. If val
satisfies the given test function, returns val
as-is. Otherwise returns undefined
.
Links: source; test/example.
Signature: (val, Cls) => val?
.
Type filtering utility. If val
is an instance of Cls
, returns val
as-is. Otherwise returns undefined
.
Links: source; test/example.
Renders a value for user display. Counterpart to #show
, which renders a value for debug purposes. Intended only for #scalar values. Rules:
- #Date with default
.toString
→ use.toISOString
. This overrides the insane JS default stringification of dates, defaulting to the reversible machine-decodable representation used for JSON. - Other #non-nil #scalars → default JS stringification.
- All other inputs including #nil →
TypeError
exception.
Links: source; test/example.
Renders a value for user display. Intended only for #scalar values. Unlike #render
, this allows nil. Rules:
Links: source; test/example.
Renders a value for debug purposes. Counterpart to #render
, which renders a value for user display. Convenient for interpolating things into error messages. Used internally in assertion functions such as #req
. Approximate rules:
- String → use
JSON.stringify
. - Function →
[function ${val.name || val}]
.- For named functions, this shorter representation is usually preferable to printing the entire source code.
- Object →
- Plain
{}
or[]
→ useJSON.stringify
. - Otherwise
[object <name>]
, prioritizing constructor name overSymbol.toStringTag
.- Exact opposite of default behavior for
Object.prototype.toString
.
- Exact opposite of default behavior for
- Plain
- Otherwise → default JS stringification.
Links: source; test/example.
Idempotent conversion to a #true. Allowed inputs:
- #Nil → return
[]
. - #True → return as-is.
- #Iterable → convert to
Array
. - Otherwise →
TypeError
exception.
Links: source; test/example.
Identity test: same as ===
, but considers NaN
equal to NaN
. Equivalent to SameValueZero as defined by the language spec. Used internally for all identity tests.
Note that Object.is
implements SameValue, which treats -0
and +0
as distinct values. This is typically undesirable. As a result, you should prefer l.is
over ===
or Object.is
.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
l.is(1, '1')
// false
l.is(NaN, NaN)
// true
Links: source; test/example.
Same as !!
or Boolean
. Sometimes useful with higher-order functions.
Links: source; test/example.
Same as !
. Sometimes useful with higher-order functions.
Links: source; test/example.
Empty function. Functional equivalent of ;
or undefined
. Sometimes useful with higher-order functions.
Links: source; test/example.
Identity function: returns its first argument unchanged. Sometimes useful with higher-order functions.
Links: source; test/example.
Takes a value and creates a function that always returns that value. Sometimes useful with higher order functions.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
const constant = l.val(1)
constant()
// 1
constant(`this input is ignored`)
// 1
Links: source; test/example.
Same as throw
but an expression rather than a statement. Also sometimes useful with higher-order functions.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
const x = someTest ? someValue : l.panic(Error(`unreachable`))
Links: source; test/example.
Complements #isVac
. Returns undefined
if the input is vacuous, otherwise returns the input as-is.
Links: source; test/example.
Like Function.prototype.bind
, but instead of taking this
as an argument, takes it contextually. By default this
is undefined
. To set it, use l.bind.call
.
Returns a new function that represents partial application of the given function, a common tool in functional programming. When called, it joins arguments from both calls and invokes the original function. Think of it like splitting a function call in two, or more. Performance is inferior to closures; avoid in hotspots.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
const inc = l.bind(l.add, 1)
inc(2)
// 3
Note: we don't provide facilities for currying. Experience has shown it to be extremely error prone. Currying, as seen in purely functional languages such as Haskell, tends to care about the amount of arguments. Calling a curried function may either create a new function, or call the underlying function (possibly side-effectful). This approach works reasonably well in statically typed languages, but not in JS where all functions are variadic and it's conventional to sometimes pass extra utility arguments "just in case", which the callee may or may not care about. bind
is different because the created function will always call the original function, regardless of how many arguments were passed.
Links: source; test/example.
Returns a new function that negates the result of the given function, like a delayed !
.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
function eq(a, b) {return a === b}
const different = l.not(eq)
different(10, 20)
// !eq(10, 20) = true
// equivalent:
function different(a, b) {return !eq(a, b)}
Links: source; test/example.
Same as Object.prototype.hasOwnProperty
but shorter and safe to call on primitives. Always false for primitives.
Links: source; test/example.
Same as Object.prototype.propertyIsEnumerable
but shorter and safe to call on primitives. Always false for primitives.
Links: source; test/example.
Returns true
if the target is #non-primitive and has the given property on its prototype. As a consequence, this returns false
if the target is a primitive, or has the given property as an "own" property, either enumerable or not.
import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
l.hasInherited([10, 20, 30], `length`)
// false
l.hasInherited([10, 20, 30], `1`)
// false
l.hasInherited([10, 20, 30], `toString`)
// true
Links: source; test/example.
True if the the given value has the given named method. Safe to call on primitives such as null
. Always false for primitives.
Links: source; test/example.
Workaround for bugs related to subclassing.
In some Safari versions, when instantiating a subclass of various recent
built-in classes such as Request
/Response
/URL
, the engine incorrectly
uses the prototype of the superclass rather than the subclass. Occurs in Safari
12-14+, both desktop and mobile. This seems to fix that. Example:
class Abort extends AbortController {
constructor() {
super()
l.setProto(this, new.target)
}
}
The following version is shorter but more confusing if you don't know full semantics of JS classes:
class Abort extends AbortController {
constructor() {l.setProto(super(), new.target)}
}
Links: source; test/example.
Short for "empty". Hybrid function / superclass for empty objects.
In function mode, Emp()
returns Object.create(null)
, with no measurable overhead. Basically a syntactic shortcut.
Calling new Emp()
also returns Object.create(null)
. This is pointless and should be avoided.
Subclassing Emp
creates a class with the cleanest possible .prototype
, which is null
-based, sharing no common ancestry with anything.
class Empty extends l.Emp {}
Object.getPrototypeOf(Empty.prototype) === null
// Instantiation and inheritance works as expected.
const val = new Empty()
val instanceof Empty
val.constructor === Empty
// `Object` stuff is not inherited.
!(val instanceof Object)
!(`toString` in val)
Links: source; test/example.
Same as +
.
Links: source; test/example.
Same as -
.
Links: source; test/example.
Same as *
.
Links: source; test/example.
Same as /
.
Links: source; test/example.
Same as %
.
Links: source; test/example.
Same as <
.
Links: source; test/example.
Same as >
.
Links: source; test/example.
Same as <=
.
Links: source; test/example.
Same as >=
.
Links: source; test/example.
Arithmetic negation. Same as unary -
.
Links: source; test/example.
Increments by 1
.
Links: source; test/example.
Decrements by 1
.
The following APIs are exported but undocumented. Check lang.mjs.
function reqNil
function optNil
function onlyNil
function reqSome
function optSome
function onlySome
function reqBool
function optBool
function onlyBool
function laxBool
function reqNum
function optNum
function onlyNum
function laxNum
function reqFin
function optFin
function onlyFin
function laxFin
function reqFinNeg
function optFinNeg
function onlyFinNeg
function reqFinPos
function optFinPos
function onlyFinPos
function reqInt
function optInt
function onlyInt
function laxInt
function reqNat
function optNat
function onlyNat
function laxNat
function reqIntNeg
function optIntNeg
function onlyIntNeg
function reqIntPos
function optIntPos
function onlyIntPos
function reqNaN
function optNaN
function onlyNaN
function reqInf
function optInf
function onlyInf
function reqBigInt
function optBigInt
function onlyBigInt
function laxBigInt
function reqStr
function optStr
function onlyStr
function laxStr
function isValidStr
function reqValidStr
function optValidStr
function onlyValidStr
function reqSym
function optSym
function onlySym
function reqKey
function optKey
function onlyKey
function reqStructKey
function optStructKey
function onlyStructKey
function reqPk
function optPk
function onlyPk
function reqJunk
function optJunk
function onlyJunk
function reqComp
function optComp
function onlyComp
function reqPrim
function optPrim
function onlyPrim
function reqFun
function optFun
function onlyFun
function reqFunSync
function optFunSync
function onlyFunSync
function reqFunGen
function optFunGen
function onlyFunGen
function reqFunAsync
function optFunAsync
function onlyFunAsync
function reqFunAsyncGen
function optFunAsyncGen
function onlyFunAsyncGen
function reqObj
function optObj
function onlyObj
function isNpo
function reqNpo
function optNpo
function onlyNpo
function laxNpo
function reqDict
function optDict
function onlyDict
function laxDict
function reqStruct
function optStruct
function onlyStruct
function laxStruct
function reqArr
function optArr
function onlyArr
function laxArr
function reqTrueArr
function optTrueArr
function onlyTrueArr
function laxTrueArr
function reqReg
function optReg
function onlyReg
function reqDate
function optDate
function onlyDate
function reqValidDate
function optValidDate
function onlyValidDate
function reqInvalidDate
function optInvalidDate
function onlyInvalidDate
function reqSet
function optSet
function onlySet
function laxSet
function reqMap
function optMap
function onlyMap
function laxMap
function reqPromise
function optPromise
function onlyPromise
function reqIter
function optIter
function onlyIter
function reqIterAsync
function optIterAsync
function onlyIterAsync
function reqIterator
function optIterator
function onlyIterator
function reqIteratorAsync
function optIteratorAsync
function onlyIteratorAsync
function reqGen
function optGen
function onlyGen
function reqCls
function optCls
function onlyCls
function isSubCls
function reqSubCls
function reqList
function optList
function onlyList
function laxList
function reqSeq
function optSeq
function onlySeq
function reqVac
function optVac
function onlyVac
function reqScalar
function optScalar
function onlyScalar
function isScalarOpt
function reqScalarOpt
function optScalarOpt
function onlyScalarOpt
function isArrble
function reqArrble
function optArrble
function onlyArrble
function isEqable
function reqEqable
function optEqable
function onlyEqable
function isClearable
function reqClearable
function optClearable
function onlyClearable
function isErr
function reqErr
function optErr
function onlyErr
function isArrOf
function reqArrOf
function optArrOf
function reqOneOf
function optOneOf
function toInst
function toInstOpt
function renderOpt
function True
function False
function eq
function errType
function msgType
function errFun
function msgFun
function throwErrFun
function errConv
function errSynt
function msgConv
function errConvInst
function msgConvInst
function errInst
function msgInst
function errIn
function msgIn
function errImpl
function msgImpl
function errTrans
function errWrap
function errCause
function convType
function convSynt
function showFunName
function get
function getOwn
function reqGet
function structKeys