Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 16, 2021 09:11 pm GMT

Languages Speedrun: Episode 26: Raku (Perl 6)

Raku, which started its life as Perl 6, was meant as successor to Perl language.

The main problem with that is that Perl 6 was officially announced in 2000, and only properly appeared in 2015, and in that time frame Perl lost most of its popularity, with most people moving to Ruby, PHP, or Python.

Raku isn't trying to achieve compatibility with Perl 5, but it follows very similar style. This probably seemed like a good idea back in 2000 when Perl was very popular, considerably less so nowadays.

But in addition to just being cleaned up Perl 5, Raku also has some really cool new concepts that are definitely worth taking a look at.

Hello, World!

#!/usr/bin/env rakusay "Hello, World!";

Two things to notice here.

The good thing - say is a really nice name. Of all the words languages use for this concept, I think say is much better than print or puts or console.log or println or prn or writeln and so on. Raku uses a lot of new names for various functions, some quite good, some not so much.

The bad thing - damn mandatory semicolons. Somehow Raku didn't drop this nonsense, even though they've been dropped by nearly every modern language by now, as well as by all good JavaScript coding styles (if you're coding JavaScript with semicolons, you're doing it wrong).

Every useless token increases mental load, and every pointless "missed semicolon" error costs both programmer time and concentration - extremely limited resources. There's no excuse to force semicolons in any new language.

Semicolons in Perl and Raku are statement separators not terminators, so unlike C/Java/etc. you can skip them for last statement of a block.

FizzBuzz

#!/usr/bin/env rakusub fizzbuzz($n) {  given $n {    when $_ % 3 == 0 && $_ % 5 == 0 { "FizzBuzz" }    when $_ % 3 == 0 { "Fizz" }    when $_ % 5 == 0 { "Buzz" }    default { $_ }  }}for 1..100 {  say fizzbuzz($_)}

Here's one way to do FizzBuzz. The loop is very nice, and 1..100 for range from 1 to 100 makes much more sense than range(1, 101) convention.

Raku just like Perl uses $_ for default variable in a lot of contexts, such as for loops and given blocks. Just to make clear - these two $_s are not overwriting each other, each $_ is specific to its block.

Fibonacci

#!/usr/bin/env rakumulti sub fib(1) { 1 }multi sub fib(2) { 1 }multi sub fib($n) { fib($n-1) + fib($n-2) }for 1..30 {  say fib($_)}

Raku allows defining multiple functions with the same name, matched depending on number of parameters, their types, or values. This looks quite Haskell-like.

Equality

Time to get to the most shitty part of Perl design, that 100% got copied over to Raku. There are no "numbers" or "strings". It's just scalars, and they convert automatically.

But it's not so simple. 2 and 2.0 are the same thing (as numbers), but "2" and "2.0" are completely different (as strings). So Perl and Raku have full parallel sets of operators. For some like concatenation (2 ~ 2 is 22, + is always numerical) that's fine, but the absolute worst is that there are two equalities: == for numbers and eq for strings.

What's the problem with that? Well, in a normal language with normal == operator, it can be used not just for numbers and strings, but also for arrays, hashes, sets, or whatever else we throw at it.

In Raku we have this abomination:

#!/usr/bin/env raku# Numericalsay 2 + 2 == 4;say 2 == "2.0";# String equalitysay "hello" eq "hello";# Looks like it works...say [1, 2] eq [1, 2];# Raku sorts the key order (unlike Ruby) so they matchsay {b=>2, a=>1} eq {a=>1, b=>2};# WTF? How is any of these True?say ["one", "two"] eq "one two";say [1, 2] eq ["1 2"];say [1, 2] eq "1 2";say {a=>1} eq "a1";say {a=>1, b=>2} eq "a1
b2";

It's True for every one of them.

Also notice completely insane default stringification rules (Str()) - instead of Str([1, 2]) being something sensible like [1, 2], it's "1 2". And for hashes, what the hell with those tabs and newlines?

JSON

There's another popular language with broken inequality - JavaScript. And the traditional workaround would be to JSON.stringify(a) === JSON.stringify(b).

Let's see how this goes. First Rakudo comes without JSON in standard library, and without package manager.

I needed to brew uninstall rakudo moarvm nqp and brew install rakudo-star instead to get its zef package manager. That is really weird, but a lot of languages have packaging quirks, so let's roll with it. I didn't actually need to install any packages, as rakudo-star package (but not rakudo package) already included JSON::Tiny.

But wait a minute, if Raku doesn't distinguish numbers from strings, how is it even doing JSON at all!

#!/usr/bin/env rakuuse JSON::Tiny;say to-json([1, 2]);say to-json(["1", "2"]);

It turns out this works just fine:

$ ./json.raku[ 1, 2 ][ "1", "2" ]

Raku acts as if there was no distinction between numbers and strings, but it's there behind the scenes internally. Number 2 and string "2" are 99% same, except when they aren't.

I still remember this bug from my Perl days, where printing a variable would change its type:

#!/usr/bin/perl -luse JSON;my $x = 2;print encode_json($x);print "$x";print encode_json($x);

Which led to this WTF:

$ ./json_bug.pl22"2"

So you'd print something for debugging, and suddenly your program would work completely differently. That was a fun evening, I still remember it 15 years later, and that issue was buried much deeper than this.

Anyway, I cannot reproduce it in Raku:

#!/usr/bin/env rakuuse JSON::Tiny;my $x = 2;say to-json($x);say "$x";say to-json($x);

Where it definitely works:

$ ./json_bug.raku222

But we'll probably still run into the issue where numbers and strings are 99% same except where they aren't. Maybe more experienced Raku programmers can tell me how to trigger this issue in Raku.

Oh and notice kebab-case identifiers are supported.

JSON Equality

Let's try our equality-by-JSON:

#!/usr/bin/env rakuuse JSON::Tiny;sub json-equal($a, $b) {  to-json($a) eq to-json($b)}# True as expectedsay json-equal([1, 2], [1, 2]);# False as expectedsay json-equal([1, 2], "[1, 2]");say json-equal("2", "2.0");# Also True...say json-equal(5, 5.0);say json-equal(0, -0);# Also False...say json-equal(1, "1");# Totally random if True or Falsesay json-equal({b=>2, a=>1}, {a=>1, b=>2});

First three tests are fine. Then 5 and 5.0 are same, as are 0 and -0 - because these are actually big rationals not floats, let's not get into Raku number system just yet.

1 and "1" being not JSON-equal is something we already knew about.

But what about {b=>2, a=>1} and {a=>1, b=>2}? Str() always sorted their keys so they were always eq, but for JSON conversion their order is not guaranteed, so this program will print True or False at random.

Overall, total disaster. This can be worked around, but from my Perl and JavaScript experience, not having working universal == is massive pain that comes up all the time.

EQV

Oh wait, we're not done yet. There's eqv! Let's see how it does:

#!/usr/bin/env raku# True as expectedsay [1, 2] eqv [1, 2];say {b=>2, a=>1} eqv {a=>1, b=>2};# This is still Truesay 0 eqv -0;# False as expectedsay [1, 2] eqv "[1, 2]";say "2" eqv "2.0";# Now this is False too?say 5 eqv 5.0;say 1 eqv "1";

It's the closest to what we got so far. 1 and "1" being different is what we already knew about. But now 5 and 5.0 are treated by eqv as different (as integer and rational), even though they were the everywhere else up to this point. It's still the best we got so far, but it's a total embarrassment to design a language without a working universal == in this century.

Unicode

At least Perl never had problems with Unicode, and neither does Raku:

#!/usr/bin/env rakusay uc "w";say chars "w";say chars "";

Which outputs completely correct:

$ ./unicode.rakuW41

State and Hashes

Let's do Fibonacci again, except with memoization:

#!/usr/bin/env rakusub fib($n) {  state %cache = (0 => 0, 1 => 1);  %cache{$n} //= fib($n - 1) + fib($n - 2);}say fib($_) for 1..1000

Raku has a lot of scopes. my are the usual local variables. state are scoped to their function, but they're only initialized once.

Variable types can be inferred by the sigil - $ are scalar (strings, integers, and so on). @ are arrays. % are hashes (or dictionaries). Because Raku knows their type, it can initialize them to correct empty values - but i this case we initialize it ourselves.

A //= B sets A to B if it's missing (||= checks truthiness of A, and that's not what we want, as 0, empty string etc. are false in Raku). It also returns the return value.

So this is extremely concise way to do memoization for a language that doesn't have builtin support for memoization specifically.

Oh and you can use if, for and such as a suffix. That definitely improves readability for "guard clauses" (return if ... or return unless or break if ...), not necessarily here with a for loop.

Junctions

Did you ever want to write if x == 1 | 2 | 3, but your language wouldn't support it, so you needed to do if x == 1 || x == 2 || x == 3 instead?

I've got some good news!

#!/usr/bin/env rakusay "What's your name?";my $name = get;if ($name eq "admin" | "root") {  say "Don't try to hack me"} else {  say "Hello, $name";}

Let's give it a go:

$ ./junctions.rakuWhat's your name?AliceHello, Alice$ ./junctions.rakuWhat's your name?adminDon't try to hack me

get reads one line, and | creates a "junction". It's a collection of values in one of the modes (and, or, one, none) that performs all its operations on each element for as long as it can.

#!/usr/bin/env rakusay "Some of them match!" if (1 | 2 | 3) * 2 == (4 | 5);say "All of them match!" if (1 & 2) * 2 == (4 | 5 | 2);
$ ./junctions2.rakuSome of them match!All of them match!

Only when evaluated in boolean context junctions collapse to a single value. The code means:

  • one of (1 * 2, 2 * 2, 3 * 2) equals one of (4, 5)
  • all of (1 * 2, 2 * 2) equals one of (4, 5, 2)

Junctions are pretty much unique to Raku, and I have no idea if they actually make coding easier, or they're just unnecessary complexity.

Regular expressions

Perl introduced its own extended regular expressions, and now every single language uses Perl style regular expressions, so it was a huge success. The pre-Perl version is basically forgotten. Perl's regular expressions were at least largely compatible with pre-Perl kind, so they were limited to using small number of symbols in some twisted combinations to achieve extra functionality.

Raku obviously introduced its own regular expression system, which tries to be more expressive and is intentionally not backwards compatible.

#!/usr/bin/env rakusay "What's your name?";my $name = get;if ($name ~~ /^<[a..z]>+$/ | /^<[A..Z]>+$/) {  say "Hello, $name"} else {  say "Please use consistent case"}

What is going on here...

$ ./regexp.rakuWhat's your name?aliceHello, alice$ ./regexp.rakuWhat's your name?BOBHello, BOB$ ./regexp.rakuWhat's your name?CharliePlease use consistent case

First, the pattern matching with ~~ (Perl and Ruby use =~). Any junction for |. Neither is specific to regular expressions.

Interesting things start with /^<[a..z]>+$/. Raku regular expressions has a lot of new features, and so they had to change some existing syntax to make space for all the new features. Character range [a-z] became <[a..z]>. The [] got repurposed to be non-capturing group ((?:) in Perl regexp), which is arguably much more important functionality.

The subject of Raku Regular Expressions is big enough, that I'll need to dedicate a whole episode to it. It's basically a language within a language.

Unicode operators

It really should come at no surprise that Raku has Unicode operators in its syntax. After Julia, this is the second language we're investigating which does it, and I expect this to become mainstream feature of new programming languages going forward.

Porting Julia's example:

#!/usr/bin/env rakusay [1,2]  [2,3,1];say 3  [2,3,1];say (/3).sin * 3.sqrt;

It outputs:

$ ./unicode_operators.rakuTrueTrue1.4999999999999998

Just like in Julia, set operations like and work the same. is the usual constant. Unlike Julia, .sin is a method not a standalone function, and .sqrt is a method and there's no .

Neither Raku nor Julia go overboard with Unicode operators, but they can definitely make math code more readable.

Kitchen Sink Language Design

Raku is a kitchen-sink language like Perl or Scala. They tend to try out all the ideas, courageously break with pass assumptions, and some of these turns out to be amazing. But all the not-so-good ideas have high mental cost, so kitchen-sink languages tend to top the charts of the most disliked as well.

Sometimes it works wonders. Perl might have fallen far from its peak popularity, but it will live forever as the ancestor of pretty much every modern programming language. Ruby, JavaScript, PHP (and to lesser degree Python) are all basically Perl's children, and these languages in turn influenced everything else. It revolutionized programming language design. Just compare the last major pre-Perl language Java, which is a total usability disaster, with any of the post-Perl designs.

On a smaller scale CoffeeScript tried to throw a kitchen sink at making JavaScript bearable, and while it's largely dead now, ES6+ wouldn't exist without CoffeeScript paving the way.

But just as often, it doesn't work at all. Kitchen sink languages like Raku or Scala didn't get far, and so far didn't have much impact on other languages either.

Should you use Raku?

If you're interested in programming language design, or thinking about designing your own programming language, Raku is definitely worth checking out, even if you don't plan to write any real code in it. It tries out so many things, I barely scratched the surface here.

If you're a Perl programmer who for some reason doesn't want to switch to Ruby or Python like most, Raku might be worth a try. For me it hold on to too many bad design ideas from Perl, but if you're already accepting the costs, maybe it's not a big deal.

On the other hand, if you think Ruby is already Perl 6, or can't stand Perl style languages, then Raku will be highly unappealing.

I haven't really explored how well Raku works in pragmatic terms, such as its ecosystem, performance, and so on. From a quick look it doesn't look too great.

I'd definitely recommend Raku as language to give a go for a fun weekend, even if few would want to use it for anything more serious.

Code

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

Code for the Raku episode is available here.


Original Link: https://dev.to/taw/languages-speedrun-episode-26-raku-perl-6-4emf

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To