wasm-lang/doc/design.md

3.5 KiB

design choices

  • no macros, try to keep parsing fast. instead, have constant time expressions
  • static typing in a scripting language, but may need to be able to make ad-hoc structs
  • push work to the definition instead of making callers handle stuff (ie Drop will exist, defer won't)

typing?

// rust style
struct Foo<T> {
  param: T,
}

// zig style
fn Foo(T: type) -> type {
  struct {
    param: T
  }
}

// how would traits work with zig style stuff?

// rust style
fn length<T>(vec: Vec<T>) -> u32 {  }

// zig style
fn length(vec: ???) -> u32 { ??? }

fn main() {
  let a: Foo<i32> = Foo {
    foo: 10,
  };
  
  let b: Foo(i32) = Foo(i32) {
    
  };
}

packages?

// rust-style "use"
// can be used to bring anything in scope, not only packages
use foobar;
use foobar::{a, b, c};

// typescripty imports
// strings make it easier to import
import "foobar" as name;
import "foobar" as { a, b, c };
import "https://foobar.tld/path/to/file" as name;
// import "foobar"; // bad?

// use with urls?
use "https://foobar.tld/path/to/file" as name;
use "https://foobar.tld/path/to/file"::Client;

// imports as functions
// feels strange if import is a function but export/pub is a keyword
// but it also is more extensible import(something, ...argv)
let foobar = import("foobar");
let { a, b, c } = import("foobar");

context?

trait Entry {
  fn is_dir(self) -> bool;
  fn is_file(self) -> bool;
  fn is_symlink(self) -> bool;
  fn name(self);
  fn create(self);
  fn open(self);
  fn remove(self);
  fn rename(self);
  fn truncate(self, size: u64);
  fn exists(self);
  fn metadata(self) -> Metadata;
}

trait File is Read + Write + Seek + Entry {}

struct Metadata {
  ino: u64,
  len: u64,
  modified: u64,
  permissions: Permissions,
}

trait Filesystem {
  fn entry(self, path: Path) -> Entry;
  
  fn open(self, path: Path, options: OpenOptions) -> File {
    self.entry(path).open(options)
  }

  fn read<T>(self, path: Path) -> T {
    // bikeshedding go brrr
    self.open(path, .Read).read();
    // "infer struct type"
    foo(path, .{});
    foo(path, :{});
    // maybe ditch {} and go for []?
    // but then what would be for arrays?
    foo(path, [foo: bar]);
    foo(path, [foo = bar]);
    foo(path, StructType { foo: bar });
    foo(path, StructType { foo = bar });
    foo(path, StructType [ foo: bar ]);
    foo(path, StructType [ foo = bar ]);
    
    self.open(path, :Read).read()
  }
  
  fn write(self, path: Path, data: View<[u8]>) -> Result<(), Error> {
    self.open(path, :Write).write(data)
  }
}

// vars, cli args, current directory/file, etc
struct Environment { ... }

trait Network {
  fn listen_tcp(self, addr: SocketAddr)  -> Result<TcpServer, Error>;
  fn connect_tcp(self, addr: SocketAddr) -> Result<TcpClient, Error>;
  fn listen_tls(self, addr: SocketAddr)  -> Result<TlsServer, Error>;
  fn connect_tls(self, addr: SocketAddr) -> Result<TlsClient, Error>;
  fn bind_udp(self, addr: SocketAddr)    -> Result<UdpSocket, Error>;
}

// all context is passed into 
struct Context {
  fs: Filesystem,
  env: Environment,
  os: Os,
  net: Network,
  log: Log,
}

// fn main({ log }: Context) {
fn main(ctx: Context) {
  // do things with ctx here...
  ctx.log.info("hello world!");

  let http = HttpClient::new(ctx.net);
  http.get("https://url.tld/path/to/something").send()?.json()?;
  http.post("https://url.tld/path/to/something").body(something)?;
}

// problem: i'd need to pass ctx to *every* function that wants to log?
fn another() {
  // maybe this?
  import { log } from "context";
  log.debug("something");
}