Giter VIP home page Giter VIP logo

winglang / wing Goto Github PK

View Code? Open in Web Editor NEW
4.4K 165.0 169.0 153.24 MB

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

Home Page: https://winglang.io

License: Other

Rust 25.08% JavaScript 1.18% CWeb 0.27% C++ 0.03% TypeScript 73.01% Python 0.01% Scheme 0.04% HTML 0.07% Shell 0.11% C 0.16% CSS 0.03% Dockerfile 0.01%
programming-language cloud compiler sdk serverless language winglang devops-tools devtool rust

wing's Introduction

Wing Banner

Welcome to the Wing Language! 👋

Take a Tour ▪︎ Getting Started ▪︎ Join Slack ▪︎ FAQ ▪︎ Roadmap ▪︎ Issues ▪︎ Discussions ▪︎ Contribute

Winglang is a new open-source programming language designed for the cloud (aka "cloud-oriented"). Wing enables developers to build distributed systems that leverage cloud services as first-class citizens by combining infrastructure and application code in a safe and unified programming model (aka "cloud-oriented"). Wing programs can be executed locally (yes, no internet required) using a fully-functional simulator, or deployed to any cloud provider (yes, Wing programs are portable across providers).

The mission of Winglang is to bring back your creative flow and close the gap between imagination and creation.

Developing for the cloud today requires mastering various layers of the cloud stack, IAM roles, networking, and numerous tools, along with finding creative ways to test and debug code. In addition, long deployment times hinder iteration cycles and take developers out of their creative flow.

Winglang addresses these pains by letting you work at a higher level of abstraction and allowing you to focus on business logic instead of cloud mechanics, only surfacing low-level details when it's needed. We also provide you with a set of tools that let you test your code locally, significantly faster than before.

Wing Demo

Wing is built by Elad Ben-Israel, the guy behind the AWS CDK, the gang at the Wing Cloud team and an amazing community of contributors (also known as Wingnuts).

Click here to watch a short video introduction to the Wing language.

Why do we think the cloud needs a programming language? 🤔

Cloud applications are fundamentally different from applications that run on a single machine - they are distributed systems that rely on cloud infrastructure to achieve their goals.

In order to be able to express both infrastructure and application logic in a safe and unified programming model, Winglang has two execution phases: preflight for infrastructure definitions and inflight for runtime code.

Preflight code is executed during compilation and produces the infrastructure configuration for your app (e.g. Terraform, CloudFormation, etc). Inflight code is compiled into JavaScript and executed within cloud compute platforms in Node.js environments.

Let's look at a simple example:

bring cloud;

let queue = new cloud.Queue();
let counter = new cloud.Counter();
let bucket = new cloud.Bucket();

queue.setConsumer(inflight (message) => {
  let i = counter.inc();
  bucket.put("file-{i}.txt", message);
});

cloud.Queue, cloud.Counter and cloud.Bucket are preflight objects. They represent cloud infrastructure resources. When compiled to a specific cloud provider, such as AWS, a Terraform file will be produced with the provider's implementation of these resources. The queue.setConsumer() method is a preflight method that configures the infrastructure to invoke a particular inflight function for each message in the queue.

Now comes the cool part: the code that runs inside the inflight function interacts with the counter and the bucket objects through their inflight methods (counter.inc() and bucket.put()). These methods can only be called from inflight scopes.

Very cool, but what here cannot be done by a library or compiler extension?

In existing languages, where there is no way to distinguish between multiple execution phases, it is impossible to naturally represent this idea that an object has methods that can only be executed from within a specific execution phase (or within certain scopes of the program). You are welcome to read more about it here (including code samples that show the same app built in Wing vs. other solutions).

What makes Wing a good fit for cloud development? 🌟

Wing was built from scratch to make it easy for building applications on any cloud. It includes an assembly of different features that serve that purpose:

For a more in-depth look at Wing's features and benefits, check out our documentation.

Getting started 🛠️

🚧 This is a pre-release, please see our project status for more details.

If you'd just like to dip your feet in the water and see what Wing is all about, you can try it out in our online playground or walk through the interactive tour.

When you're ready to start building your own Wing apps, you'll need to:

  1. Install the Wing CLI.
  2. Get the Wing IDE Extension for your favorite editor.
  3. Launch the Wing Console and take it for a spin!

For a step-by-step guide, head over to our Getting Started guide. It's a once-in-a-lifetime adventure into the Wing rabbit hole!

FAQs ❓

Here are some questions we're commonly asked that are covered by our FAQ:

Community 💬

Join our flock in the Wing Slack community. We're here to help each other, answer questions, and share our cloud adventures. Alternatively, post any questions on GitHub Discussions.

Contributing 🤝

Want to help Wing take flight? Check out our contribution guide to learn how to set up a development environment and contribute to the project. You can also get started by opening the project in GitHub Codespaces.

Open in GitHub Codespaces

We are incredibly grateful to our entire community for contributing bug fixes and improvements:

License 📜

Wing is licensed under the MIT License. Contributions are made under our contribution license.

Happy coding, and remember: the sky's the limit with Wing (yes, another pun)! 🌤️🚀

wing's People

Contributors

3p3r avatar ainvoner avatar chriscbr avatar ekeren avatar eladb avatar eladcon avatar flyingimer avatar fynnfluegge avatar garysassano avatar graylime avatar hasanaburayyan avatar jogold avatar marciocadev avatar markmcculloh avatar meirdev avatar mergify[bot] avatar monada-bot[bot] avatar monadabot avatar polamoros avatar revitalbarletz avatar roniwds avatar shaiber avatar shaiber01 avatar skorfmann avatar skyrpex avatar staycoolcall911 avatar tsuf239 avatar warkanlock avatar weepingclown13 avatar yoav-steinberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wing's Issues

Version bumping

  • All public artifacts share a semantic version (0 major)
  • Version increments per semantic commit to main
    • minor or patch bump determined by PR/Commit type

Depends on #101

Build app local

run the preflight code and compile the code into js
--target=local --> Output is localhost js
This task is mainly handled by WingSDK

pay tech debt to runtime + future?

In order of importance in terms of runtime quality and stability:

  • Node runtime is not reentrant, needs N-API to become so. (44a0f5d)
  • Lots of stuff need to be RAII'd in wingrr.cc. Pretty sure some minor stuff are leaking. (e444c13)
  • Rust crate has RPATH issues, cargo should not need LD_LIBRARY_PATH to execute tests.
  • Language support should be hot-pluggable with dlsym maybe? To reduce size and attack surface? (won't do. docker for now)
  • wingrr executable target could be a bit more "formal" in its arg parsing.~ (won't do. not priority)
  • Go engine's entrypoint needs renaming or isolation.
  • Actually implement and respect workdir in execution.
  • The wingrr.h API can be shared and exposed to all participating languages for nested execution.
  • A very simple BSON/JSON API, only supporting POD types and CStrings can be exposed to all participating languages to store shared "state" among each other and free of each other's GC reach. This API can then be exposed to Rust for querying shared state. My initial thought is to wrap nlohmann/json around a very limited number of C functions, a lot like a very limited in-memory light MongoDB implementation. SQlite can also be used to achieve the same purpose. Meaning that a very simple CRUD api over SQLite could be shared among all languages. This is how JSII's IPC layer will be eliminated eventually. With a fast in-memory shared database among all languages. Additional api can be offered to dump and restore shared state from disk.

Implicit inflight client initialization

In the "deny list" example, we have this interesting behavior where the inflight code implicitly initializes the inflight client of Bucket. The Bucket class implementation would have its own ~init() function which might initialize the AWS SDK, for example. To the user, they're just accessing the Bucket's inflight/runtime operations, and they aren't aware there's an AWS SDK under the hood.

I've abbreviated the example below:

struct DenyListRule { ... }



resource DenyList {
  _bucket: cloud.Bucket;
  _object_key: str;

  init(props: DenyListProps) {
    this._bucket = cloud.Bucket();
    this._object_key = "deny-list.json";
  }

  ~ rules: mut_map<DenyListRule>?;

  ~ init() {
    // this._bucket is already initialized by the capture mechanic!
    this.rules = this._bucket.get(this._object_key) ?? mut_map<DenyListRule>();
  }

  ~ lookup(name: str, version: str): DenyListRule? {
    return this.rules[name] ?? this.rules["${name}/v${version}"];
  }

  ~ add_rule(rule: DenyListRule) { ... }
}



But what if Bucket's ~init function had required parameters? In this case, the compiler would not have enough information to automatically initialize the client for this._bucket inside the DenyList client.

For MVP, I propose we start with the restriction that inflight init functions cannot have any parameters. I think this small restriction is worth it for the end user experience of using resources in their code directly inflight (without manual initialization). Later, if users need more flexibility or this turns out to be a confusing user experience, we could also add an alternative syntax to customize capture client initialization on a per-resource or per-inflight-scope basis, or change the language to make resource client initialization required.

Wing toolchain, command line

The CLI should support the following commands and requirements:

  • Command: wing compile --target local bucket-uploader.w
  • Command: wing run bucket-uploader.wx (for local)
  • Command: wing compile --target tf-aws bucket-uploader.w
  • It should work on Mac, Linux, Windows - is that easy?
  • It should support the standard commands and structure (e.g. --help, --version)
  • The CLI should be available via npm: npm install -g @monadahq/wing

Event-based programming

We should consider having first class support for event based programming as sugar to publishing/subscribing to events emitted by cloud resources.

Think C# events for the cloud.

Basic AST framework for compilation

Trying to capture here the next steps in getting wingc design to be usable for basic apps:

  • Framework for creating a winglang AST during compilation instead of just spitting out JS code in a visitor pattern.
  • Build symbol table during compilation. AST references will link to the symbol table. The table will be nested for scoping support.
  • Generate output JS code through traversal of the AST.

Build app aws

run the preflight code and compile the code into tf (not packages)
--target=aws --> Output is terraform + js
This task is mainly handled by WingSDK

Type System

I'm listing the main things to be done here, perhaps we should break this down into multiple issues:

  • Handle capturing:
    • Resources
    • Immutable data.
  • Enforce mutability specification (const/mut).
  • Enforce member access specification (private/public).
  • Enforce in/preflight limitations (can't create resoures inflight etc.)
  • Named args support in functions/methods/constructors

MacOS runtime support

This should not be TOO bad. Go's cshared pattern seems cross platform. Node itself is cross platform. Our language support is mostly through Node and WASI. so that's portable as well. JNI and Mono are also portable.

Capture SDK resources

  • Detect sdk symbol references as resources (partial support until we have bring for JSII libs implemented) .
  • Capture references to SDK resources in inflight.
  • Add explicit * permissions between the inflight container (Function for now) and the captured resource. I think scanning the resources and creating a static list of all its inflight methods and passing this list to the capture will do the trick.

Output verification tests

Implement snapshot testing for verifying we're creating the expected JS code for both preflight and inflight for a given winglang input.

This should be aligned with the language specs so we're sure we cover everything.

As a first step we should take a look at existing project's output snapshot tests (clang, llvm, tsc, ...).

  • Add at least 1 end top end test via the CLI
  • Add a mechanism to allow us to easily add tests for the compiler

Completion and hover (language server)

  • Semantic syntax highlighting
  • Compiler diagnostics
  • Completion
    • Show valid keywords
    • Available variables within scope
    • Available types within scope
    • properties of current object
  • Hover info (show types for symbol)
    • Also show documentation

Development builds

While debugging on Wing, I noticed I could not use dbg! macros since they get omitted from the release build in Rust. But if I compile Rust in debug mode instead, then it doesn't get linked to wingrt. There should be a way to do make a "development" build of Wing end to end, which includes all debug statements and debug symbols in builds etc.

See #65 for some previous solution discussion

List of missing compiler features

This issue is a list of basic compilation features we're missing. At this point I feel it'll be clutter to create a separate issue for each one, but I still want a place to document the things that come up.

*Note that features like building libraries, packaging, importing etc are big language features that I want to keep outside the scope of this issue.

  • Validate all control paths return <Type> if the function has a defined return type.
  • Support implicit forward declarations like:
fn f() {
  x = 1;
}
let x = 2;
  • Don't fail on compilation error, somehow try to continue pass the error so we can report multiple errors in one pass.
  • Print span information in error messages including the file name.
  • Add span information to Types

WebAssembly runtime support

In order to run the compiler in the browser, we would need to have a functioning runtime for it.

Assuming the compiler, is itself compile-able to WASM, we can mark all APIs in wingpf.h as WASM imports and start by mocking them in browser first. So just straight execution of JS in browser. That's easy to start with.

Then slowly we can bring in either Txiki or build our own runtime with Spidermonkey which is already compiled to WASI and supported by Mozilla. We'll just add libuv to it and mock libuv in browser.

Eventually this new runtime will replace Node for orgs that do not like Node in their ops environment.

Other WASI-backed languages will be supported by this runtime as well, however, native bound languages will not be supported for the near future in the browser (Go, Java, and C#).

bundle external dependencies into libwingpf.so

Use webpack to bundle everything needed to run all WASI backed languages into a single JS file and then link it into the shared library itself, instead of relying on the filesystem.

Converge inflight and mutability?

I think we can go even bigger here. Instead of making the user realize that there is some special client required to interact with a construct at "runtime", why don't we use our compiler powers to determine what is required to capture. Each object should provide a full abstraction over the underlying resource, whether or not that needs to be done at "construction time" via, eg., CFN, or at "runtime" via, eg., the AWS SDK.

The model I'm considering here is more along the lines of mutability: objects have certain methods that will mutate itself and some that do not (post fact: I think inspired by Rust). Since wing is targeting multiple computation environments, the "initial" one is the only environment that can access the mutable methods. Other environments are only provided the immutable methods. We can do some fancy dependency analysis to determine what code needs to be captured in order to make these methods still function.

As an example, here is what a DynamoDB table would look like (the keyword to notice here is mut):

table.w
construct dynamodb.Client {
  private sdk_client: ...
  private table_name: token

  init(mut self, table_name: token) {
    self.sdk_client = ...
  }

  put_item(self, item) {
    self.sdk_client.put_item({
      TableName: self.table_name,
      ...item
    })
  }
}

construct dynamodb.CfnTable {
  table_name: token
  global_secondary_indices: list<...>

  init(mut self) {
    self.table_name = token(Arn.of(...))
  }

  synth(self): object {
    return {
      TableName: self.table_name,
      ...
    }
  }
}

construct dynamodb.Table {
  private cfn_table: dynamodb.CfnTable
  private client: dynamodb.Client

  init(mut self, partition_key: string) {
    self.cfn_table = dynamodb.CfnTable(partition_key)
    self.client = dynamodb.Client(self.cfn_table.table_name)
  }

  add_global_secondary_index(mut self, ...) {
    self.cfn_table.global_secondary_indices.push(...)
  }

  put_item(self, ...) {
    self.client.put_item(...)
  }
}

Then, a library consumer can create a construct that listens to a queue and adds new entries to the table using an immutable method.

register_user.w
construct RegisterUser {
  init(mut self, users: dynamodb.Table) {
    queue = sqs.Queue()

    register_user = lambda.Function(process (user: object) -> {
      users.put_item({
        ID: { S: user.id },
        UserName: { S: user.name }
      })
    })

    queue.consume_messages(register_user)
  }
}

This will get compiled to the following intermediate wing code using compiler analysis to figure out which objects need to be initialized and what dead code to be removed.

register_user.inter.w
/* no change */
construct dynamodb.Client {
  private sdk_client: ...

  init(mut self) {
    self.sdk_client = ...
  }

  put_item(self, item) {
    self.sdk_client...
  }
}

construct dynamodb.CfnTable implements aws.CfnResource {
  public table_name: token

  /* different than source */
  init(mut self) {
    self.table_name = <complied token>
  }
}

/* no change, besides removing add_global_secondary_index */
class dynamodb.Table {
  private cfn_table: dynamodb.CfnTable
  private client: dynamodb.Client

  init(mut self, partition_key: string) {
    self.cfn_table = dynamodb.CfnTable(partition_key)
    self.client = dynamodb.Client()
  }

  put_item(self, ...) {
    self.client.put_item({
      TableName: self.cfn_table.table_name
      ...
    })
  }
}

/* argument generated from user-supplied function */
fun main(user: object) {
  /* compiled init */
  users = Table()
  /* end compiled init */

  /* user-supplied function */
  users.put_item({ ID: { S: user.id }, UserName: { S: user.name } })
  /* end user-supplied function */
}

Notably, the following will fail to compile:

register_user_bad.w
construct AddIndex {
  init(mut self, users: dynamodb.Table) {
    queue = sqs.Queue()

    add_index = lambda.Function(process (id: string) -> {
      users.add_global_secondary_index(string)
    })

    queue.consume_messages(add_index)
  }
}
compiler output
error: cannot compile closure that references a mutable object
 |    add_index = lambda.Function(process (id: string) -> {
 |      users.add_global_secondary_index(string)
 |            ^^^^^^^^^^^^^^^^^^^^^^^^^^ `add_global_secondary_index` requires `users` to be mutable but it is immutable in this closure

Furthermore, immutable methods can be invoked in the "initial" computation environment. This goes along with the idea that initialization of some construct includes two main components: resource allocation and data injection, regardless of whether allocation means asking the OS for some memory or telling AWS to create a new resource.

(This entire idea will require some work around how we reference computation environments and which properties to capture automatically but figure that can wait until we decide this is the right direction to go.)

Originally posted by @BenChaimberg in https://github.com/monadahq/rfcs/pull/4#discussion_r918012091

Monorepo setup

rfcs/
docs/
libs/ (To be consumed by other code)
  tree-sitter-wing/
  wingc/
  wingii/
  wing-sdk/
apps/ (To be consumed by humans, either directly or by proxy)
  wing/
  wingrt/
  wing-language-server/
  vscode-wing/

Formalize compiler's error reporting mechanism

Following the convo here: #3 (comment)

We need to formalize an error reporting mechanic for wingc. It currently just panics and crashes.

I propose en enum based approach with string representations. Pseudo code:

enum Errors {
  ERROR_WHATEVER1: 'compiler failed due to whatever1',
  ERROR_WHATEVER2: 'compiler failed due to whatever1'
}

The error reporting mechanism then need to export APIs for runtime to consume.

Ruby runtime support

Ruby is almost implemented in 4e6f48c
require("wasi") is not resolved though. Seems like a bug in the integration layer.

Mechanism for explicitly qualifying lifting

Workaround

Since it is possible to lift inflight closures (because a closure's "operation" is always "call me"), then, it is possible to work around the limitation we currently have by wrapping the operation in a closure and lifting this closure.

In the following example, selectBucket returns a cloud.Bucket and then the .put() operation cannot be qualified.

bring cloud;

let b1 = new cloud.Bucket() as "b1";
let b2 = new cloud.Bucket() as "b2";

let selectBucket = inflight (i: num) => {
  if i % 2 == 0 {
    return b1;
  } else {
    return b2;
  }
};

new cloud.Function(inflight () => {
  let bucket = selectBucket(1);
  bucket.put("hello.txt", "world");
// ^^^^ can't qualify
});

To overcome this limitation, we can change selectBucket to actually perform the operation:

bring cloud;

let b1 = new cloud.Bucket() as "b1";
let b2 = new cloud.Bucket() as "b2";

let putBucket = inflight (i: num, k: str, v: str) => {
  if i % 2 == 0 {
    b1.put(k, v);
  } else {
    b2.put(k, v);
  }
};

new cloud.Function(inflight () => {
  putBucket(1, "hello.txt", "world");
});

We need a syntax and implementation for the user to explicitly define rules describing how inflight code is going to use a resource.

The rules can be passed to the resource's capture implementation.

The most basic rule is obviously what inflight methods of the resource are being used. This can be used to set up permissions, for example. But we should also keep in mind that there might be information we want to pass describing possible ranges of arguments to these methods or regexes for valid argument values etc.

Example:

let b = cloud.Bucket();
cloud.Function((event) ~> { 
   // We should have a way to explicitly allow only write access to `b` and only access to the `"filename.dat"` key in `b`.
  b.upload("filename.dat", event.data);
});

In the future we should also have a way to automatically generate these rules based on the code.

Windows runtime support

This should not be TOO bad. Go's cshared pattern seems cross platform. Node itself is cross platform. Our language support is mostly through Node and WASI. So that's portable as well. JNI and Mono are also portable.

Worst case scenario, port can be done in stages.

VSCode plugin

  • Adds languages support for wing
  • Bundles the wing language server binary (for all platforms)
  • Has CI to create a VSIX artifact in a github release
  • Notifies the user of a new version of the VSIX

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.