Skip to content

Latest commit

 

History

History
99 lines (70 loc) · 4.32 KB

extended.md

File metadata and controls

99 lines (70 loc) · 4.32 KB

na x

na x is na extended with support for types and functions.

na's data types may be extended with types and functions, inspired by edn's tagged elements.

Types may be defined by both users and parsers, while functions can only be defined by parsers.

Types and functions must be defined with a valid name. Types must be prefixed with a # sigil.

When applied to a value, a type or function must appear before the value, on the same line, separated by space.

Parsers may allow clients to register handlers for custom types and functions, for example to transform na values into data types of the target language. Client-defined handlers should be pure functions without side effects.

Security is paramount

A parser's built-in handlers must be pure functions with no side effects. Further, parsers must by default disallow clients to register handlers. To enable client-defined handlers, a parser must be explicitly instructed to run in unsafe mode, which should raise a warning.

Resilience is important

If a parser encounters a type or function for which no handler is registered, it may ignore it and use the value verbatim instead.

Parsers must be able to read any syntactically valid na data without causing errors. Errors may, however, be raised if the parser is run in strict mode.

Standard types

#any: #none | #some
#some: #truth | #number | #text | #block
#number: #decimal | #integer | #natural | #ratio
#list: [ #natural: #any ]  -- key signature
#record: [ #name: #any ]   -- key signature

Where:

  • #none denotes the concept of nothing/null/nil/void/undefined
  • #name denotes a valid name
  • #number denotes an arbitrary-precision number, with the following subtypes:

In standard na x, #name may only be used as the key of a key signature, and keys may only be of the type #natural or #name.

Fixed-precision numbers

Support for fixed-precision numbers are implementation/platform dependent.

Implementations should use type names from the following list:

  • Signed integers
    • #i8 denotes an 8-bit signed integer
    • #i16 denotes a 16-bit signed integer
    • #i32 denotes a 32-bit signed integer
    • #i64 denotes a 64-bit signed integer
    • #i128 denotes a 128-bit signed integer
  • Unsigned integers
    • #u8 denotes an 8-bit unsigned integer
    • #u16 denotes a 16-bit unsigned integer
    • #u32 denotes a 32-bit unsigned integer
    • #u64 denotes a 64-bit unsigned integer
    • #u128 denotes a 128-bit unsigned integer
  • Floating-point

Examples

User-defined types

#person: [                     -- type definition
    name: #text                -- type annotation
    friends: #persons | #none  -- optional item
]

#persons: [#natural: #person]  -- list type (key signature)

joe: #person [                 -- typed record
    name: 'Joe'
]

Parser-defined functions

area: square[7m, 6m]                          -- applying a function to a block of values
timestamp: instant '1985-04-12T23:20:50.52Z'  -- "casting" a string to an RFC 3339 timestamp
id: uuid 'f81d4fae7dec11d0a76500a0c91e6bf6'   -- "casting" a string to an RFC 4122 UUID

Derived formats

na x may itself be used as a subset of other data formats.