npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@essence-lang/standard-library

v0.2.0

Published

The Essence standard library, written in Essence.

Readme

The Standard Library

Essence's standard library, written in Essence.

Everything a Program can reach before its first line is declared here: the core Protocols (Equatable, Printable, Comparable), Boolean, Optional, Ordering, Record, String, the whole numeric tower (Integer, Rational, Algebraic, Transcendental and the covering Number, which brings the Number and Irrational Union Types with it), and List together with NestedList and NonEmptyList.

The only things NOT declared here are the ones no declaration could produce: the bare Type tags — Boolean, String, Integer, Rational, Algebraic, Transcendental, the open Record and the unapplied List — which live in packages/compiler/src/enricher/primitives.ts. loop — the one native Function family with no Namespace to live in — is declared here after all, in Loop.es, as ordinary free Functions. Printing is a Namespace: Terminal.print renders through Printable and ends the line, Terminal.inspect shows a value's structure and answers with it unchanged, and Terminal.write is the raw primitive both are built on (Terminal.es).

About half of the declared Method entries are also IMPLEMENTED here, in Essence; the rest bind to @essence-lang/runtime. What stays native is a deliberate line, not a backlog: the primitives everything else is composed from (Boolean.negate/is/and/or, integer and rational arithmetic, same-kind compare), the JavaScript intrinsics Essence has no expression for (String.uppercase, String.trim(at:), String.normalize(as:), String.lines/words, Record's reflective Methods, String.compare — there is no way to name a character's code point), and the iteration primitives the rest rest on (List.reduce, item(at:), slice, keepEvery, append(contentsOf:), static of, firstItem(where:) — the short-circuiting find beside the eager keepEvery — and String.split(on:), which is also the one native that decides what a "character" is: it segments into Unicode grapheme clusters (see graphemesOf in String.ts), so length, slice, reverse, firstIndex and the rest, all written on top of it, count and cut by grapheme).

One Method is native for a reason worth reading before assuming otherwise: List.is, because the pairwise form trips an infinite recursion in generic inference (the repro is at the declaration). String.replaceEvery used to be too — its empty part inserted at UTF-16 code-unit boundaries — but the empty part is now a no-op, so it is split(on part)::join(with replacement) in Essence.

The voice

The library is meant to be guessable: after a handful of Methods, a reader should be able to predict what the next one is CALLED and which of its Arguments carry a label. Four rules decide that, and every Method here follows them.

1. A transforming Method is an imperative command. add, sort, reverse, trim, round, negate, flatten, map, split, insert, clamp — never the past-tense participle (sorted, reversed, trimmed). In a mutating language list.sort() is dangerous and sorted() is how an immutable API warns you; Essence has no such hazard to warn against, because EVERY Method is a Query and nothing is ever changed in place. list::sort() can only mean "give me the sorted List" — there is no mutating sort to confuse it with. Immutability is a global invariant, stated once here, not something each name re-encodes. :: already lends the receiver-first feel; the imperative completes it and reads better (1::add(2), not 1::added(2)).

2. A preposition is a label, never fused into the verb. When an Argument is reached through a preposition — of a thing, on a separator, with a prefix, by a comparison, at an index — the preposition is that Argument's label and the verb stem stays bare: text::firstIndex(of ","), text::split(on ","), text::starts(with "x"), list::sort(by compare), list::item(at 0), 2::raise(to 10), 1::divide(by 2).

3. Direct object positional, everything prepositional labelled. A verb's direct object — what it acts on, with no preposition between — stays positional and bare: contains(_ other), prepend(_ item), add(_ other), insert(_ item, …). Everything reached THROUGH a preposition is labelled, whether it is the only Argument (firstIndex(of:)) or a later one (replaceEvery(_ part, with:), insert(_ item, at index), pad(to length, with pad)). So insert(_ item, at index) reads "insert item, at index" — the item is the direct object, the index is reached through at.

4. A variant of one idea is an Overload, not a new name. One trim with an at: Overload, not trimmed/trimmedAtStart/trimmedAtEnd; one sort, not sorted/sortedBy; one round with a toward: Overload, not round/roundDown/roundUp/truncate. And a fixed set of modes is a choice, never a Stringtrim(at Side#Start), not trim("start"), and round(toward Rounding#Down), not a Method name per direction. The default is a Case of that choice too, not a value hidden in a body: trim() IS trim(at #BothEnds) and round() IS round(toward #Nearest).

What counts as a variant. A MODE — one of a fixed set of ways to carry the same operation out, where the caller picks and the return Type does not change. A quantifier or a position spelled into the name is NOT one: removeFirst /removeLast, firstItem/lastItem, firstIndex/lastIndex and replaceEvery/replaceFirst say WHICH items the Method is about, which reads as a different question rather than as the same question answered differently. Those keep their own names.

The one thing rule 4 does NOT license. The numeric tower declares the four inequalities on Integer and Rational AND on the covering Number, and that is not duplication to collapse — it is a performance stratification. The same-kind entry is written on the member's own compare; Number's is the sixteen-cell cross-kind table that reaches the whole numeric tower. Deleting the member entries would route two Integers through it and nearly double a Program that only prints a greeting. The reasoning is written above Integer::isLessThan, and packages/compiler/src/tests/bundleSize.spec.ts is the guard. Before collapsing anything that looks repeated here, check whether the repeat is what keeps a body reaching only its own Namespace's primitives.

Three name SHAPES, so rule 1 is not misapplied:

| Shape | Form | Examples | |---|---|---| | Transformation — does something, returns the result | imperative command | sort, reverse, trim, negate, pad, clamp, raise(to:), join(with:) | | Predicate — returns a Boolean | is…/has…/doesNot… prefix, or a direct verb | isEmpty, isEven, hasItems, contains, starts(with:) | | Accessor — returns an intrinsic part | noun or adjective; no verb to force | length, numerator, reciprocal, absolute, keys, firstItem, item(at:), firstIndex(of:) |

Rules 2 and 3 do NOT apply to the is…/has…/doesNot… prefixes — those are predicate naming, not prepositional Arguments, so isGreaterThan, isBetween and doesNotContain keep their fused word. Quantifiers and adjectives are not prepositions either: removeEvery, keepEvery, removeFirst, removeDuplicates, firstItem/lastItem keep theirs.

Two more conventions worth stating because they are already consistent and easy to break:

  • A predicate Parameter is always labelled wherekeepEvery(where:), count(where:), anyItem(where:).
  • Count-like nonsense is lenient; value-like failure returns an OptionalList.repeat(_, times 0) is the empty List, while clamp with inverted bounds is #Empty.
  • Keep return Types tight. Add Overloads rather than widening one signature: Integer::add(Integer) -> Integer beside add(Rational) -> Rational, never a single add(Number) -> Number.
  • A static constant is a proper noun. Number.Pi, Number.Tau, Number.E, Number.GoldenRatio — PascalCase, named for the thing itself; never SCREAMING_CASE, and spelled out (GoldenRatio) unless the mathematics itself writes the short form (Pi, E).

List's bounded Methods

Three of List's Method Generics carry a Protocol bound, and each bound is a statement about what the Method needs rather than a restriction to work around.

join<infer ItemType is Printable>(with separator: String) -> String is deliberately wider than a reader might expect: joining asks nothing of the items but that each can say what it is, so [1, 2, 3]::join(with ", ") is "1, 2, 3", not a type error. sort<infer ItemType is Comparable>() is the same shape for ordering.

is, isNot, contains, doesNotContain, firstIndex(of:), lastIndex(of:), count(of:), removeEvery(_ item:) and removeDuplicates are bounded is Equatable, so equality between items means the item Type's OWN is rather than a structural comparison the language cannot express. That is a narrowing: a Method holding an UNBOUNDED List<ItemType> can no longer call them, and the Diagnostic says which bound to add. List conforms is Equatable where ItemType is Equatable, so nested Lists still have a witness.

Development

Editing the library itself — the declarations { … } form, how the loader reads these files, the native contract, the emission model, the editing hazards, and what registering a new Namespace takes — is covered in DEVELOPMENT.md.