DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

100 Languages Speedrun: Episode 91: Arturo

Arturo is an another attempt at creating a minimal programming language.

I don't think it saw any use other than by its author yet, but it has a homebrew package (brew install arturo) and even VSCode extension, so let's give it a try!

Hello, World!

We can start with the very obvious Hello, World! program.

This identical program works in so many languages, they mostly differ in newline being included or not, and if you need parentheses or if they're optional.

#!/usr/bin/env arturo

print "Hello, World!"
Enter fullscreen mode Exit fullscreen mode
$ ./hello.art
Hello, World!
Enter fullscreen mode Exit fullscreen mode

Obnoxiously, Arturo prints Windows \r\n newlines even on OSX.

Arturo also has REPL with arturo. It annoyingly erases the whole screen and prints massive banner, and also only quits on Ctrl-C instead of Ctrl-D. These might sound like minor issues, but they're highly disruptive if you want to get into and out of repl. REPL should not mess the screen, it should only show prompt (maybe one line for version etc., but I'd recommend against it) and quit on Ctrl-D. Not following these basic rules is wrong.

Operator Precedence

Like many other minimalist languages, Arturo makes the same mistake of not having operator precedence. However unlike every single one of them which then evaluates left to right, Arturo evaluates right to left, possibly the world's only such language.

Of course I'd strongly advise even "minimalistic" languages to get their shit together and implement proper operator precedence. A lot of languages like Smalltalk's progeny had to undo this madness, and this is always more painful than getting it right in the first place.

#!/usr/bin/env arturo

print 2 * 3 + 4
Enter fullscreen mode Exit fullscreen mode

It prints an answer that's not 10:

$ ./math.art
14
Enter fullscreen mode Exit fullscreen mode

In practice this system just means parentheses everywhere, as it's not possible to overcome decades of math education like that for no reason, and without parnetheses everywhere you'll constantly be reading everything incorrectly.

Variables

Arturo variables are wild:

#!/usr/bin/env arturo

a: 1
b: 1
c: new 1

inc 'a

print a
print b
print c
Enter fullscreen mode Exit fullscreen mode

What do you think this prints?

$ ./variables.art
2
2
1
Enter fullscreen mode Exit fullscreen mode

WTF just happened? It goes beyond "modifying string literals" problem, we're modifying integer literals, and it wasn't even the same literal.

We can fix it by new, but I'm completely baffled by why anyone even thought to make them work like this.

FizzBuzz

We can do it with if? condition [then-block] else [else-block]:

#!/usr/bin/env arturo

loop 1..100 'n [
  print [
    if? 0 = n % 3 [
      if? 0 = n % 5 ["FizzBuzz"] else ["Fizz"]
    ] else [
      if? 0 = n % 5 ["Buzz"] else [n]]
    ]
  ]
]
Enter fullscreen mode Exit fullscreen mode

Fibonacci

Arturo has string interpolation, with yet another syntax. If someone was interested in collecting all the variants, maybe there's a 100 string interpolation syntaxes by now.

It's also really damn slow, even this fib 1-20 takes 2s, fib 1-30 takes 4 minutes. By comparison Python does 1-30 fib in less than a second. So we're talking about 300x slower than Python.

#!/usr/bin/env arturo

fib: function [n] [
  if? n =< 2 [1] else [(fib (n - 1)) + (fib (n - 2))]
]

loop 1..20 'n [
  print ~"fib(|n|) = |fib n|"
]
Enter fullscreen mode Exit fullscreen mode
$ ./fib.art
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

Unicode

Arturo correctly handles Unicode:

#!/usr/bin/env arturo

print upper "Żółw"
print lower "Żółw"
print size "Żółw"
print size "🍰"
Enter fullscreen mode Exit fullscreen mode
$ ./unicode.art
ŻÓŁW
żółw
4
1
Enter fullscreen mode Exit fullscreen mode

Maybe

Blocks are passed as variables, and we can just execute them with do. In this example passing f instead of [do f] would also have worked.

#!/usr/bin/env arturo

maybe: function [f] [
  if 0 = random 0 1 [do f]
]

loop 1..20 'n [
  maybe [print n]
]
Enter fullscreen mode Exit fullscreen mode
$ ./maybe.art
1
2
4
6
8
9
12
16
Enter fullscreen mode Exit fullscreen mode

Wordle

Wordle is becoming the FizzBuzz of this series, so let's do one for Arturo as well.

By the way if you want to do a similar series, I think Wordle is a great toy problem, as it does file reading, string manipulation, user input, looping, Unicode, randomness, and all in very small amount of code.

Wordle in Arturo is quite decent:

#!/usr/bin/env arturo

words: split.words read "wordle-answers-alphabetical.txt"
word: words\[random 0 ((size words) - 1)]

guess: ""
while [guess <> word] [
  guess: input "Guess: "
  if? 5 <> size guess
    [print "Guess must be 5 letters long."]
  else [
    loop 0..4 'n [
      case []
        when? [guess\[n] = word\[n]] [prints "🟩"]
        when? [contains? word guess\[n]] [prints "🟨"]
        else [prints "🟥"]
    ]
    prints "\n"
  ]
]
Enter fullscreen mode Exit fullscreen mode
$ ./wordle.art
Guess: arise
🟥🟥🟩🟩🟩
Guess: noise
🟥🟥🟩🟩🟩
Guess: guise
🟩🟩🟩🟩🟩
Enter fullscreen mode Exit fullscreen mode

prints is the non-newline version of print and case is what Arturo has instead of if/elsif/else chains.

Should you use Arturo?

I don't recommend it.

If you try Arturo, operator precedence is likely going to be a huge pain, Arturo makes so many baffling design cohices, and it doesn't provide good feedback if you make mistakes.

If you want a minimalistic language to play with, I'd recommend starting with Ioke or Brat. If you tried them already, and want to try something else, then I guess Arturo is an option for another weekend.

Code

All code examples for the series will be in this repository.

Code for the Arturo episode is available here.

Latest comments (2)

Collapse
 
drkameleon profile image
Yanis Zafirópulos • Edited

Despite the "bashing" (lol) Arturo took in your unofficial... review, as Arturo's main developer, first, let me thank you for taking the time to have an (obviously quite good) look into it and writing this post. I really appreciate it - including the criticism - which for me has always something positive to learn from and move the project to the right direction.

I generally understand most of the objections you have with Arturo, as a language; some are quite obvious ones, some less so, but still...

Regarding the way the REPL behaves, I've been working on it like a lot in the past few months and I think most - if not all - of your recommendations could be implemented. A challenge here has always been Windows (whose terminals have made my life unnecessarily difficult), but what can I say. I bet anybody with at least some experience in terminal- (and even UI-) app portability can feel my pain... lol

Regarding the elephant in the room: operator precedence. I understand your issues, and I obviously get the math-side of things. But programming is not exactly maths. If it was only about multiplications/divisions/additions/etc, your argument would be totally valid. But, in programming, we usually have tens of different operators, with wildly different precedence rules depending on the language in question (I admit, I've never been able to memorize more than a few - and I honestly don't know why I have to...) and, yep, that usually ends in parentheses hell, indeed. Arturo's approach is quite simple: since symbols - even infix ones - are nothing but aliases to functions (yes, even + and *, etc actually point to standard-library functions; in that case, add and mul respectively), why would they suddenly break the rule that exists throughout the language? So, the solution is quite clear-cut: we simply pick a rule and stick to it. (Consistency has been one of my main priorities since the very beginning, so here we go...). And, no, Arturo is by no means the only language out there to follow this example: REBOL, Red, Ren-C, namely (and the whole group of "sibling" languages, as a matter of fact) also do something along the exact same lines.

As a sidenote, this for example: if? n =< 2 [1] else [(fib (n - 1)) + (fib (n - 2))] looks pretty much like the parentheses hell you describe; but it has nothing to do with math operator precedence per-se. I would say it's more about knowing how the language actually works. And once you know, IMHO, it's actually far simpler and straightforward: if? n =< 2 -> 1 else -> (fib n-1) + fib n-2. (I honestly doubt whether anybody would argue this looks confusing or over-complicated...)

As some interesting trivia, Grafito - mainly, the largest and most complete project fully-written in Arturo - in its 1200+ lines, you will notice there are some 15 pairs of parentheses in total. And, no, it's not like I was struggling to reduce them. That's pretty much what happened naturally.

Regarding variables, you do have a point as well. And I fully understand the confusion. It's one of the points that I do want to iron out. Only thing I just have to mention is that this behavior happens only when you change a variable "internally", that is: by passing it as a literal to a function. E.g. i: i + 1 is totally safe and transparent. Things like inc 'i are tricky (essentially, it's like passing by-reference; it's a pointer, not a value). But quite powerful, once you actually know what you're doing.

Last but not least, regarding your criticism about Arturo's speed, I'm totally with you. I'm not satisfied with it either. But given that I wanted to get the language (and its library) to a rather mature stage first, I guess I decided to put off the optimization of its VM. But it will happen. And soon. ;-) Generally, it's not that... horrible. It mainly depends on the use-case: admittedly, recursion and function calls are not efficient, yet; but, on the other hand, in many other cases, it already beats hands-down e.g. Ruby and the vast majority of interpreted languages (with the only exception, perhaps, being Lua - which is as lightweight as you can possibly get, with all the shortcomings this has, ofc). So, corner-cases aside, I think we're not too far off our goal - for a stack-based bytecode VM written from scratch that is pretty-much in a vanilla, non-JIT, no-nothing, unoptimized state, that is.

Again, thanks for taking the time to review Arturo - and all the constructive criticism. Needless to say, you are more than welcome to post any issue to GitHub as well (I look into all of them, one by one, and take them into account very seriously) and even participate by contributing code to the project.

Have a great day! :)

Yanis
(aka Dr.Kameleon)

Collapse
 
taw profile image
Tomasz Wegrzanowski

By the only one I meant that Arturo is the only one I'm aware of that evaluates right to left. A few like REBOL, Smalltalk etc. go left to right. Self does another interesting thing, as it bans mixing of different operators. 2+3*4 just won't compile in Self, you need parentheses. Self was one of the languages I wanted to review, but there were technical issues that prevented it.

Vast majority of languages follow math rules, or have syntax where it doesn't matter (Lisp, Forth, Assembly etc.).

I only mentioned speed issues for languages which are into "100x slower than Python" category. Speed is definitely a fixable issue.