DEV Community

Discussion on: 100 Languages Speedrun: Episode 26: Raku (Perl 6)

Collapse
 
codesections profile image
Daniel Sockwell

Thanks for the writeup! It's always great to hear another prospective and this post gives some fairly strong indications of where our docs could be clearer.

One distinction I'd like to draw re: == and eqv is between the "the behaviors of equality operators" and "the richness of the type system". As you point out, in JavaScript, 1, Math.cos(0) and 1.0 are all === – and this makes perfect sense, because the typeof each one is number and the value of each one is 1; thus, they really are equal, both in type and value.

In contrast, in Raku, 1 is an Int, cos(0) is a Num (i.e., a floating point number) and 1.0 is a Rat (a rational number). And that distinction (sometimes!) matters, either for performance or correctness reasons, and I'm glad to have access to eqv. (For example, with floating point rounding errors: .1 × 3 == .3 multiplies a Rat and thus returns True; .1e0 × 3 == .3 multiplies a Num/float and returns False – the way most other languages behave.) In other cases, the distinction between 1 doesn't matter – I just care how many of something there is – and I'm glad to have ==.

Maybe it's because of the time I've spent with Rust and other languages that care more about their types, but I have a strong preference for equality operators that are explicit about their type conversions. Ruby's decision to have 1 and 1.0 be == even though they're different classes strikes me as a misfeature: in every other case == tests for object equality, but in this one, special case it does something different. Though I totally understand that it's an area where people disagree :)

Thread Thread
 
taw profile image
Tomasz Wegrzanowski

Every language does 1 == 1.0 numerically, so Ruby isn't unusual here, Raku's eqv is the weird one out.
But mainly if you have a bunch of numbers of different types in Ruby, it is immediately obvious which number is what type, as they all print as different:

> p [1, 1.0, 1/1r]
[1, 1.0, (1/1)]
Enter fullscreen mode Exit fullscreen mode

Same with Python:

> print([1, 1.0, Fraction(1,1)])
[1, 1.0, Fraction(1, 1)]
Enter fullscreen mode Exit fullscreen mode

And I think pretty much all other languages where there are ints and floats and other types. If there are multiple number types, they appear as different.

In Raku they appear identical as 1, while eqv treats them as different. I don't think this is a good design.

Thread Thread
 
codesections profile image
Daniel Sockwell

Well, "print as" is also a complex topic :) In Raku, the .gist method (which is used by say etc) prints both 1.0 and 1 as 1, but the .raku method (which prints the debug representation) prints 1.0 and 1. This feels correct to me – again, maybe because of time spent with Rust, which makes exactly the same choice.

Thread Thread
 
codesections profile image
Daniel Sockwell

Oh, and re:

Every language does 1 == 1.0 numerically

Again with Rust, not only does 1 == 1.0 not return true, it doesn't even compile (without an explicit cast). So, from a certain point of view, Raku's behavior represents something of a compromise between the dynamic behavior of a language like Ruby and the type-system-enforced guarantees of a more static language.

And, really, Raku falls somewhere between those two extremes quite frequently. You noted Raku's clear Perl legacy, and that's definitely a big part of the linage. But Raku's DNA also owes a surprisingly large amount to Haskell due to a large number of Haskellers involved in the early design process (or so I've heard/read – I wasn't involved that early).

Thread Thread
 
taw profile image
Tomasz Wegrzanowski

Haskell makes the same choices as Ruby and Python (and pretty much every other language):

Prelude> 1
1
Prelude> 1.0
1.0
Prelude> 1 == 1.0
True
Enter fullscreen mode Exit fullscreen mode