100 Languages Speedrun: Episode 82: Pyret

taw

Tomasz Wegrzanowski

Posted on February 8, 2022

100 Languages Speedrun: Episode 82: Pyret

Let's talk education. If you want to learn programming without spending years on it, there are basically three decent ways that are proven to work:

  • you start with Ruby to learn it properly in the best language, with proper TDD and so on (what the best bootcamps are doing)
  • you start with HTML+CSS, then learn JavaScript (frontend route)
  • you start with Python, in Jupyter style environment (data science route)

Most other languages are terrible for beginners, due to enormous amount of additional complexity that gets in the way of learning. Yes even the "good" languages. That's not to say Ruby, JavaScript, and Python aren't complex - but in their case complexity is largely opt-in, and beginners can get to employable levels of proficiency without ever creating a Ruby DSL, Python decorator, JavaScript prototype chain, or such.

There are a few more languages that are approachable for beginners (SQL, Spreadsheets, Logo), but they offer no easy path to real world development. All your elite Excel skills won't transfer into programming skills well.

Universities teach programming all wrong. They start with an extremely beginner-hostile language like C, Java, SML, Scheme, or whatnot, and the only reason programmers come out by the end is that they have years to do that.

So anyway, Pyret is a new programming language designed specifically for education, that tries to be far more beginner-friendly than any existing language, while teaching transferable skills. Let's see how it worked.

Hello, World!

We can start by writing a Hello, World!, and the language already fails, by printing a lot of irrelevant crap in addition to our message:

#!/usr/bin/env pyret

print("Hello, World!")
Enter fullscreen mode Exit fullscreen mode
$ ./hello.arr
1/1 modules compiled (hello.arr)
Cleaning up and generating standalone...
Hello, World!
The program didn't define any tests.
Enter fullscreen mode Exit fullscreen mode

We need to pass a bunch of flags (-q for quiet, -k for no tests) just to get hello world working:

#!/usr/bin/env pyret -qk

print("Hello, World!\n")
Enter fullscreen mode Exit fullscreen mode
$ ./hello2.arr
Hello, World!
Enter fullscreen mode Exit fullscreen mode

Fibonacci

Let's do the Fibonacci sequence!

#!/usr/bin/env pyret -q

fun fib(n):
  if n <= 2:
    1
  else:
    fib(n - 2) + fib(n - 1)
  end
end
check:
  fib(1) is 1
  fib(2) is 1
  fib(10) is 55
end
Enter fullscreen mode Exit fullscreen mode

This part isn't too bad. fib definition is fine, in some hybrid Ruby-Python syntax.

Pyret wants unit tests to follow the code with check: block. In real apps, I don't like this mixing of app code and test code, but this is a reasonable thing to do in language for beginners. Pyret uses == for comparisons in normal program, but in tests it's is, which seems like unnecessary complexity 🤷.

Thing go downhill fast when we try to print it, in the same format I always use.:

for each(n from range(1, 21)):
  block:
    print("fib(")
    print(n)
    print(")=")
    print(fib(n))
    print("\n")
  end
end
Enter fullscreen mode Exit fullscreen mode

WTF is this? Pyret has no string interpolation, which is an insane obstacle to throw at beginners. There are no easy workarounds like passing multiple arguments to print, or automatically turning numbers into strings with "fib(" + n. Oh and if you have multiple expressions in a block, you need to wrap the whole thing in a pointless block: block. WTF?

OK, let's try another approach, how about we construct the string once, without interpolating?

for each(n from range(1, 21)):
  print("fib(" + to-string(n) + ")=" + to-string(fib(n)) + "\n")
end
Enter fullscreen mode Exit fullscreen mode

This is far less readable than string interpolation would be:

# Not Pyret
for each(n from range(1, 21)):
  print("fib(#{n})=#{fib(n)\n")
end
Enter fullscreen mode Exit fullscreen mode

And of course it even prints some crap with -q, instead of doing the reasonable thin and only printing extra stuff when there are problems:

$ ./fib2.arr
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
Looks shipshape, all 3 tests passed, mate!
Enter fullscreen mode Exit fullscreen mode

You can disable tests with -k, but then you won't know if there are any issues. Overall, it's baffling design all over.

FizzBuzz

Here's one way to do the FizzBuzz. I'm converting n to string inside fizzbuzz function, as printing is otherwise such pain. Pyret doesn't have println like vast majority of languages, and you can't print(n, "\n") or print(n + "\n") or even just have two print without wrapping the whole thing in a block: ... end.

#!/usr/bin/env pyret -q

fun fizzbuzz(n):
  if num-modulo(n, 15) == 0:
    "FizzBuzz"
  else if num-modulo(n, 5) == 0:
    "Buzz"
  else if num-modulo(n, 3) == 0:
    "Fizz"
  else:
    to-string(n)
  end
end
check:
  fizzbuzz(30) is "FizzBuzz"
  fizzbuzz(31) is "31"
  fizzbuzz(33) is "Fizz"
  fizzbuzz(35) is "Buzz"
end

for each(n from range(1, 101)):
  print(fizzbuzz(n) + "\n")
end
Enter fullscreen mode Exit fullscreen mode

And again, there's an extra stupid line at the end:

$ ./fizzbuzz.arr
1
2
Fizz
4
Buzz
...
Fizz
97
98
Fizz
Buzz
Looks shipshape, all 4 tests passed, mate!
Enter fullscreen mode Exit fullscreen mode

Defining a class

Let's define a Person class! Oh wait, we can't. Pyret doesn't have classes, it only has ML-style data types:

#!/usr/bin/env pyret -q

data Person:
  person(first-name, last-name)
end
check:
  to-string(person("Alice", "Smith")) is "person(Alice, Smith)"
end

people = [list:
  person("Alice", "Wonderland"),
  person("Bob", "Marley"),
  person("Eve", "Jackson"),
]

for each(a-person from people):
  print(to-string(a-person) + "\n")
end
Enter fullscreen mode Exit fullscreen mode
$ ./person.arr
person(Alice, Wonderland)
person(Bob, Marley)
person(Eve, Jackson)
Looks shipshape, your test passed, mate!
Enter fullscreen mode Exit fullscreen mode

The logic of this choice is - why teach people OOP which pretty much every modern language is based on to some degree, when you can teach them dead 1970s' approach to programming, right?

Operator Precedence

OK, just one more thing, can Pyret at least do basic math:

#!/usr/bin/env pyret -q

a = 20
b = 4
c = 100

print(a + b * c)
Enter fullscreen mode Exit fullscreen mode

Of course it cannot:

$ ./math.arr
Operators of different kinds cannot be mixed at the same level, but `+` is at math.arr:7:8-7:9 at the same level as `*` at math.arr:7:12-7:13. Use parentheses to group the operations and to make the order of operations clear.
There were compilation errors
Enter fullscreen mode Exit fullscreen mode

Because obviously an average programming student never had a single math class.

By the way Pyret is not the only language which does this. Smalltalk-derived Self language does, because Smalltalk had an insane idea of not having operator precedence (2 + 3 * 4 means 20 in Smalltalk), so to dig itself out of this insane hole, Self forced parentheses in this situation. This way any Smalltalk code ported to Self, which didn't obey the usual rules, would need some extra parentheses one way or the other. Better explicit than broken. Pyret has zero reason to do this.

Should you use Pyret?

No. The language is terrible, and completely unsuitable for education, or for any other use. It's basically ML with nicer syntax, and nobody needs that.

One big area where languages differ a lot, and where a beginner friendly language could make a big difference is error messages. Experienced programmers can deal with cryptic errors, beginners really can't. Pyret fails at this as well, and its error messages are generally just awful.

The language is bad, and you should use Ruby, JavaScript, or Python for teaching beginners. Absolutely not this.

Code

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

Code for the Pyret episode is available here.

💖 💪 🙅 🚩
taw
Tomasz Wegrzanowski

Posted on February 8, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related