Brat is another experimental language, with very minimalistic syntax that looks sort of Ruby and Io inspired to me. It describes itself as "a little toy language that just doesn’t care".
Brat runs on Lua VM. There are no packages, so you need to compile it from sources. There's also no VSCode syntax highlighting, and very limited documentation.
Hello, World!
#!/usr/bin/env brat
p "Hello, World!"
$ ./hello.brat
Hello, World!
Brat compiles to Lua, so if you can check generated hello.lua
for what exactly will be executed. It's not the most human-readable.
FizzBuzz
#!/usr/bin/env brat
1.to 100 { n|
true? (n % 15 == 0)
{ p "FizzBuzz" }
{
true? (n % 5 == 0)
{ p "Buzz" }
{
true? (n % 3 == 0)
{ p "Fizz" }
{ p n }
}
}
}
Block syntax drops the first |
, so it's just { n| ... }
instead of { |n| ... }
.
There's no keywords here. true? condition, {if_true}, {if_false}
is how you can do if-else.
The exact rules aren't clear, but commas are optional in some cases.
Unicode
Unicode support is 💩, just like in Lua.
#!/usr/bin/env brat
p "Hello".length
p "Żółw".length
p "💩".length
$ ./unicode.brat
5
7
4
Functional Programming
The basic functional programming works:
#!/usr/bin/env brat
a = [1 2 3 4 5]
p a.map { x| x * 2 }
p a.select { x| x % 2 == 1}
p a.reduce { x, y| x + y}
$ ./functional.brat
[2, 4, 6, 8, 10]
[1, 3, 5]
15
Fibonacci
Brat has very little syntax, but one thing it includes is Ruby-style string interpolation.
#!/usr/bin/env brat
fib = {n|
true? (n <= 2)
{ 1 }
{ fib(n - 1) + fib(n - 2) }
}
1.to 20 {n| p "fib(#{n}) = #{fib(n)}" }
$ ./fib.brat
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
fib(10) = 55
fib(11) = 89
fib(12) = 144
fib(13) = 233
fib(14) = 377
fib(15) = 610
fib(16) = 987
fib(17) = 1597
fib(18) = 2584
fib(19) = 4181
fib(20) = 6765
Data Structures
Brat has Arrays and Hashes, and they have working ==
and print
, at would be totally unremarkable, except in Lua both of these operations are completely broken.
#!/usr/bin/env brat
a = [1 2 3 4 5]
h = ["name" : "Alice", "surname" : "Smith"]
p a
p h
p [1 2] == [1 2]
p ["name": "Alice"] == ["name": "Alice"]
$ ./data.brat
[1, 2, 3, 4, 5]
[name: Alice, surname: Smith]
true
true
Person
Brat uses prototype-based OO model:
#!/usr/bin/env brat
person = object.new
person.to_s = { "#{name} #{surname}" }
alice = person.new
alice.name = "Alice"
alice.surname = "Smith"
p alice
$ ./person.brat
Alice Smith
Vector
We can declare constructors, operators, and so on:
#!/usr/bin/env brat
vector = object.new
vector.x = 0
vector.y = 0
vector.to_s = { "<#{x},#{y}>" }
vector.init = { xx, yy |
my.x = xx
my.y = yy
}
vector.+ = { other |
vector.new(x + other.x, y + other.y)
}
a = vector.new(20, 60)
b = vector.new(400, 9)
c = a + b
p a
p b
p c
$ ./vector.brat
<20,60>
<400,9>
<420,69>
Wordle
Here's Wordle in Brat.
- it would be nice to have some
elsif
as chainedtrue?
gets quite messy - we also need to
include :file
to get access tofile
functions - defining
array.prototype.random
we need to explicitly sayobject.random
so it doesn't call itself
#!/usr/bin/env brat
include :file
array.prototype.random = { my[object.random(length)] }
words = file.read("wordle-answers-alphabetical.txt").split("\n")
word = words.random
guess = ""
while { guess != word } {
print "Guess word: "
guess = g
true? { guess.length == 5 }
{
0.to 4 { n|
true? { guess[n] == word[n] }
{ print "🟩" }
{
true? { word.include?(guess[n]) }
{ print "🟨" }
{ print "🟥" }
}
}
print "\n"
}
{
p "Guess must be 5 characters"
}
}
$ ./wordle.brat
Guess word: agile
🟥🟥🟥🟩🟥
Guess word: world
🟥🟩🟥🟩🟨
Guess word: could
🟥🟩🟥🟩🟨
Guess word: dolly
🟩🟩🟩🟩🟩
Should you use Brat?
Brat is actually a decent Smalltalk-style language. It has extremely minimalist syntax, prototype-based OOP, and in many ways it should be a lot more enjoyable than actual Smalltalk.
It's a bit of a pain to install, but if you want "Smalltalk experience" for one weekend, Brat is a solid choice.
There are definitely some annoyances like error messages being unclear, Brat insisting on extra spaces where you'd think they're unnecessary, and lack of Unicode support, but it's good enough for some fun playing with it.
Brat is obviously nowhere near production-ready.
Code
All code examples for the series will be in this repository.
Top comments (7)
I've briefly read all previous posts in series and it's so interesting to read about more and more languages!
There is also MoonScript which is CoffeeScript for Lua, very enjoyable to use.
I'd love to read more about how unicode and
==
is broken in more mainstream languages like Rust, Go, Dart, F#. Also how it's not broken in Ruby, and how, for instance,"ä".count("a") == 1
is absolutely correct.Also very curious to read how redundantly redundant TypeScript is, because there is no need for types in Ruby :)
Seriously, thanks for keeping writing!
Oh MoonScript looks interesting, and I still have a few free slots in the series.
Well,
luarocks install moonscript
on OSX andmoon
/moonc
crash, so I don't think I'll be doing that.You've played with brat even though their try page is broken: try.brat-lang.org/, and now some OSX bug is stopping you. That's fine, I know MoonScript is awesome.
Maybe PureScript then, a Haskell for JS?
And ReScript, OCaml for JS?
I'm interested in them because they both are considered safer than TypeScript. ReScript has a little hype, PureScript has zero hype. But why, if Haskell looks much more pleasant to work with than OCaml. (I learned them by reading your speedrun :)
And they both are alive, used in modern productions.
It turns out that MoonScript works with
luarocks install moonscript --dev
, so that might actually be happening.I had a lot of false starts in this series. With one post per day, I can't really be spending too much time getting something running.
I'm avoiding browser languages because I just did a 100-episode series about Electron and related tech before this one. I don't think anyone else ever did this kind of 100 programming languages series, but I'm sure if someone did, there'd be very little overlap between languages in this series and languages in theirs.
Once the series finishes, I guess I'll do a summary of what worked and what didn't etc.
Also I'd be happy to read from you about Ruby RBS
Why Ruby even need types, if the idea is to do TDD all the way, the code must be clear enough without type annotations?
Why types in separate files, was it good idea or bad?
After playing and seriously working with so many languages for many years, what do you think about importance of type annotations 1) for developers who are reading the code 2) for compile time checks?
Ruby was my primary language for 5 years or so, and I've left it couple of years ago primarily because of 1) and 2) from above, very interesting which direction Ruby is moving now, what's the future of it.
For my opinion about type systems, check Crystal episode.
All static type systems are awful in one of the listed ways, and types are needed less than ever as everyone TDDs, most platform move to JITs (which benefit very little from type annotations; unlike AOT compilers), and IDEs have enough machine learning in them they can do all the fancy stuff like proper autocompletion without static type annotations.
I'm not really seeing RBS or Python optional annotations or such getting much traction. (TypeScript gets some traction, maybe because JavaScript is so much messier on its own).
If you need type system, I'd say the absolute minimum to be tolerable is supporting union types (like
Person | null
orstring | number
), and that's basically just Crystal, TypeScript, and the unreleased Scala 3. Somehow 99% of languages with static type systems fail even that.