DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

100 Languages Speedrun: Episode 88: Brat

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!"
Enter fullscreen mode Exit fullscreen mode
$ ./hello.brat
Hello, World!
Enter fullscreen mode Exit fullscreen mode

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 }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
$ ./unicode.brat
5
7
4
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode
$ ./functional.brat
[2, 4, 6, 8, 10]
[1, 3, 5]
15
Enter fullscreen mode Exit fullscreen mode

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)}" }
Enter fullscreen mode Exit fullscreen mode
$  ./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
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode
$ ./data.brat
[1, 2, 3, 4, 5]
[name: Alice, surname: Smith]
true
true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
$ ./person.brat
Alice Smith
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
$ ./vector.brat
<20,60>
<400,9>
<420,69>
Enter fullscreen mode Exit fullscreen mode

Wordle

Here's Wordle in Brat.

  • it would be nice to have some elsif as chained true? gets quite messy
  • we also need to include :file to get access to file functions
  • defining array.prototype.random we need to explicitly say object.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"
    }
}
Enter fullscreen mode Exit fullscreen mode
$ ./wordle.brat
Guess word: agile
🟥🟥🟥🟩🟥
Guess word: world
🟥🟩🟥🟩🟨
Guess word: could
🟥🟩🟥🟩🟨
Guess word: dolly
🟩🟩🟩🟩🟩
Enter fullscreen mode Exit fullscreen mode

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.

Code for the Brat episode is available here.

Top comments (7)

Collapse
 
romeerez profile image
Roman K

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!

Collapse
 
taw profile image
Tomasz Wegrzanowski

Oh MoonScript looks interesting, and I still have a few free slots in the series.

Collapse
 
taw profile image
Tomasz Wegrzanowski

Well, luarocks install moonscript on OSX and moon/moonc crash, so I don't think I'll be doing that.

Thread Thread
 
romeerez profile image
Roman K • Edited

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.

Thread Thread
 
taw profile image
Tomasz Wegrzanowski

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.

Collapse
 
romeerez profile image
Roman K

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.

Thread Thread
 
taw profile image
Tomasz Wegrzanowski

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 or string | number), and that's basically just Crystal, TypeScript, and the unreleased Scala 3. Somehow 99% of languages with static type systems fail even that.