Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do the Perl 6 set operations compare elements?

Tags:

set

raku

moarvm

Running under moar (2016.10)

Consider this code that constructs a set and tests for membership:

my $num_set = set( < 1 2 3 4 > );
say "set: ", $num_set.perl;
say "4 is in set: ", 4 ∈ $num_set;
say "IntStr 4 is in set: ", IntStr.new(4, "Four") ∈ $num_set;
say "IntStr(4,...) is 4: ", IntStr.new(4, "Four") == 4;
say "5 is in set: ", 5 ∈ $num_set;

A straight 4 is not in the set, but the IntStr version is:

set: set(IntStr.new(4, "4"),IntStr.new(1, "1"),IntStr.new(2, "2"),IntStr.new(3, "3"))
4 is in set: False
IntStr 4 is in set: True
IntStr(4,...) is 4: True
5 is in set: False

I think most people aren't going to expect this, but the docs doesn't say anything about how this might work. I don't have this problem if I don't use the quote words (i.e. set( 1, 2, 3, 4)).

like image 425
brian d foy Avatar asked Nov 26 '16 04:11

brian d foy


3 Answers

You took a wrong turn in the middle. The important part is what nqp::existskey is called with: the k.WHICH. This method is there for value types, i.e. immutable types where the value - rather than identity - defines if two things are supposed to be the same thing (even if created twice). It returns a string representation of an object's value that is equal for two things that are supposed to be equal. For <1>.WHICH you get IntStr|1 and for 1.WHICH you get just Int|1.

like image 158
timotimo Avatar answered Oct 13 '22 11:10

timotimo


As explained in the Set documentation, sets compare object identity, same as the === operator:

Within a Set, every element is guaranteed to be unique (in the sense that no two elements would compare positively with the === operator)

The identity of an object is defined by the .WHICH method, as timotimo elaborates in his answer.

like image 4
smls Avatar answered Oct 13 '22 11:10

smls


Write your list of numbers using commas

As you mention in your answer, your code works if you write your numbers as a simple comma separated list rather than using the <...> construct.

Here's why:

4 ∈ set 1, 2, 3, 4 # True

A bare numeric literal in code like the 4 to the left of constructs a single value with a numeric type. (In this case the type is Int, an integer.) If a set constructor receives a list of similar literals on the right then everything works out fine.

<1 2 3 4> produces a list of "dual values"

The various <...> "quote words" constructs turn the list of whitespace separated literal elements within the angle brackets into an output list of values.

The foundational variant (qw<...>) outputs nothing but strings. Using it for your use case doesn't work:

4 ∈ set qw<1 2 3 4> # False

The 4 on the left constructs a single numeric value, type Int. In the meantime the set constructor receives a list of strings, type Str: ('1','2','3','4'). The operator doesn't find an Int in the set because all the values are Strs so returns False.

Moving along, the huffmanized <...> variant outputs Strs unless an element is recognized as a number. If an element is recognized as a number then the output value is a "dual value". For example a 1 becomes an IntStr.

According to the doc "an IntStr can be used interchangeably where one might use a Str or an Int". But can it?

Your scenario is a case in point. While 1 ∈ set 1,2,3 and <1> ∈ set <1 2 3> both work, 1 ∈ set <1 2 3> and <1> ∈ set 1, 2, 3 both return False.

So it seems the operator isn't living up to the quoted doc's claim of dual value interchangeability.

This may already be recognized as a bug in the set operation and/or other operations. Even if not, this sharp "dual value" edge of the <...> list constructor may eventually be viewed as sufficiently painful that Perl 6 needs to change.

like image 2
raiph Avatar answered Oct 13 '22 10:10

raiph