{"id":16092265,"url":"https://github.com/alecthomas/langx","last_synced_at":"2026-03-06T06:31:14.865Z","repository":{"id":66600209,"uuid":"227677308","full_name":"alecthomas/langx","owner":"alecthomas","description":"Language experimentation.","archived":false,"fork":false,"pushed_at":"2026-02-05T04:56:56.000Z","size":125,"stargazers_count":23,"open_issues_count":3,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-05T17:36:38.007Z","etag":null,"topics":["compiler-design","parser","semantic-analysis","type-analysis"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alecthomas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["alecthomas"]}},"created_at":"2019-12-12T19:05:23.000Z","updated_at":"2026-02-02T17:57:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"38369aba-7d95-4843-b215-e356494e2fa3","html_url":"https://github.com/alecthomas/langx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alecthomas/langx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecthomas%2Flangx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecthomas%2Flangx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecthomas%2Flangx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecthomas%2Flangx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alecthomas","download_url":"https://codeload.github.com/alecthomas/langx/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecthomas%2Flangx/sbom","scorecard":{"id":178773,"data":{"date":"2025-08-11","repo":{"name":"github.com/alecthomas/langx","commit":"e6cb95f8f09389a0dfbaa4818b5f40ddc0e92aff"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Info: Possibly incomplete results: error parsing shell code: parameter expansion requires a literal: bin/activate-hermit:0","Warn: downloadThenRun not pinned by hash: bin/hermit:23","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T18:23:27.444Z","repository_id":66600209,"created_at":"2025-08-16T18:23:27.444Z","updated_at":"2025-08-16T18:23:27.444Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30164590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T04:43:31.446Z","status":"ssl_error","status_checked_at":"2026-03-06T04:40:30.133Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler-design","parser","semantic-analysis","type-analysis"],"created_at":"2024-10-09T16:06:26.920Z","updated_at":"2026-03-06T06:31:14.829Z","avatar_url":"https://github.com/alecthomas.png","language":"Go","funding_links":["https://github.com/sponsors/alecthomas"],"categories":[],"sub_categories":[],"readme":"# Language Experiment\n\nThis is a playground for PL ideas. My goal is to have a safe elegant language while maintaining simplicity. This is\nclearly subjective.\n\n- Sum types.\n- Static typing.\n- Concurrent safety.\n- ...\n\n## First-class support for deploying\n\nWhat does this mean?\n\n- Builtin support for bundling resources, hot reload during development.\n- Automatic Docker builds?\n\n## Concurrency options\n\nThe appeal of Go's concurrency model is that any normal synchronous function can be called asynchronously. The downside\nof its model is that there are no guarantees around concurrent access - each type must manage its own synchronisation\nmanually. How do we solve this?\n\n### Automatic ownership management\n\nLike Rust, but without the boilerplate. Similar to [D](https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in-d/).\n\nRules:\n\n1. Any value passed to an asynchronous construct will have its ownership transferred.\n2. The `copy` operator creates a clone of a value.\n\neg.\n\n```\ninterface Component {\n    fn kind(): int\n}\n\nclass ECS {\n    let components = new {int: [Component|None]}\n\n    pub fn assign\u003cC\u003e(id: int, component: C) {\n        let kind = component.kind()\n        for components[kind].len() \u003c= id {\n            components[kind].append(None)\n        }\n        components[kind][id] = component\n    }\n\n    pub fn each\u003cC...\u003e(iter fn(id:int))) {\n    }\n}\n```\n\n### [Active Object](https://en.wikipedia.org/wiki/Active_object)\n\nConcept: concurrent-safe facades for synchronous objects are automatically generated by the compiler.\n\nBoth types and values can be marked as `async`.\n\n```\nasync class Processor { }\n\nlet a = new Processor()\n\nclass Vector {}\n\nlet a = new Vector()\nlet b = async a\n```\n\nIf public fields are allowed this basically necessitates getters/setters, at least at the compiler level. Perhaps public\nfields should be forbidden for asnyc values?\n\nNon-async values are equivalent to `unique_ptr\u003cT\u003e` in C++.\n`async` values may have multiple concurrent references. There is no equivalent in C++.\n\n#### Example\n\n```\nasync class Redis {\n    let count = 0\n    let values: {string: Value}\n\n    pub fn set(key: string, value: Value) {\n        count++\n        values[key] = value\n    }\n\n    pub fn get(key: string): Value? {\n        return values[key]\n    }\n}\n```\n\nMight result in the following:\n\n```\nasync class Redis {\n    let count = 0\n    let values: {string: Value}\n    let _count_lock: RWLock\n    let _values_lock: RWLock\n\n    pub fn set(key: string, value: Value) {\n        with _count_lock.acquire_rw(), _values_lock.acquire_rw() {\n            count++\n            values[key] = value\n        }\n    }\n\n    pub fn get(key: string): Value? {\n        with _values_lock.acquire_ro() {\n            return values[key]\n        }\n    }\n}\n```\n\n### Actors?\n\n- Actor methods cannot return values.\n- Invoking any method (including the constructor) on an actor enqueues the call on its mailbox.\n- All messages to the actor are applied synchronously.\n- Actors cannot contain public fields.\n- Any value passed into an actor will transfer ownership to the actor.\n- If an actor named \"main\" exists it will be the main entry point rather than the \"main\" function.\n\n#### Actor functions?\n\n```\nactor fn poll(msg: string) {\n   print(msg)\n}\n\nlet p = start poll\np(\"hello\")\n```\n\nBit ugly?\n\n#### Normal actors\n\n```\nactor Owner {\n    let pet: Pet?\n\n    init(pet: Pet?) {\n        self.adopt(pet)\n    }\n\n    fn adopt(pet: Pet) {\n        self.pet = pet\n        feed(\"seed\")\n    }\n\n    fn feed(food: string) {\n        if let pet = pet {\n            pet.feed(food)\n        }\n    }\n}\n\nactor main {\n    init() {\n        let pet = Pet(\"Lucius\")\n        //let pets = [pet]\n        // Start Actor.\n        //let owner = Owner(pets[0])   // Error: can't transfer ownership of an element.\n        let owner = start Owner(pet)   // Create and start the actor.\n        //pet.feed(\"moo\")              // Error: pet is owned by \"owner\"\n\n        // Send some messages.\n        owner.adopt(pet)\n        owner.feed(\"seed\")\n\n        kill(owner)\n    }\n}\n```\n\n[Akka example](https://alvinalexander.com/scala/how-to-communicate-send-messages-scala-akka-actors):\n\n```scala\nimport akka.actor._\n\ncase object PingMessage\ncase object PongMessage\ncase object StartMessage\ncase object StopMessage\n\nclass Ping(pong: ActorRef) extends Actor {\n    var count = 0\n    def incrementAndPrint { count += 1; println(\"ping\") }\n    def receive = {\n        case StartMessage =\u003e\n            incrementAndPrint\n            pong ! PingMessage\n        case PongMessage =\u003e\n            incrementAndPrint\n            if (count \u003e 99) {\n                sender ! StopMessage\n                println(\"ping stopped\")\n                context.stop(self)\n            } else {\n                sender ! PingMessage\n            }\n        case _ =\u003e println(\"Ping got something unexpected.\")\n    }\n}\n\nclass Pong extends Actor {\n    def receive = {\n      case PingMessage =\u003e\n          println(\" pong\")\n          sender ! PongMessage\n      case StopMessage =\u003e\n          println(\"pong stopped\")\n          context.stop(self)\n      case _ =\u003e println(\"Pong got something unexpected.\")\n    }\n}\n\nobject PingPongTest extends App {\n    val system = ActorSystem(\"PingPongSystem\")\n    val pong = system.actorOf(Props[Pong], name = \"pong\")\n    val ping = system.actorOf(Props(new Ping(pong)), name = \"ping\")\n    // start the action\n    ping ! StartMessage\n    // commented-out so you can see all the output\n    //system.shutdown\n}\n```\n\nIn langx:\n\n```\n// Actor interfaces may only be applied to actors.\nactor interface Pinger {\n  fn ping(ping: Pinger)\n}\n\nactor Ping: Pinger {\n    let count = 0\n\n    pub fn start(pong: Pong) {\n        incrementAndPrint()\n        pong.ping(self)\n    }\n\n    pub fn ping(ping: Pinger) {\n        incrementAndPrint()\n        if count \u003e 99 {\n            println(\"ping stop\")\n            ping.stop()\n            kill(self)\n        } else {\n            ping.ping(self)\n        }\n    }\n\n    fn incrementAndPrint() {\n        count++\n        println(\"ping\")\n    }\n}\n\nactor Pong: Pinger {\n    pub fn ping(ping: Pinger) {\n        println(\"pong\")\n        ping.ping(self)\n    }\n\n    pub fn stop() {\n        println(\"pong stop\")\n        kill(self)\n    }\n}\n\nfn main() {\n    let ping = Ping()\n    let pong = Pong()\n    ping.start(pong)\n}\n\n```\n\n## Classes\n\n```\npub class Vector: Stringer {\n    // All fields are given default values. One difference from Go is that\n    // arrays, maps, and classes are given default-constructor values.\n    let x, y, z : float32\n\n    // A default constructor is always provided for all public fields.\n    // In this case it would be equivalent to:\n    //\n    //     constructor(x:float = 0, y:float = 0, z:float = 0)\n\n    pub fn length():float { // Pure.\n        return Math.sqrt(x * x + y * y + z * z)\n    }\n\n    pub fn add(other:Vector) { // Impure.\n        x += other.x\n        y += other.y\n        z += other.z\n    }\n\n    // \"override\" can be specified when implementing traits to ensure that changes to the\n    // interface don't result in methods beign orphaned.\n    override pub fn string(): string {\n        return \"Vector({x}, {y}, {z})\"\n    }\n}\n```\n\n## Generics\n\n```\nclass Stack\u003cT\u003e: Iterable\u003cT\u003e {\n    let stack: [T]   // Backed by an array.\n\n    pub fn push(v: T) {\n        stack.append(v)\n    }\n\n    // Implements Iterable\u003cT\u003e\n    override pub fn iterator(): Iterator\u003cT\u003e {\n        return stack.iterator()\n    }\n}\n```\n\nGeneric functions:\n\n```\nfn map\u003cT, U\u003e(l: [T], f fn(v T):U): [U] {\n    let out: [U]\n    for v in l {\n        out.append(f(v))\n    }\n    return out\n}\n\nlet ints = [1, 2, 3]\nlet floats = map(ints, fn(v int) float {\n    return float(v)\n})\n```\n\n## Arrays\n\n```\nlet a: [string]         // Explicitly typed.\nlet a = [\"hello\"]       // Type inference.\n```\n\n## Maps\n\n```\nlet a: {string: Vector}              // Explicitly typed.\nlet b = {\"hello\": Vector(x:1, y:2, z:3)}   // Type inference.\n```\n\n## Sets\n\n```\nlet a: {string}         // Explicitly typed.\nlet a = {\"hello\"}        // Type inference.\n```\n\n## Range\n\nSyntactic sugar for a range type? Used for slices, for loops, etc.\n\n```\n// Equivalent.\nlet a = 1..3\nlet a = new Range\u003cint\u003e(1, 3)\n\nlet b = [1, 2, 3, 4, 5, 6]\n\n// Equivalent.\nlet c = b[1..3] \nlet c = b[a]\n\nfor n in 1..10 {\n}\n```\n\n## Type aliases?\n\nCreates an alias for an existing type, with its own set of methods etc.\n\n```\nalias Number float {\n    // TODO: Constraints?\n    constraint self \u003e= 1 \u0026\u0026 self \u003c= 10\n\n    fn midpoint() float {\n        return this / 2.0\n    }\n}\n\n```\n\n## Channels?\n\nChannels should be used to pass values between threads.\n\n```\nlet a = new chan\u003cVector32\u003e()\n\nlet v = Vector32{1, 2, 3}\nv.x = 2   // Mutate\na \u003c- v    // Copy\n```\n\n## Interfaces\n\nStructural typing and traditional interfaces are complementary in that the former is more consumer-centric, while the\nlatter is more provider-centric. To that end, langx interfaces support both.\n\nInterfaces are fairly straightforward:\n\n```\ninterface Pet {\n    // Method with a default implementation. Can still be overridden.\n    fn description(): string {\n        return \"{name} is {age} years old\"\n    }\n\n    // Methods without implementations must be provided.\n    fn mood(): string\n\n    // Fields without a default value must be provided by implementations.\n    let name: string\n    let age: int\n}\n```\n\nStructural typing usage:\n\n```\nclass Dog {\n    pub fn mood(): string {\n        return \"happy\n    }\n\n    pub let name: string\n    pub let age: int\n}\n\nlet dog: Pet = Dog(name: \"Fido\", age: 8)\n```\n\n```\nclass Dog: Pet {\n    pub fn mood(): string {\n        return \"happy\n    }\n\n    pub let name: string\n    pub let age: int\n}\n```\n\n## Constructors\n\nConstructor arguments are *always* named? Positional arguments are not supported?\n\n```\nclass Vector {\n    let x, y, z: float\n\n    // Static factory method.\n    static fn unit(): Vector {\n        return Vector(y: 1)\n    }\n}\n\nfn f() {\n    // A default constructor is generated if not otherwise provided.\n    let a = Vector(x: 1)\n}\n```\n\n## Sum types / enums\n\nVery similar to Swift.\n\n```\nenum Result\u003cT\u003e {\n    case Value(T)\n    case Error(error)\n}\n\nenum Optional\u003cT\u003e {\n    case None\n    case Value\u003cT\u003e\n}\n\nlet result = Result.Value(\"hello world\")\n\nswitch (result) {\ncase .Value(value):\ncase .Error(err):\n}\n```\n\nThe default value for an enum is the first case, only if it is untyped. If all cases are typed (eg. `Result\u003cT\u003e` above)\nthen there is no possible default value.\n\nSupport for anonymously combining types into enums:\n\n```\nenum Option\u003cT\u003e {\n    case Value(T)\n    case None\n}\n\n// This will merge the Option\u003cT\u003e with error to create a single enum:\n//\n// enum Anonymous {\n//   case Value(string)\n//   case None\n//   case error\n// }\n//\n// Do we want this vs. the Option becoming a first class case?\n//\n// Upside is you can return any literal that can be inferred, downside is you can't\n// convert to an Option.\nfn f(): Option\u003cstring\u003e|error {\n    return \"hello\"\n}\n```\n\n## Pattern matching\n\n```\nlet tuples = [(\"a\", 123), (\"b\", 234)]\n\nfor tuple in tuples {\n    match tuple {\n    case (\"a\", n):\n        println(\"a #{n}\")\n\n    default:\n        println(tuple[0], tuple[1])\n    }\n}\n```\n\n## For loop\n\n```\nfor value in array {\n}\n\nfor (index, value) in array {\n}\n\nfor key in map {\n}\n\nfor (key, value) in map {\n}\n\nfor value in set {\n}\n\nfor value in channel {\n}\n```\n\n## Error handling?\n\n```\nenum MyError: Error {\n    case IOError(io.Error)\n    case UserError(string)\n    case SomeFailure\n}\n\nfn sub(): string throws {\n    return \"\"\n}\n\nclass UserError: Error {\n    let msg: string\n    \n    override fn error(): string { return msg }\n}\n\nfn function(): io.Error|string {\n    if false {\n        return .UserError(\"something is false\")\n    }\n    let a = try sub() // Rethrow\n    if let a = try sub() {\n    }\n    return \"hello\"\n}\n```\n\n## Native optional type\n\n```\n// Builtin type definition.\nenum Optional\u003cT\u003e {\n    case None\n    case Some\u003cT\u003e\n}\n\nlet a = Optional.Some(\"hello world\")\n\nfn f() {\n    // \"if let\" is shorthand for:\n    //\n    //      switch a {\n    //      case .Some(b):\n    //      default:\n    //      }\n    if let b = a {\n    } else {\n    }\n\n    a = none\n    // Shorthand for: a = Optional\u003cstring\u003e.None\n    a = \"goodbye\"\n    // Shorthand for: a = Optional\u003cstring\u003e.Some(\"goodbye\")\n}\n```\n\n## Imports\n\nLike Go? Automatic imports?\n\nPython style?\n\n```\nimport \u003cpackage\u003e[ as \u003calias\u003e][, ...]\nfrom \u003cpackage\u003e import \u003csymbol\u003e[as \u003calias\u003e][, ...]\n```\n\neg.\n\n```\nimport \"github.com/alecthomas/participle\" as parser\nfrom \"github.com/alecthomas/participle\" import Parser\n```\n\nOr Go style only?\n\n```\nimport \u003cpackage\u003e[ as \u003calias\u003e][, ...]\n```\n\neg.\n\n```\nimport \"github.com/alecthomas/participle\" as parser\n```\n\n## Annotations?\n\nAccessible via reflection. Mmmmmmmmmmm. Avoid for now, though there needs to be some solution for eg. JSON encoding.\nWriting manual encoders/decoders sucks.\n\n```\nimport \"types\"\nfrom \"types\" import annotation\n\n// Lets the compiler know that this class is intended to be an annotation.\n@annotation(restrict=[types.Class, types.Field, types.Method])\nclass json {\n    let omit:bool = false\n}\n\nclass User {\n    let name:string\n\n    let email:string\n\n    let age:int\n\n    @json(omit=true)\n    let ssn:string\n}\n```\n\n## Compile time reflection\n\nAla [Zig](https://ziglang.org/#Compile-time-reflection-and-compile-time-code-execution). This is great. I haven't given\nthis much though, so I'm not sure what it would entail. An interpreter maybe?\n\nThis would actually be fairly straightforward to implement with WASM as the backend. The compile-time code outputs WASM\nwhich is then interpreted by the compiler to generate code/AST that is compiled again.\n\n## Interoperability with Go/C?\n\nIf the language is hosted by the Go runtime, should it support interoperability with Go? Or C?\n\nPros:\n\n- large set of existing libraries\n\nCons:\n\n- how does immutability interoperate with Go?\n- limits the language to constructs supported by the Go runtime\n\n## Resource lifetimes\n\nFor objects implementing `io.Closer`, `with` will close them at the end of the block.\n\n```\nwith try f = os.open(\"/etc/passwd\") {\n}\n```\n\nAs with `if try`, `with` blocks can have `catch` and `rethrow` alternates.\n\n## Error handling\n\nErrors are reported via enums:\n\n```\nfn open(path: string): File|error {\n    return error(\"{path} not found\")\n}\n```\n\nHow do we handle these elegantly?\n\n### Rust-style re-throw operator `?`?\n\n```\nlet f = os.Open(\"/etc/passwd\")?\n```\n\nVery convenient, but too magical?\n\n### Error-specific `try` syntax in `if` and `with` blocks?\n\n```\nif|with try [\u003cvar\u003e = ] \u003cexpr\u003e \u003cblock\u003e\ncatch [[\u003cvar\u003e:]\u003ctype\u003e] \u003cblock\u003e\nrethrow\n```\n\n```\nwith try file = os.open(\"/etc/passwd\") {\n    let scanner = new bufio.Scanner(file)\n    for scanner.scan() {\n        println(scanner.text())\n    }\n    return scanner.err()\n} catch os.ErrNotExist {\n    return\n} rethrow\n\nwith try file = os.open(\"/etc/passwd\") {\n}\n\nif try os.stat(\"/etc/passwd\") {\n} catch {\n}\n```\n\n```rust\nuse std::io;\nuse std::fs;\n\nfn read_username_from_file() -\u003e Result\u003cString, io::Error\u003e {\n    fs::read_to_string(\"hello.txt\")\n}\n```\n\n```\nimport ioutil\n\nfn readUsernameFromFile(): bytes|error {\n    return ioutil.readFile(\"hello.txt\")\n}\n\n// \u003ctype\u003e? is a shortcut for \u003ctype\u003e|none\nfn username(): string? {\n    return \"bob\"\n}\n```\n\n## Examples\n\n### Templated Enum\n\n```\nenum Option\u003cT\u003e {\n    case Some(T)\n    case None\n}\n\nlet a : Option\u003cstring\u003e = \"hello\"\nlet b : Option\u003cString\u003e = none\n```\n\n### Entity Component System\n\n```\nclass Vector {\n    let x, y, z : float\n}\n\nclass Base {\n    let position, direction : Vector\n    let opacity : float\n}\n\nclass Script {\n    let source : string\n}\n\nenum Component {\n    case Base(Base)\n    case Script(Script)\n\n    let slot() : int {\n        switch self {\n        case Base(_): return 0\n        case Script(_): return 1\n        }\n    }\n}\n\nclass ECS {\n    let free : [int]\n    let entities : [[Component?]]\n\n    // Create a new Entity.\n    fn create() : int {\n        if (free.size() \u003e 0) {\n            return free.pop()\n        }\n        let id = entities.size()\n        entities.append([])\n        return id\n    }\n\n    fn delete(id : int) {\n        free.push(id)\n        entities[id] = []\n    }\n\n    fn assign(id : int, component : Component) {\n        let components = entities[id]\n        let slot = component.slot()\n\n        if (slot \u003e= components.size()) {\n            components.resize(slot + 1)\n        }\n        components[slot] = component\n    }\n\n    fn unassign(id : int, component : Component) {\n        entities[id][component.slot()] = none\n    }\n}\n```\n\n## Redis\n\n```go\ntype Scalar interface { scalar() }\ntype Float float64\nfunc (Float) scalar() {}\ntype String string\nfunc (String) scalar() {}\ntype Bool bool\nfunc (Bool) scalar() {}\n\ntype Value struct {\n  Scalar Scalar\n  List []Scalar\n  Hash map[string]Scalar\n}\n```\n\n```\ntype Scalar = float|string|bool\n\nenum Value {\ncase Scalar(Scalar)\ncase List([Scalar])\ncase Hash({string: Scalar})\n}\n\nclass Redis {\n    let values: {string: Value}\n\n    fn len(key: string): int? {\n        let value = values[key] else return\n        switch value {\n        case .List(list):\n            return list.size()\n\n        case .Hash(hash):\n            return hash.size()\n\n        case .Scalar(scalar):\n            if let str = scalar as .string {\n                return str.size()\n            }\n        }\n    }\n\n    // Append one or more elements to a list.\n    fn rpush(key: string, scalar: Scalar): error?\n        if let value = values.setDefault(key, .List([])) {\n            list.append(scalar)\n        } else {\n            return error(\"expected a list at {key}\")\n        }\n    }\n\n    // Length of a list.\n    fn llen(key: string): int? {\n        if let value = values[key]; list = value as .List {\n            return list.size()\n        }\n    }\n\n    fn lindex(key: string, index: int): Scalar? {\n        if let value = values[key]; list = value as .List {\n            return list[index]\n        }\n    }\n\n    // Set a field of a hash value.\n    fn hset(key: string, field: string, value: Scalar): error? {\n        if let value = values.setDefault(key, .Hash({})) {\n            hash[field] = value\n        } else {\n            return error(\"expected a hash at {key}\")\n        }\n    }\n\n    fn hget(key: string, field: string): Scalar? {\n        if let value = values[key]; hash = value as .Hash {\n            return hash[field]\n        }\n    }\n\n    fn hlen(key: string): int? {\n        if let value = values[key]; hash = value as .Hash {\n            return hash.size()\n        }\n    }\n\n    fn del(key: string) {\n        values.delete(key)\n    }\n\n    fn exists(key: string): bool {\n        return values.contains(key)\n    }\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecthomas%2Flangx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falecthomas%2Flangx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecthomas%2Flangx/lists"}