Packages and Libraries
Iota | Go | Rust | Haskell | |
---|---|---|---|---|
? | "pkg.go.dev" | "crates.io" | a global root | |
other namespaces | ? | "github.com/", etc | cargo configurable? | cabal configurable? |
"project" | ? | module, "go.mod" | a cargo workspace or project | a cabal unit |
"library" | ? | package | a target in a project | a sub-unit |
"binary" | ? | package main | a target in a project | a sub-unit |
Translation unit | ? | =package | =crate | =file |
module namespaces
Go-style, one implicit global root like "pkg.iota.dev/", optionally other roots like "github.com/org/". These are root packages that include other packages recursively? The universe of packages is just a lazy bag, like in Nix?
A package file tree of a package my-pkg
:
my-sub-pkg/
package.iota
...
assets/ # static resources
build/ # like target/ in Cargo
docs/
deps/
github.com/org/package/import.iota
github.com/org/pkg/subpkg/import.iota
examples/
exe/ # like cmd/ in Go, app/ in Cabal ?
binary1.iota
lib/ # like pkg/ in Go
pubmod1.iota
pubmod1.test.iota
pubmod2.iota
src/ # like internal/ in Go
internal1.iota
test/ # like tests/ in Cargo?
package.iota
It is equivalent to a table:
(
package = @import("package.iota")
lib = (
pubmod1 = @import("lib/pubmod1.iota")
pubmod2 = @import("lib/pubmod2.iota")
)
exe = (
binary1 = @import("exe/binary1.iota")
)
src = (internal1 = @import("src/internal1.iota"))
deps = (
`github.com`/org/pkg = @import("deps/github.com/org/package/import.iota")
)
...
)
What's a package?
- a const-value of:
- metadata
- name, description, url, license, etc
- dependencies
- list of its modules
- metadata
- a const-trait that can
build()
/test()
/etc its values?- iotac executable == "iota const evaluator". iota-"cargo" is just a library.
$ iotac build --target=binary1
== run const-fnpackage.build(target = binary1)
$ iotac test
== run const-fnpackage.test()
- ...
package.iota
is a go.mod
-like project file, an entry point to building. A mix of cargo.toml, Deno's main file, go.mod, etc.
// my_package = package(
// package my_package(ctx: Context) // shortcut
// like cargo.toml,
// key `name` set to `my_package`:
name = "my_package"
description = "foos bars"
url = "author/lib" // if uploaded to the main registry
dependencies = (
"github.com/org/lib" = (version = "v0.1"),
)
require = (
// the list of capabilities required from the environment
)
// Q: how to describe build targets and tests?
// Q: how to include modules?
// like `mod mod1;` in Rust, Go-style syntactically?
// end
binary vs library
Entry points require a uniq ref to a process resource (borrowed from the OS).
module definition
// shortcut: module main(ctx: Context) (...) { ... }
const main module(ctx: Context) (
doc = "module documentation goes here?"
// public definitions, module "signature"
) is
// imports
const std = ctx.import('std')
// private definitions, implementations
end // closes is, optional if EOF
module imports
// full form
self.import(
"std" // std = "pkg.iota.dev/std"
"github.com/org/lib" // lib = "github.com/org/lib"
rename "name"
)
module contexts
The context of a module defines the list of accessible static capabilities.
An executable gets the root dynamic capability from the environment (e.g. from the ELF loader or another loader middleware).
A library gets its capabilities from its importer.
Interop
Goals:
- make C ABI interop rich and powerful (so it works well)
- make C headers interop easy, almost seamless technically (~
@ cImport
). - make it complex and demanding, proof-heavy when imported (lots of additional static information)
- Rust interop via
extern "interop_abi"
? - WIT from WASM?
- Q: GC-based languages