File Coverage

blib/lib/Syntax/Operator/Identical.pm
Criterion Covered Total %
statement 35 38 92.1
branch 7 8 87.5
condition 2 3 66.6
subroutine 10 11 90.9
pod 1 4 25.0
total 55 64 85.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2022-2024 -- leonerd@leonerd.org.uk
5              
6             package Syntax::Operator::Identical 0.05;
7              
8 4     4   1212582 use v5.14;
  4         18  
9 4     4   44 use warnings;
  4         9  
  4         281  
10              
11 4     4   25 use Carp;
  4         9  
  4         415  
12              
13 4     4   2730 use meta 0.003_002;
  4         5459  
  4         236  
14 4     4   36 no warnings 'meta::experimental';
  4         7  
  4         2389  
15              
16             require XSLoader;
17             XSLoader::load( __PACKAGE__, our $VERSION );
18              
19             =encoding UTF-8
20              
21             =head1 NAME
22              
23             C - almost certainly a terrible idea; don't use this
24              
25             =head1 SYNOPSIS
26              
27             You almost certainly don't want to use this.
28              
29             However, if despite all my warnings you still want to, then on Perl v5.38 or
30             later:
31              
32             use v5.38;
33             use Syntax::Operator::Identical;
34              
35             my $x = ...;
36             my $y = ...;
37              
38             if( $x ≡ $y ) {
39             say "x and y are identical";
40             }
41              
42             Or via L on Perl v5.14 or later:
43              
44             use v5.14;
45             use Syntax::Keyword::Match;
46             use Syntax::Operator::Identical;
47              
48             my $x = ...;
49              
50             match($x : ≡) {
51             case(undef) { say "The value is not defined" }
52             case(123) { say "The value is identical to 123" }
53             case("abc") { say "The value is identical to abc" }
54             }
55              
56             =head1 DESCRIPTION
57              
58             This module provides an infix operator that implements an identity test
59             between two values, in a way somewhat similar to the now-deprecated smartmatch
60             (C<~~>) operator, or other similar ideas.
61              
62             It is probably not a good idea to use this operator; it is written largely as
63             a demonstration on how such an operator I be implemented, as well as to
64             illustrate how fragile it is, in particular around the "is it a string or a
65             number?" part of the logic.
66              
67             =head2 Comparison Logic
68              
69             This operator acts symmetrically; that is, given any pair of values C<$x> and
70             C<$y>, the result of C<$x ≡ $y> will be the same as C<$y ≡ $x>. It uses the
71             following rules:
72              
73             =over 4
74              
75             =item * Definedness
76              
77             If both values are C, the operator yields true. Otherwise, if one value
78             is defined and the other is not, it yields false.
79              
80             =item * Booleans (on Perl v5.36 or later)
81              
82             If both values are booleans (as according to C), then the
83             operator returns true or false depending on whether they have the same value.
84             Otherwise, if only one is a boolean then it yields false.
85              
86             =item * References
87              
88             If both values are references, the operator yields true or false depending on
89             whether they both refer to the same thing. Otherwise, if only one is a
90             reference it returns false.
91              
92             =item * Non-references
93              
94             For any other pairs of values, if I value has a numerical part, then a
95             numerical comparison is made as per the C<==> operator, and if that is false
96             then this operator yields false. Then, if I value has a stringy part,
97             then a string comparison is made as per the C operator, and if that is
98             false then this operator yields false. Because non-defined and reference
99             values have already been considered at this point, at least one of these tests
100             must necessarily be performed.
101              
102             At this point, if there are no other reasons to reject it, the operator yields
103             true.
104              
105             =back
106              
107             As a consequence of the boolean rule, on Perl v5.36 or later, real boolean
108             values are not identical to either the numfied or stringified values they
109             would yield.
110              
111             (5 == 5) == 1; # is true
112             (5 == 5) ≡ 1; # is false
113              
114             (5 == 5) eq "1"; # is true
115             (5 == 5) ≡ "1"; # is false
116              
117             As a consequence of the reference rule, references are not identical to a
118             numified or stringified copy of themselves.
119              
120             my $aref = [];
121              
122             $aref == 0+$aref; # is true
123             $aref ≡ 0+$aref; # is false
124              
125             $aref eq "$aref"; # is true
126             $aref ≡ "$aref"; # is false
127              
128             Also as a consequence of the reference rule, any reference to an object is
129             never considered identical to a plain string or number, I that object
130             overloads the string or number comparison operators in a way that would
131             consider it to be.
132              
133             As a consequence of the final non-reference rule, comparisons between a
134             mixture of pure-number and pure-string values will be more strict than either
135             the C<==> or C operator alone would perform. Both operators must consider
136             the values equal for it to pass.
137              
138             10 == "10.0"; # is true
139             10 ≡ "10.0"; # is false, because eq says so
140              
141             =cut
142              
143             sub import
144             {
145 4     4   622 my $pkg = shift;
146 4         44 my $caller = caller;
147              
148 4         19 $pkg->import_into( $caller, @_ );
149             }
150              
151             sub unimport
152             {
153 1     1   12 my $pkg = shift;
154 1         3 my $caller = caller;
155              
156 1         5 $pkg->unimport_into( $caller, @_ );
157             }
158              
159 4     4 0 22 sub import_into { shift->apply( 1, @_ ) }
160 1     1 0 3 sub unimport_into { shift->apply( 0, @_ ) }
161              
162             sub apply
163             {
164 5     5 0 14 my $pkg = shift;
165 5         16 my ( $on, $caller, @syms ) = @_;
166              
167 5 100       29 @syms or @syms = qw( ≡ =:= ≢ !:= );
168              
169 5         29 $pkg->XS::Parse::Infix::apply_infix( $on, \@syms, qw( ≡ =:= ≢ !:= ) );
170              
171 5         364 my %syms = map { $_ => 1 } @syms;
  5         19  
172 5         9 my $callerpkg;
173              
174 5         16 foreach (qw( is_identical is_not_identical )) {
175 10 100       60 next unless delete $syms{$_};
176              
177 5   66     171 $callerpkg //= meta::package->get( $caller );
178              
179 5 100       26 $on ? $callerpkg->add_symbol( '&'.$_ => \&{$_} )
  4         33  
180             : $callerpkg->remove_symbol( '&'.$_ );
181             }
182              
183 5 50       5900 croak "Unrecognised import symbols @{[ keys %syms ]}" if keys %syms;
  0            
184             }
185              
186             =head1 OPERATORS
187              
188             =head2 ≡, =:=
189              
190             my $equal = $lhs ≡ $rhs;
191             my $equal = $lhs =:= $rhs;
192              
193             Yields true if the two operands are identical, using the rules defined above.
194             The two different spellings are aliases; the latter is simply an ASCII-safe
195             variant to avoid needing to type the C<≡> symbol.
196              
197             =head2 ≢, !:=
198              
199             my $unequal = $lhs ≢ $rhs;
200             my $unequal = $lhs !:= $rhs;
201              
202             The complement operator to C<≡>; yielding true where it would yield false, and
203             vice versa. The two different spellings are aliases; the latter is simply an
204             ASCII-safe variant to avoid needing to type the C<≢> symbol.
205              
206             =head1 FUNCTIONS
207              
208             As a convenience, the following functions may be imported which implement the
209             same behaviour as the infix operators, though are accessed via regular
210             function call syntax.
211              
212             These wrapper functions are implemented using L, and thus
213             have an optimising call-checker attached to them. In most cases, code which
214             calls them should not in fact have the full runtime overhead of a function
215             call because the underlying test operator will get inlined into the calling
216             code at compiletime. In effect, code calling these functions should run with
217             the same performance as code using the infix operators directly.
218              
219             =head2 is_identical
220              
221             my $equal = is_identical( $lhs, $rhs );
222              
223             A function version of the C<≡> operator.
224              
225             =head2 is_not_identical
226              
227             my $unequal = is_not_identical( $lhs, $rhs );
228              
229             A function version of the C<≢> operator.
230              
231             =cut
232              
233             =head1 AUTHOR
234              
235             Paul Evans
236              
237             =cut
238              
239             0x55AA;