{"slug": "sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation", "title": "Sake-lang: Experimental language based on Ruby but with types for each operation", "summary": "Ko1 released Sake, an experimental programming language that keeps Ruby's syntax but requires a type on every operation rather than on variables, parameters or fields, with whole-program type inference and pre-run name checking. The project's stated motivation — making a language easy for AI to write — was not supported by its evaluation on 2026-10-08, and development continues at a small scale; the repository ships 59 example programs across ten categories, a roughly 10k-token cheatsheet for language models, and a Sinatra todo app and Rails-style blog port as web experiments.", "body_md": "**Sake** (/seɪk/, as in \"for the sake of\") is an experimental programming language. It is designed\nfor the sake of finding a new place for types. It keeps **Ruby's syntax**, but you write the type\non each **operation**, never on a variable, a parameter, or a field:\n\n```\nString.upcase(name)        # Sake\nname.upcase                # Ruby: an error in Sake, reported with the fix above\n```\n\nBecause every operation names its type:\n\n- **One line is enough to read most calls.** For most calls, a line tells you which function runs,\nwithout looking up declarations. A module function`M.f(x)` , a type list`(A|B).f(x)` and an\noperator choose by the first argument's type, from a set of candidates fixed before running.\n- **Names are checked before running.** A misspelled operation or type is reported before the\nprogram starts.\n- **Types are inferred, never written.** The whole-program inference catches values that cannot\nfit an operation, and the strictness level chooses how much of that stops a run.\n- **Run-time checks remain.** Every operation still checks its arguments while running, so what\nthe checks before running miss still stops at the exact operation.\n\nThe motivation was to make a language that is easy for AI to write. **The evaluation did not\nsupport it (2026-10-08); development continues at a small scale**, from what using the language turns\nup. See [Status](#status).\n\n**Writing Sake with a language model?** Give it [docs/cheatsheet.md](https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md): the\nrules on four pages plus the compact list of operations, about 10k tokens, and all a model needs\n(raw: [https://raw.githubusercontent.com/ko1/sake-lang/main/docs/cheatsheet.md](https://raw.githubusercontent.com/ko1/sake-lang/main/docs/cheatsheet.md)). The full\nreference manual and the playground are at [https://ko1.github.io/sake-lang/](https://ko1.github.io/sake-lang/).\n\n``` python\nclass Point\n  attr_reader x, y\n  def norm2(p) = @x * @x + @y * @y        # @x is Point.x(p): p is the first argument\nend\n\nwords = String.split(\"the cat and the hat\", \" \")\ncounts = Hash.new(0)\nArray.each(words) { |w| counts[w] += 1 }\np(counts)\nputs(Point.norm2(Point.new(3, 4)))\njs\n$ bin/sake readme.sake\n{\"the\" => 2, \"cat\" => 1, \"and\" => 1, \"hat\" => 1}\n25\n```\n\nMistakes are reported before anything runs, each with a fix:\n\n```\nname = \"sake\"\nputs(name.upcase)\nputs(String.upcse(name))\nbash\n$ bin/sake mistakes.sake\nmistakes.sake:2:11: error: method call on a value `name.upcase` is not allowed\n  hint: String.upcase(name)\n  hint: Symbol.upcase(name)\nmistakes.sake:3:13: error: undefined function `String.upcse`\n  hint: did you mean `String.upcase`?\n```\n\n- [examples/](https://github.com/ko1/sake-lang/blob/main/examples/README.md) : 59 programs in ten categories, each with the output it prints:[text](https://github.com/ko1/sake-lang/blob/main/examples/text) ,[numbers](https://github.com/ko1/sake-lang/blob/main/examples/numbers) ,[algorithms](https://github.com/ko1/sake-lang/blob/main/examples/algorithms) ,[data structures](https://github.com/ko1/sake-lang/blob/main/examples/data-structures) ,[data](https://github.com/ko1/sake-lang/blob/main/examples/data) ,[grids and games](https://github.com/ko1/sake-lang/blob/main/examples/grids-and-games) ,[parsers and codecs](https://github.com/ko1/sake-lang/blob/main/examples/parsers-and-codecs) ,[simulation](https://github.com/ko1/sake-lang/blob/main/examples/simulation) ,[types and errors](https://github.com/ko1/sake-lang/blob/main/examples/types-and-errors) and[apps](https://github.com/ko1/sake-lang/blob/main/examples/apps) .[first.sake](https://github.com/ko1/sake-lang/blob/main/examples/first.sake) is the smallest tour, and the SQL engine in[apps/sql/](https://github.com/ko1/sake-lang/blob/main/examples/apps/sql/README.md) the largest program. The index says what each one does and\nwhich features it shows.\n- Web: a todo app on the [Sinatra](https://github.com/ko1/sake-lang/blob/main/sakelib/sinatra.sake) port\n([experiments/2026-10-12-web/sinatra/](https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-12-web/sinatra/README.md) , with the same app\nin Ruby next to it), and a blog with Rails' routes, controllers, views and models\n([experiments/2026-10-12-web/rails/](https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-12-web/rails) ). Both serve requests\nover a real socket (WEBrick).\n- [sakelib/](https://github.com/ko1/sake-lang/blob/main/sakelib) : Ruby's libraries and gems ported to Sake (JSON, CSV, ERB, Rack, Sinatra,\nminitest, and more). Each one's test,[test/sakelib/](https://github.com/ko1/sake-lang/blob/main/test/sakelib) , prints what the same program\nprints with the Ruby library;[https://ko1.github.io/sake-lang/compare.html](https://ko1.github.io/sake-lang/compare.html) shows the two side by side.\n- [test/samples/](https://github.com/ko1/sake-lang/blob/main/test/samples) : about 200 small programs, mostly one feature each, with their\nexpected output, including the error messages.\n- [test/native/](https://github.com/ko1/sake-lang/blob/main/test/native) : the programs that`bin/sabic` and`bin/ceec` compile to native code.\n\nThe interpreter is written in Ruby, parses with [Prism](https://github.com/ruby/prism), and walks\nthe AST. It needs **Ruby 4.0** (tested with 4.0.2 and Prism 1.9.0) and has no other dependencies.\n\n```\nbin/sake FILE.sake              # check, then run\nbin/sake -c FILE.sake           # check only\nbin/sake --strict FILE.sake     # check more strictly before running (level 2)\nbin/sake --strict=3 FILE.sake   # levels 0-4, or items: --strict=type,nil\nbin/sake --types FILE.sake      # experimental: print the inferred types\nbin/sake --dump=ast FILE.sake   # print the SakeAST (the resolved program that runs)\n```\n\nThe exit status is 0 on success, 1 for an error while running, and 2 for a problem found before running.\n\n`bin/sabic` compiles a program to a native executable by way of Rust, for the subset whose types\nthe generator can fix: Integer (as 64-bit, an overflow is an error), Float, true/false, nil as\n`Option`, String, Array, Tuple, classes, blocks, unions of classes as a Rust `enum` with mixin calls\nas a `match`, and `case`/` in` on values and types. Anything outside the subset is reported with its\nline, and the program still runs on the interpreter. A program that the checks of `bin/sake` stop at its\ndefault level is not compiled either.\n\n```\nbin/sabic FILE.sake            # writes FILE.rs and builds FILE with rustc -O\nbin/sabic FILE.sake --emit     # only the Rust source\n```\n\nOn three micro benchmarks (loops, fib, levenshtein) the compiled program ran 3 to 21 times faster than\nRuby 4.0 with YJIT, 8 to 53 times faster than Ruby without it, and within 0.9 to 1.6 times of Rust\nwritten by hand ([experiments/2026-10-10-rust-backend/](https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-10-rust-backend/README.md)).\nA mixin call dispatched on the argument's type costs 0.12 ns, about the same as a hand-written `enum`\nand an eighth of Rust's `dyn Trait`. Against [Spinel](https://github.com/matz/spinel), the Ruby AOT\ncompiler, the same programs ran at the same speed on loops and levenshtein and 8 times faster on\nthe dispatch benchmark, with an analyzer of 1,100 lines of Ruby where Spinel's is 107,000 lines of C,\nbecause Sake's rules do that work in the language. `bin/ceec` does the same through C, on the same\ntype inference, and runs at the same speed as the Rust output; against hand-written C, the remaining\ngap is the overflow check on every Integer operation (fib: 0.186 s with it, 0.065 s without, hand C 0.064). The interpreter is 150 to 760 times slower than\nRuby with YJIT.\n\n- [docs/tutorial.md](https://github.com/ko1/sake-lang/blob/main/docs/tutorial.md) : a tour with small programs. Every output in it was produced\nby running the program.\n- [docs/spec.md](https://github.com/ko1/sake-lang/blob/main/docs/spec.md) : the language as implemented.\n- [docs/builtins.md](https://github.com/ko1/sake-lang/blob/main/docs/builtins.md) : all built-in operations, generated from the interpreter.\n- [docs/cheatsheet.md](https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md) : the rules on four pages plus the compact list of\noperations, about 10k tokens, for a language model (or a Ruby programmer) writing Sake. Its\nexamples are run on every build.\n- [docs/guide.html](https://github.com/ko1/sake-lang/blob/main/docs/guide.html) : the tutorial and the specification on one page. It is also\npublished at[https://claude.ai/artifact/EdrbscXRGUtkKppprRKohP](https://claude.ai/artifact/EdrbscXRGUtkKppprRKohP) .\n- [docs/manual/](https://github.com/ko1/sake-lang/blob/main/docs/manual) : the reference manual in Japanese and English, a[ligarb](https://github.com/ko1/ligarb) book (`docs/manual/build.sh` builds`docs/manual/build/index.html` ). Part 2 documents every built-in\noperation, one chapter per type, with examples that`ruby tools/check_reference.rb` runs and compares.\nGitHub Pages serves the built book at[https://ko1.github.io/sake-lang/manual/](https://ko1.github.io/sake-lang/manual/) , the guide at[https://ko1.github.io/sake-lang/guide.html](https://ko1.github.io/sake-lang/guide.html) and the playground at[https://ko1.github.io/sake-lang/playground/](https://ko1.github.io/sake-lang/playground/) ; the front page[https://ko1.github.io/sake-lang/](https://ko1.github.io/sake-lang/) is\nthis README (`.github/workflows/pages.yml` and`tools/gen_site.rb` , no Jekyll).\n- [https://ko1.github.io/sake-lang/compare.html](https://ko1.github.io/sake-lang/compare.html) : Ruby and Sake side by side. Each library port's test program\nwritten with Ruby's own library next to the same program in Sake (they print the same output), and the port\n(`tools/gen_compare.rb` ).\n- [ide/](https://github.com/ko1/sake-lang/blob/main/ide/README.md) : the playground, a browser IDE on ruby.wasm with completion, diagnostics as\nyou type, and the inferred types on hover.\n- [DESIGN.md](https://github.com/ko1/sake-lang/blob/main/DESIGN.md) : the design notes, with the reasons behind each decision (in Japanese).\n- [docs/comparison.md](https://github.com/ko1/sake-lang/blob/main/docs/comparison.md) : what is new in Sake and what is not, axis by axis against\nElm, Crystal, Rust, Elixir, TypeScript, Clojure and the type-inference literature (in Japanese).\n\n- **Values** : Integer, Float, Rational, Complex, String, Symbol, true/false, nil, Tuple`[a, b]` ,\nRecord`{x: 1}` , Array, Hash, Set, Range, Regexp, and Time.\n- **Named types** :`class C` with`attr_reader` /`attr_accessor` lines for its fields (`Struct.new(:x, :y)` is the shorthand), and exception types`class E < Exception` .\n- **Operators** :`a + b` runs the`+` of`a` 's type, and`a[i]` its`[]` . Your own types join by\nincluding`Arithmetic` ,`Comparable` ,`Bitwise` , or`Indexable` .\n- **Chains** :`T.g(x).U.f(args)` is`U.f(T.g(x), args)` , a step without a type continues with the type\nthe chain named last (`String.strip(s).upcase` ), and`_` is the previous statement's value, so a\nsequence of operations reads in order while the type of each step is on the same line.\n- **Functions and blocks** : functions are polymorphic and take no annotations. Blocks are passed\nwith`yield` .\n- **Modules** :`module` with`include` works like Ruby's modules, resolved statically:`include M` copies M's functions. Namespaces nest (`A::B` ).`class B < A` copies A's definitions into B; there\nis no inheritance (a B is not an A).\n- **nil** : a value that may be nil has the type`nil | T` , narrowed by`if x` ,`x != nil` , and\nearly returns.\n- **Patterns** :`x in Integer` and`case x in ...` narrow types, and the checker verifies that a`case` is exhaustive.\n- **Exceptions** :`raise` ,`rescue` ,`ensure` , and`retry` , plus inference of which exceptions may\nescape a function.\n- **Built-in library** : about 550 operations, named after Ruby's core library.`Enum` (the prelude)\nis Enumerable under a short name:`Enum.map(x) { }` dispatches to Array, Hash, Set or Range, and a\nclass joins with`include Enum` and`def each` .\n\nNot yet: `case`/` when`, and first-class blocks. Built-in constants such as `Math::PI` are read as\noperations (`Math.PI`); a program has no value constants (` def pi = 3.14159` instead).\n\n```\nbin/sake                 the command\nbin/sabic                the compiler to native code through Rust (sabi, 錆, is rust)\nbin/ceec                 the compiler to native code through C\nlib/sake/                resolver (checks before running), lower (Prism AST to SakeAST), interpreter\n                         and typer (type inference), both on SakeAST, the Rust and C backends (rust.rb, c.rb)\n                         with their range analysis (ranges.rb), standard library (stdlib*.rb)\nsakelib/                 the library written in Sake: the prelude (Enum) and ports of Ruby's libraries and gems\ntest/                    golden tests (test/samples/*.sake with *.expected), CLI tests, test/native/ (programs\n                         both native backends must print like the interpreter)\nexamples/                example programs by category, each with its expected output (examples/README.md)\ndocs/                    tutorial, specification, built-in list, one-page guide, examples\ntools/                   generators for the docs (they run the examples)\nexperiments/             experiments, each with its method, results, and limits in a README\n```\n\nRun the tests with:\n\n```\nruby test/test_samples.rb       # UPDATE=1 rewrites the expected outputs\nruby test/test_cli.rb           # the command line, and whether the generated docs are up to date\nruby test/test_examples.rb      # examples/**/*.sake print their *.expected (UPDATE=1 rewrites them)\nruby test/test_sakelib.rb       # the library ports: each test/sakelib/X.sake prints what X.rb prints\nruby test/test_sake_suite.rb    # Sake's test suite written in Sake (test/sake/*_test.sake, on sakelib/minitest.sake)\nruby test/test_ide.rb           # the playground's Ruby side (lib/sake/ide.rb)\nruby test/test_rust.rb          # the Rust backend: test/native/*.sake compiled and compared with the interpreter (needs rustc)\nruby test/test_c.rb             # the C backend: the same programs through bin/ceec (needs cc)\nruby tools/check_reference.rb   # the built-in reference (docs/manual/*/ref): signatures, coverage, and every example\n```\n\n**The evaluation concluded on 2026-10-08. Development goes on at a small scale: fixes and features\nthat come from using the language (on 2026-10-10, about 40 findings of the built-in reference, and\nnested namespaces).**\n\nThis was a research prototype. The hypothesis was that writing types on **operations** is better\nthan writing them on **variables**, for AI to write and to understand code. The evaluation\n([experiments/2026-10-05-ai-writability/](https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-05-ai-writability/README.md), in\nJapanese; summary page: [https://claude.ai/artifact/NaxNc5iropLucuvhawKETD](https://claude.ai/artifact/NaxNc5iropLucuvhawKETD)) could not separate\nthat from a larger effect: Sake is a language no model has seen before.\n\nWhat the evaluation showed:\n\n- **A capable model uses a new language at about twice the cost.** Claude Sonnet 5.5 had never\nseen Sake, and got only its documents (about 45k tokens, or an 8k-token cheat sheet). It still\nwrote, changed, read, and fixed programs as correctly as in Ruby, up to a SQL engine of 4,000\nlines grown in 6 stages. It almost never called an operation that does not exist. The cost was\n1.1 to 2.4 times Ruby's output tokens depending on the task: reading was cheap, writing was\nexpensive. Borrowing Ruby's syntax and Ruby's names probably helped (not tested).\n- **A smaller model cannot.** With Claude Haiku 4.5, Sake passed 26% of the hidden tests against\nRuby's 77%, and better documents or instructions did not close the gap.\n- **Where to write types did not show up.** On the SQL engine, all 12 runs in Ruby, Java, Haskell,\nScheme, Ruby with Steep, and Sake passed 366 to 368 of the 369 hidden tests, and no type checker\nprevented a bug that the others shipped. The output tokens, against Ruby, were Java 1.06,\nHaskell 1.18, Scheme 1.22, Steep (types on every variable and method) 1.48, and Sake 1.71.\nApart from Steep, which pays for writing every type, the order roughly follows how well-known\nthe language is. Sake's overhead did not shrink as one agent wrote\nmore programs, or with a shorter document. So the cost of an unfamiliar language is paid in\nevery task, and it hides any difference between operations and variables.\n- **Not compared: understanding and fixing code, against a language with types on variables.** Reading and changing were compared only with plain Ruby. A fair test would need a language with\ntypes on variables that models know as little as Sake, and such a language is hard to find or\nto make.\n\nWhat would justify reopening it: a task where Sonnet-class models fall clearly below the ceiling,\n*and* static type checks (in any language) are seen to stop errors that the dynamically typed\nsolutions ship.\n\n**Not measured: whether people read Sake more easily**, for example when reviewing code that AI\nwrote. \"One line tells you which function runs\" is also a claim about a human reader, and the\nevaluation only had AI read. Testing it needs people: review the same AI-written changes with\nplanted bugs in Ruby and in Sake, and compare how many bugs are found and how long it takes. The\nreviewers already know Ruby, so the comparison is biased against Sake.\n\nThe design notes and their reasons are in [DESIGN.md](https://github.com/ko1/sake-lang/blob/main/DESIGN.md).\n\n- Checking a 4,400-line program takes about 3 seconds with YJIT, which `bin/sake` turns on (4.5 before the\nshape keys of 2026-10-09,`experiments/2026-10-09-typer-shape-keys/` ; it took\n190 before 2026-10-08; see`experiments/2026-10-09-typer-speed/` ), and starting a program takes about 0.3 seconds for 2,500\nlines, about 5 times Ruby.\n- Fixed on 2026-10-08: the checker did not terminate for mutually recursive functions that use\n`Array + Array` (each`+` made a new array type per instantiation). A container made in the context\nof a container this function made is now shared, so the chain stops.", "url": "https://wpnews.pro/news/sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation", "canonical_source": "https://github.com/ko1/sake-lang", "published_at": "2026-10-17 18:55:25+00:00", "updated_at": "2026-10-17 19:06:18.220225+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["Sake", "Ruby", "Ko1", "Sinatra", "Rails"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation", "markdown": "https://wpnews.pro/news/sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation.md", "text": "https://wpnews.pro/news/sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation.txt", "jsonld": "https://wpnews.pro/news/sake-lang-experimental-language-based-on-ruby-but-with-types-for-each-operation.jsonld"}}