Sake-lang: Experimental language based on Ruby but with types for each operation 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. Sake /seɪk/, as in "for the sake of" is an experimental programming language. It is designed for the sake of finding a new place for types. It keeps Ruby's syntax , but you write the type on each operation , never on a variable, a parameter, or a field: String.upcase name Sake name.upcase Ruby: an error in Sake, reported with the fix above Because every operation names its type: - One line is enough to read most calls. For most calls, a line tells you which function runs, without looking up declarations. A module function M.f x , a type list A|B .f x and an operator choose by the first argument's type, from a set of candidates fixed before running. - Names are checked before running. A misspelled operation or type is reported before the program starts. - Types are inferred, never written. The whole-program inference catches values that cannot fit an operation, and the strictness level chooses how much of that stops a run. - Run-time checks remain. Every operation still checks its arguments while running, so what the checks before running miss still stops at the exact operation. The motivation was to make a language that is easy for AI to write. The evaluation did not support it 2026-10-08 ; development continues at a small scale , from what using the language turns up. See Status status . Writing Sake with a language model? Give it docs/cheatsheet.md https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md : the rules on four pages plus the compact list of operations, about 10k tokens, and all a model needs 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 reference manual and the playground are at https://ko1.github.io/sake-lang/ https://ko1.github.io/sake-lang/ . python class Point attr reader x, y def norm2 p = @x @x + @y @y @x is Point.x p : p is the first argument end words = String.split "the cat and the hat", " " counts = Hash.new 0 Array.each words { |w| counts w += 1 } p counts puts Point.norm2 Point.new 3, 4 js $ bin/sake readme.sake {"the" = 2, "cat" = 1, "and" = 1, "hat" = 1} 25 Mistakes are reported before anything runs, each with a fix: name = "sake" puts name.upcase puts String.upcse name bash $ bin/sake mistakes.sake mistakes.sake:2:11: error: method call on a value name.upcase is not allowed hint: String.upcase name hint: Symbol.upcase name mistakes.sake:3:13: error: undefined function String.upcse hint: did you mean String.upcase ? - 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 which features it shows. - Web: a todo app on the Sinatra https://github.com/ko1/sake-lang/blob/main/sakelib/sinatra.sake port 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 in Ruby next to it , and a blog with Rails' routes, controllers, views and models experiments/2026-10-12-web/rails/ https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-12-web/rails . Both serve requests over a real socket WEBrick . - sakelib/ https://github.com/ko1/sake-lang/blob/main/sakelib : Ruby's libraries and gems ported to Sake JSON, CSV, ERB, Rack, Sinatra, minitest, and more . Each one's test, test/sakelib/ https://github.com/ko1/sake-lang/blob/main/test/sakelib , prints what the same program prints 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. - test/samples/ https://github.com/ko1/sake-lang/blob/main/test/samples : about 200 small programs, mostly one feature each, with their expected output, including the error messages. - test/native/ https://github.com/ko1/sake-lang/blob/main/test/native : the programs that bin/sabic and bin/ceec compile to native code. The interpreter is written in Ruby, parses with Prism https://github.com/ruby/prism , and walks the AST. It needs Ruby 4.0 tested with 4.0.2 and Prism 1.9.0 and has no other dependencies. bin/sake FILE.sake check, then run bin/sake -c FILE.sake check only bin/sake --strict FILE.sake check more strictly before running level 2 bin/sake --strict=3 FILE.sake levels 0-4, or items: --strict=type,nil bin/sake --types FILE.sake experimental: print the inferred types bin/sake --dump=ast FILE.sake print the SakeAST the resolved program that runs The exit status is 0 on success, 1 for an error while running, and 2 for a problem found before running. bin/sabic compiles a program to a native executable by way of Rust, for the subset whose types the generator can fix: Integer as 64-bit, an overflow is an error , Float, true/false, nil as Option , String, Array, Tuple, classes, blocks, unions of classes as a Rust enum with mixin calls as a match , and case / in on values and types. Anything outside the subset is reported with its line, and the program still runs on the interpreter. A program that the checks of bin/sake stop at its default level is not compiled either. bin/sabic FILE.sake writes FILE.rs and builds FILE with rustc -O bin/sabic FILE.sake --emit only the Rust source On three micro benchmarks loops, fib, levenshtein the compiled program ran 3 to 21 times faster than Ruby 4.0 with YJIT, 8 to 53 times faster than Ruby without it, and within 0.9 to 1.6 times of Rust written by hand experiments/2026-10-10-rust-backend/ https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-10-rust-backend/README.md . A mixin call dispatched on the argument's type costs 0.12 ns, about the same as a hand-written enum and an eighth of Rust's dyn Trait . Against Spinel https://github.com/matz/spinel , the Ruby AOT compiler, the same programs ran at the same speed on loops and levenshtein and 8 times faster on the dispatch benchmark, with an analyzer of 1,100 lines of Ruby where Spinel's is 107,000 lines of C, because Sake's rules do that work in the language. bin/ceec does the same through C, on the same type inference, and runs at the same speed as the Rust output; against hand-written C, the remaining gap 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 Ruby with YJIT. - 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 by running the program. - docs/spec.md https://github.com/ko1/sake-lang/blob/main/docs/spec.md : the language as implemented. - docs/builtins.md https://github.com/ko1/sake-lang/blob/main/docs/builtins.md : all built-in operations, generated from the interpreter. - docs/cheatsheet.md https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md : the rules on four pages plus the compact list of operations, about 10k tokens, for a language model or a Ruby programmer writing Sake. Its examples are run on every build. - 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 published at https://claude.ai/artifact/EdrbscXRGUtkKppprRKohP https://claude.ai/artifact/EdrbscXRGUtkKppprRKohP . - 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 operation, one chapter per type, with examples that ruby tools/check reference.rb runs and compares. GitHub 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 this README .github/workflows/pages.yml and tools/gen site.rb , no Jekyll . - 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 written with Ruby's own library next to the same program in Sake they print the same output , and the port tools/gen compare.rb . - ide/ https://github.com/ko1/sake-lang/blob/main/ide/README.md : the playground, a browser IDE on ruby.wasm with completion, diagnostics as you type, and the inferred types on hover. - DESIGN.md https://github.com/ko1/sake-lang/blob/main/DESIGN.md : the design notes, with the reasons behind each decision in Japanese . - 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 Elm, Crystal, Rust, Elixir, TypeScript, Clojure and the type-inference literature in Japanese . - Values : Integer, Float, Rational, Complex, String, Symbol, true/false, nil, Tuple a, b , Record {x: 1} , Array, Hash, Set, Range, Regexp, and Time. - 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 . - Operators : a + b runs the + of a 's type, and a i its . Your own types join by including Arithmetic , Comparable , Bitwise , or Indexable . - Chains : T.g x .U.f args is U.f T.g x , args , a step without a type continues with the type the chain named last String.strip s .upcase , and is the previous statement's value, so a sequence of operations reads in order while the type of each step is on the same line. - Functions and blocks : functions are polymorphic and take no annotations. Blocks are passed with yield . - 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 is no inheritance a B is not an A . - nil : a value that may be nil has the type nil | T , narrowed by if x , x = nil , and early returns. - Patterns : x in Integer and case x in ... narrow types, and the checker verifies that a case is exhaustive. - Exceptions : raise , rescue , ensure , and retry , plus inference of which exceptions may escape a function. - Built-in library : about 550 operations, named after Ruby's core library. Enum the prelude is Enumerable under a short name: Enum.map x { } dispatches to Array, Hash, Set or Range, and a class joins with include Enum and def each . Not yet: case / when , and first-class blocks. Built-in constants such as Math::PI are read as operations Math.PI ; a program has no value constants def pi = 3.14159 instead . bin/sake the command bin/sabic the compiler to native code through Rust sabi, 錆, is rust bin/ceec the compiler to native code through C lib/sake/ resolver checks before running , lower Prism AST to SakeAST , interpreter and typer type inference , both on SakeAST, the Rust and C backends rust.rb, c.rb with their range analysis ranges.rb , standard library stdlib .rb sakelib/ the library written in Sake: the prelude Enum and ports of Ruby's libraries and gems test/ golden tests test/samples/ .sake with .expected , CLI tests, test/native/ programs both native backends must print like the interpreter examples/ example programs by category, each with its expected output examples/README.md docs/ tutorial, specification, built-in list, one-page guide, examples tools/ generators for the docs they run the examples experiments/ experiments, each with its method, results, and limits in a README Run the tests with: ruby test/test samples.rb UPDATE=1 rewrites the expected outputs ruby test/test cli.rb the command line, and whether the generated docs are up to date ruby test/test examples.rb examples/ / .sake print their .expected UPDATE=1 rewrites them ruby test/test sakelib.rb the library ports: each test/sakelib/X.sake prints what X.rb prints ruby test/test sake suite.rb Sake's test suite written in Sake test/sake/ test.sake, on sakelib/minitest.sake ruby test/test ide.rb the playground's Ruby side lib/sake/ide.rb ruby test/test rust.rb the Rust backend: test/native/ .sake compiled and compared with the interpreter needs rustc ruby test/test c.rb the C backend: the same programs through bin/ceec needs cc ruby tools/check reference.rb the built-in reference docs/manual/ /ref : signatures, coverage, and every example The evaluation concluded on 2026-10-08. Development goes on at a small scale: fixes and features that come from using the language on 2026-10-10, about 40 findings of the built-in reference, and nested namespaces . This was a research prototype. The hypothesis was that writing types on operations is better than writing them on variables , for AI to write and to understand code. The evaluation experiments/2026-10-05-ai-writability/ https://github.com/ko1/sake-lang/blob/main/experiments/2026-10-05-ai-writability/README.md , in Japanese; summary page: https://claude.ai/artifact/NaxNc5iropLucuvhawKETD https://claude.ai/artifact/NaxNc5iropLucuvhawKETD could not separate that from a larger effect: Sake is a language no model has seen before. What the evaluation showed: - A capable model uses a new language at about twice the cost. Claude Sonnet 5.5 had never seen Sake, and got only its documents about 45k tokens, or an 8k-token cheat sheet . It still wrote, changed, read, and fixed programs as correctly as in Ruby, up to a SQL engine of 4,000 lines grown in 6 stages. It almost never called an operation that does not exist. The cost was 1.1 to 2.4 times Ruby's output tokens depending on the task: reading was cheap, writing was expensive. Borrowing Ruby's syntax and Ruby's names probably helped not tested . - A smaller model cannot. With Claude Haiku 4.5, Sake passed 26% of the hidden tests against Ruby's 77%, and better documents or instructions did not close the gap. - Where to write types did not show up. On the SQL engine, all 12 runs in Ruby, Java, Haskell, Scheme, Ruby with Steep, and Sake passed 366 to 368 of the 369 hidden tests, and no type checker prevented a bug that the others shipped. The output tokens, against Ruby, were Java 1.06, Haskell 1.18, Scheme 1.22, Steep types on every variable and method 1.48, and Sake 1.71. Apart from Steep, which pays for writing every type, the order roughly follows how well-known the language is. Sake's overhead did not shrink as one agent wrote more programs, or with a shorter document. So the cost of an unfamiliar language is paid in every task, and it hides any difference between operations and variables. - 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 types on variables that models know as little as Sake, and such a language is hard to find or to make. What would justify reopening it: a task where Sonnet-class models fall clearly below the ceiling, and static type checks in any language are seen to stop errors that the dynamically typed solutions ship. Not measured: whether people read Sake more easily , for example when reviewing code that AI wrote. "One line tells you which function runs" is also a claim about a human reader, and the evaluation only had AI read. Testing it needs people: review the same AI-written changes with planted bugs in Ruby and in Sake, and compare how many bugs are found and how long it takes. The reviewers already know Ruby, so the comparison is biased against Sake. The design notes and their reasons are in DESIGN.md https://github.com/ko1/sake-lang/blob/main/DESIGN.md . - Checking a 4,400-line program takes about 3 seconds with YJIT, which bin/sake turns on 4.5 before the shape keys of 2026-10-09, experiments/2026-10-09-typer-shape-keys/ ; it took 190 before 2026-10-08; see experiments/2026-10-09-typer-speed/ , and starting a program takes about 0.3 seconds for 2,500 lines, about 5 times Ruby. - Fixed on 2026-10-08: the checker did not terminate for mutually recursive functions that use Array + Array each + made a new array type per instantiation . A container made in the context of a container this function made is now shared, so the chain stops.