Exporting a Value

Status: implemented

This is a complete module:

export default 5;

The module function returns an object of its exports: this module returns { default: 5 }. The default export is 5, which a default import binds and JSON/DataJS value output serializes. If the default export is an object, it remains inside that property; its members do not become module exports.

Named constants are exports and local bindings:

export const z = [5];
export const a = z;
export default a;

This module returns { a: z, default: z, z: z }, with all three properties sharing the same array. Export keys follow JavaScript namespace order (lexicographic, including default). Initializers use the existing const rules: earlier bindings are available, and duplicate bindings are errors. The name then is reserved for exports, regardless of its value, and nowhere else: a local const, a parameter or an import binding may be called then (names).

Every other name a const may have can be exported, a built-in prototype's included. The export object has no prototype — a JavaScript module namespace has none — so export const __proto__ = 7; returns { ["__proto__"]: 7 }, an own property like any other export, and an importer selects it the same way (importing).

Unless a module ends in throw, at least one export is required (module exports nothing): a const of a name exports it, and an exported pattern exports each name it binds (destructuring), so a module whose only exports bind no name is refused at its last export. A named-only module needs no default: export const a = 5; returns { a: 5 }. When present, export default is last; only trivia — whitespace, newlines and comments — may follow it. Ordinary and exported constants may appear together after all imports.

A default export can be any supported value:

export default { "name": "fjs", "tags": ["data", "config"] };

export default alone already expresses everything JSON expresses — the value that follows it is the module's default export. What takes a module past a tree, and past what JSON can hold at all, is the rest of the language: constants and imports name shared parts, and bigint and undefined are values JSON has no spelling for.

export { ... } and re-exports remain unsupported. Export-list options are tracked separately in export-lists.

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export.

Examples

The compiler demo offers the programs exports in module.f.js, each with its real output.

The demo on this page runs sample.f.js, this page's example as a function of one number — a named export beside the default one — with every output fjs compile makes of it: the expression graph (EDAG), the Rust and the WebAssembly that Rust builds to. Its Run button calls the default export with 1 and 2, as JavaScript and as WebAssembly, and shows what each call returned or threw.

export const five = 5;
export default (...a) => five * a[0];

Reference

Demo

In FunctionalScript

fjs compile accepts this module, and it runs as JavaScript and as WebAssembly alike.

A FunctionalScript module and what fjs compile makes of it: the expression graph it lowers to, the Rust it compiles to on the NaNVM runtime, and the WebAssembly that Rust builds to. Run calls its default export with 1, then 2, first as the JavaScript it already is and then as WebAssembly, and shows what each call returned or threw and how long one call takes, timed over 1000 calls.

FunctionalScript source:

/**
 * The example of [`README.md`](./README.md) (Exporting a Value) as a
 * function of one number, for the page's WASM demo: a named export beside
 * the default one.
 *
 * @module
 */

export const five = 5;
/** @type {(...a: readonly number[]) => unknown} */
export default (...a) => five * a[0];

Expression graph (EDAG) that fjs compile lowers it to:

{}defaultfive=>5=>lengthbody0**leftright5..objidxrest0

The same graph, as fjs compile writes it to a .edag.data.js file:

export default ["{}",[[":","default",["=>",0,[],["*",5,[".",["rest"],0]]]],[":","five",5]]];

Rust that fjs compile writes from it, on the NaNVM runtime:

// @generated by `fjs compile`. Do not edit: recompile the source module instead.

use nanvm_lib::vm::unstable::{f64_any, string_key};
use nanvm_lib::vm::{Any, Array, IStaticFunction, ToAny, ToObject};

#[rustfmt::skip]
#[allow(clippy::if_same_then_else)]
pub fn module<A: IStaticFunction>(_runtime: Any<A>) -> Result<Any<A>, Any<A>> {
    Ok([(string_key("default"), A::static_function(|_self, _args| {
        let _b132: Any<A> = _args.rest(0).to_any();
        f64_any(0x4014000000000000) * (Any::dot(_b132.clone(), f64_any(0x0000000000000000)).end())?
    }, 0, Array::default(), Some("(...$0)=>5*$0[0]")).to_any()), (string_key("five"), f64_any(0x4014000000000000))].to_object().to_any())
}

WebAssembly text (WAT) of the functions compiled from this source, without the NaNVM runtime:

  (func $nanvm_demos::samples::exports::module::<nanvm_lib::naive::Naive> (;685;) (type 1) (param i32 i32)
    (local i32 i32 i32 i32 i32 i64)
    global.get $__stack_pointer
    i32.const 128
    i32.sub
    local.tee 2
    global.set $__stack_pointer
    local.get 2
    i32.const 0
    i32.store16 offset=36
    local.get 2
    i32.const 1068153
    i32.store offset=32
    local.get 2
    i32.const 1068146
    i32.store offset=28
    local.get 2
    i32.const 56
    i32.add
    local.get 2
    i32.const 28
    i32.add
    call $"core::iter::adapters::try_process::<core::iter::adapters::map::Map<core::str::iter::EncodeUtf16, core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>::Ok>, u16, core::result::Result<core::convert::Infallible, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>, <core::result::Result<alloc::rc::Rc<[u16]>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>> as core::iter::traits::collect::FromIterator<core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>>>::from_iter<core::iter::adapters::map::Map<core::str::iter::EncodeUtf16, core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>::Ok>>::{closure#0}, alloc::rc::Rc<[u16]>>"
    block ;; label = @1
      block ;; label = @2
        block ;; label = @3
          block ;; label = @4
            local.get 2
            i32.load8_u offset=56
            local.tee 3
            i32.const 255
            i32.ne
            br_if 0 (;@4;)
            local.get 2
            i32.load offset=64
            local.set 4
            local.get 2
            i32.load offset=60
            local.set 5
            local.get 2
            i32.const 0
            i32.store8 offset=40
            local.get 2
            i32.const 16
            i32.add
            local.get 2
            i32.const 40
            i32.add
            call $"<core::iter::adapters::GenericShunt<core::iter::adapters::map::Map<core::iter::sources::empty::Empty<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>, core::result::Result<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>, ()>::Ok>, core::result::Result<core::convert::Infallible, ()>> as alloc::rc::ToRcSlice<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>>::to_rc_slice"
            local.get 2
            local.get 2
            i32.load offset=20
            local.tee 6
            i32.store offset=60
            local.get 2
            local.get 2
            i32.load offset=16
            local.tee 3
            i32.store offset=56
            block ;; label = @5
              local.get 2
              i32.load8_u offset=40
              i32.const 1
              i32.ne
              br_if 0 (;@5;)
              local.get 3
              local.get 3
              i32.load
              i32.const -1
              i32.add
              local.tee 0
              i32.store
              local.get 0
              br_if 4 (;@1;)
              local.get 2
              i32.const 56
              i32.add
              call $"<alloc::rc::Rc<[nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>]>>::drop_slow"
              br 4 (;@1;)
            end
            local.get 3
            i32.eqz
            br_if 3 (;@1;)
            i32.const 206
            i32.const 0
            local.get 3
            local.get 6
            i32.const 1068219
            i32.const 16
            call $<nanvm_lib::naive::function::Function>::new
            local.set 3
            local.get 2
            i32.const 0
            i32.store16 offset=36
            local.get 2
            i32.const 1068239
            i32.store offset=32
            local.get 2
            i32.const 1068235
            i32.store offset=28
            local.get 2
            i32.const 56
            i32.add
            local.get 2
            i32.const 28
            i32.add
            call $"core::iter::adapters::try_process::<core::iter::adapters::map::Map<core::str::iter::EncodeUtf16, core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>::Ok>, u16, core::result::Result<core::convert::Infallible, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>, <core::result::Result<alloc::rc::Rc<[u16]>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>> as core::iter::traits::collect::FromIterator<core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>>>::from_iter<core::iter::adapters::map::Map<core::str::iter::EncodeUtf16, core::result::Result<u16, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>::Ok>>::{closure#0}, alloc::rc::Rc<[u16]>>"
            local.get 2
            i32.load8_u offset=56
            local.tee 6
            i32.const 255
            i32.ne
            br_if 1 (;@3;)
            local.get 2
            i64.load offset=60 align=4
            local.set 7
            local.get 2
            i32.const 0
            i32.store8 offset=28
            local.get 2
            i64.const 4617315517961601024
            i64.store offset=104
            local.get 2
            i32.const 2
            i32.store8 offset=96
            local.get 2
            local.get 7
            i64.store offset=88
            local.get 2
            local.get 3
            i32.store offset=76
            local.get 2
            i32.const 7
            i32.store8 offset=72
            local.get 2
            local.get 4
            i32.store offset=68
            local.get 2
            local.get 5
            i32.store offset=64
            local.get 2
            i64.const 8589934592
            i64.store offset=56
            local.get 2
            local.get 2
            i32.const 28
            i32.add
            i32.store offset=112
            local.get 2
            i32.const 8
            i32.add
            local.get 2
            i32.const 56
            i32.add
            call $"<core::iter::adapters::GenericShunt<core::iter::adapters::map::Map<core::array::iter::IntoIter<(nanvm_lib::vm::string::String<nanvm_lib::naive::Naive>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>), 2>, core::result::Result<(nanvm_lib::vm::string::String<nanvm_lib::naive::Naive>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>), ()>::Ok>, core::result::Result<core::convert::Infallible, ()>> as alloc::rc::ToRcSlice<(nanvm_lib::vm::string::String<nanvm_lib::naive::Naive>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>)>>::to_rc_slice"
            local.get 2
            local.get 2
            i32.load offset=12
            local.tee 6
            i32.store offset=44
            local.get 2
            local.get 2
            i32.load offset=8
            local.tee 3
            i32.store offset=40
            block ;; label = @5
              local.get 2
              i32.load8_u offset=28
              i32.const 1
              i32.ne
              br_if 0 (;@5;)
              local.get 3
              local.get 3
              i32.load
              i32.const -1
              i32.add
              local.tee 0
              i32.store
              local.get 0
              br_if 3 (;@2;)
              local.get 2
              i32.const 40
              i32.add
              call $"<alloc::rc::Rc<[(nanvm_lib::vm::string::String<nanvm_lib::naive::Naive>, nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>)]>>::drop_slow"
              br 3 (;@2;)
            end
            local.get 3
            i32.eqz
            br_if 2 (;@2;)
            local.get 0
            local.get 6
            i32.store offset=16
            local.get 0
            local.get 3
            i32.store offset=12
            local.get 0
            i32.const 5
            i32.store8 offset=8
            local.get 0
            i64.const 0
            i64.store
            local.get 1
            call $"#func680 core::ptr::drop_glue::<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>"
            local.get 2
            i32.const 128
            i32.add
            global.set $__stack_pointer
            return
          end
          local.get 2
          local.get 2
          i32.load8_u offset=59
          i32.store8 offset=43
          local.get 2
          local.get 2
          i32.load16_u offset=57 align=1
          i32.store16 offset=41 align=1
          local.get 2
          local.get 2
          i32.load offset=68
          i32.store offset=52
          local.get 2
          local.get 2
          i64.load offset=60 align=4
          i64.store offset=44 align=4
          local.get 2
          local.get 3
          i32.store8 offset=40
          i32.const 1065572
          i32.const 43
          local.get 2
          i32.const 40
          i32.add
          i32.const 1065556
          i32.const 1065616
          call $core::result::unwrap_failed
          unreachable
        end
        local.get 2
        local.get 2
        i32.load8_u offset=59
        i32.store8 offset=43
        local.get 2
        local.get 2
        i32.load16_u offset=57 align=1
        i32.store16 offset=41 align=1
        local.get 2
        local.get 2
        i32.load offset=68
        i32.store offset=52
        local.get 2
        local.get 2
        i64.load offset=60 align=4
        i64.store offset=44 align=4
        local.get 2
        local.get 6
        i32.store8 offset=40
        i32.const 1065572
        i32.const 43
        local.get 2
        i32.const 40
        i32.add
        i32.const 1065556
        i32.const 1065616
        call $core::result::unwrap_failed
        unreachable
      end
      i32.const 1057428
      i32.const 43
      local.get 2
      i32.const 127
      i32.add
      i32.const 1057412
      i32.const 1057472
      call $core::result::unwrap_failed
      unreachable
    end
    i32.const 1057428
    i32.const 43
    local.get 2
    i32.const 127
    i32.add
    i32.const 1057412
    i32.const 1057472
    call $core::result::unwrap_failed
    unreachable
  )
  (func $"<nanvm_demos::samples::exports::module<nanvm_lib::naive::Naive>::{closure#0} as core::ops::function::FnOnce<(&nanvm_lib::naive::function::Function, nanvm_lib::vm::array::Array<nanvm_lib::naive::Naive>)>>::call_once" (;686;) (type 4) (param i32 i32 i32 i32)
    (local i32 i32)
    global.get $__stack_pointer
    i32.const 144
    i32.sub
    local.tee 4
    global.set $__stack_pointer
    local.get 4
    local.get 3
    i32.store offset=12
    local.get 4
    local.get 2
    i32.store offset=8
    block ;; label = @1
      local.get 2
      i32.load
      local.tee 5
      i32.const -1
      i32.eq
      br_if 0 (;@1;)
      local.get 4
      local.get 3
      i32.store offset=24
      local.get 4
      local.get 2
      i32.store offset=20
      local.get 4
      i32.const 6
      i32.store8 offset=16
      local.get 4
      i64.const 4617315517961601024
      i64.store offset=40
      local.get 4
      i32.const 2
      i32.store8 offset=32
      local.get 2
      local.get 5
      i32.const 2
      i32.add
      local.tee 5
      i32.store
      local.get 5
      i32.eqz
      br_if 0 (;@1;)
      local.get 4
      local.get 2
      i32.store offset=132
      local.get 4
      i32.const 6
      i32.store8 offset=128
      local.get 4
      local.get 3
      i64.extend_i32_u
      i64.store offset=136
      local.get 4
      i64.const 0
      i64.store offset=104
      local.get 4
      i32.const 2
      i32.store8 offset=96
      local.get 4
      i32.const 64
      i32.add
      local.get 4
      i32.const 128
      i32.add
      local.get 4
      i32.const 96
      i32.add
      call $<nanvm_lib::vm::lambda::member::Member<nanvm_lib::naive::Naive>>::new
      block ;; label = @2
        block ;; label = @3
          block ;; label = @4
            local.get 4
            i32.load8_u offset=64
            i32.const 255
            i32.ne
            br_if 0 (;@4;)
            local.get 0
            local.get 4
            i64.load offset=80
            i64.store offset=16
            local.get 0
            local.get 4
            i64.load offset=72
            i64.store offset=8
            local.get 0
            i64.const 1
            i64.store
            local.get 4
            i32.const 32
            i32.add
            call $"#func680 core::ptr::drop_glue::<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>"
            local.get 4
            i32.const 16
            i32.add
            call $"#func680 core::ptr::drop_glue::<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>"
            local.get 2
            local.get 2
            i32.load
            i32.const -1
            i32.add
            local.tee 3
            i32.store
            local.get 3
            i32.eqz
            br_if 1 (;@3;)
            br 2 (;@2;)
          end
          local.get 4
          local.get 4
          i64.load offset=88
          i64.store offset=120
          local.get 4
          local.get 4
          i64.load offset=80
          i64.store offset=112
          local.get 4
          local.get 4
          i64.load offset=72
          i64.store offset=104
          local.get 4
          local.get 4
          i64.load offset=64
          i64.store offset=96
          local.get 4
          i32.const 128
          i32.add
          local.get 4
          i32.const 96
          i32.add
          call $<nanvm_lib::vm::lambda::member::Member<nanvm_lib::naive::Naive>>::own
          block ;; label = @4
            block ;; label = @5
              local.get 4
              i32.load8_u offset=128
              i32.const 255
              i32.eq
              br_if 0 (;@5;)
              local.get 4
              local.get 4
              i64.load offset=136
              i64.store offset=56
              local.get 4
              local.get 4
              i64.load offset=128
              i64.store offset=48
              br 1 (;@4;)
            end
            local.get 4
            i32.const 256
            i32.store16 offset=48
          end
          local.get 4
          i32.const 96
          i32.add
          call $"#func562 core::ptr::drop_glue::<nanvm_lib::vm::unpacked::Unpacked<nanvm_lib::naive::Naive>>"
          local.get 4
          i32.const 112
          i32.add
          call $"#func562 core::ptr::drop_glue::<nanvm_lib::vm::unpacked::Unpacked<nanvm_lib::naive::Naive>>"
          local.get 0
          local.get 4
          i32.const 32
          i32.add
          local.get 4
          i32.const 48
          i32.add
          call $"<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>::numeric_op::<<nanvm_lib::vm::numeric::Numeric<nanvm_lib::naive::Naive> as core::ops::arith::Mul>::mul>"
          local.get 4
          i32.const 16
          i32.add
          call $"#func680 core::ptr::drop_glue::<nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>>"
          local.get 2
          local.get 2
          i32.load
          i32.const -1
          i32.add
          local.tee 3
          i32.store
          local.get 3
          br_if 1 (;@2;)
        end
        local.get 4
        i32.const 8
        i32.add
        call $"<alloc::rc::Rc<[nanvm_lib::vm::any::Any<nanvm_lib::naive::Naive>]>>::drop_slow"
      end
      local.get 4
      i32.const 144
      i32.add
      global.set $__stack_pointer
      return
    end
    unreachable
  )

Emergent Testing

Idle. Press Run to start the suite.

  • ./proof.f.mjs
  • ./then/proof.f.mjs