.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Syntax::Keyword::Match 3pm" .TH Syntax::Keyword::Match 3pm "2022-12-26" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" "Syntax::Keyword::Match" \- a "match/case" syntax for perl .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& use v5.14; \& use Syntax::Keyword::Match; \& \& my $n = ...; \& \& match($n : ==) { \& case(1) { say "It\*(Aqs one" } \& case(2) { say "It\*(Aqs two" } \& case(3) { say "It\*(Aqs three" } \& case(4), case(5) \& { say "It\*(Aqs four or five" } \& default { say "It\*(Aqs something else" } \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module provides a syntax plugin that implements a control-flow block called \f(CW\*(C`match/case\*(C'\fR, which executes at most one of a choice of different blocks depending on the value of its controlling expression. .PP This is similar to C's \f(CW\*(C`switch/case\*(C'\fR syntax (copied into many other languages), or syntax provided by Switch::Plain. .PP This is an initial, experimental implementation. Furthermore, it is built as a non-trivial example use-case on top of XS::Parse::Keyword, which is also experimental. No \s-1API\s0 or compatibility guarantees are made at this time. .SH "Experimental Features" .IX Header "Experimental Features" Some of the features of this module are currently marked as experimental (even within the context that the module itself is experimental). They will provoke warnings in the \f(CW\*(C`experimental\*(C'\fR category, unless silenced. .PP .Vb 1 \& use Syntax::Keyword::Match qw( match :experimental(dispatch) ); \& \& use Syntax::Keyword::Match qw( match :experimental ); # all of the above .Ve .SH "KEYWORDS" .IX Header "KEYWORDS" .SS "match" .IX Subsection "match" .Vb 3 \& match( EXPR : OP ) { \& ... \& } .Ve .PP A \f(CW\*(C`match\*(C'\fR statement provides the controlling expression, comparison operator, and sequence of \f(CW\*(C`case\*(C'\fR statements for a match operation. The expression is evaluated to yield a scalar value, which is then compared, using the comparison operator, against each of the \f(CW\*(C`case\*(C'\fR labels in the order they are written, topmost first. If a match is found then the body of the labelled block is executed. If no label matches but a \f(CW\*(C`default\*(C'\fR block is present, that will be executed instead. After a single inner block has been executed, no further tests are performed and execution continues from the statement following the \f(CW\*(C`match\*(C'\fR statement. .PP The braces following the \f(CW\*(C`match\*(C'\fR block must only contain \f(CW\*(C`case\*(C'\fR or \&\f(CW\*(C`default\*(C'\fR statements. Arbitrary code is not supported here. .PP Even though a \f(CW\*(C`match\*(C'\fR statement is a full statement and not an expression, it can still yield a value if it appears as the final statment in its containing \&\f(CW\*(C`sub\*(C'\fR or \f(CW\*(C`do\*(C'\fR block. For example: .PP .Vb 5 \& my $result = do { \& match( $topic : == ) { \& case(1) { ... } \& } \& }; .Ve .PP \fIComparison Operators\fR .IX Subsection "Comparison Operators" .PP The comparison operator must be either \f(CW\*(C`eq\*(C'\fR (to compare cases as strings) or \&\f(CW\*(C`==\*(C'\fR (to compare them as numbers), or \f(CW\*(C`=~\*(C'\fR (to compare cases using regexps). .PP On Perl versions 5.32 onwards, the \f(CW\*(C`isa\*(C'\fR operator is also supported, allowing dispatch based on what type of object the controlling expression gives. .PP .Vb 4 \& match( $obj : isa ) { \& case(A::Package) { ... } \& case(Another::Package) { ... } \& } .Ve .PP Remember that comparisons are made in the order they are written, from the top downwards. Therefore, if you list a derived class as well as a base class, make sure to put the derived class \fBbefore\fR the base class, or instances of that type will also match the base class \f(CW\*(C`case\*(C'\fR block and the derived one will never match. .PP .Vb 2 \& class TheBase {} \& class Derived :isa(TheBase) {} \& \& match( $obj : isa ) { \& case(TheBase) { ... } \& case(Derived) { \& # This case will never match as the one above will always happen first \& } \& } .Ve .PP \&\fISince version 0.08\fR the operator syntax is parsed using XS::Parse::Infix, meaning that custom infix operators can be recognised, even on versions of perl that do not support the full \f(CW\*(C`PL_infix_plugin\*(C'\fR mechanism. .SS "case" .IX Subsection "case" .Vb 1 \& case(VAL) { STATEMENTS... } \& \& case(VAL), case(VAL), ... { STATEMENTS... } .Ve .PP A \f(CW\*(C`case\*(C'\fR statement must only appear inside the braces of a \f(CW\*(C`match\*(C'\fR. It provides a block of code to run if the controlling expression's value matches the value given in the \f(CW\*(C`case\*(C'\fR statement, according to the comparison operator. .PP Multiple \f(CW\*(C`case\*(C'\fR statements are permitted for a single block. A value matching any of them will run the code inside the block. .PP If the value is a non-constant expression, such as a variable or function call, it will be evaluated as part of performing the comparison every time the \&\f(CW\*(C`match\*(C'\fR statement is executed. For best performance it is advised to extract values that won't need computing again into a variable or \f(CW\*(C`use constant\*(C'\fR that can be calculated just once at program startup; for example: .PP .Vb 1 \& use constant CONDITION => a_function("with", "arguments"); \& \& match( $var : eq ) { \& case(CONDITION) { ... } \& ... \& } .Ve .PP The \f(CW\*(C`:experimental(dispatch)\*(C'\fR feature selects a more efficient handling of sequences of multiple \f(CW\*(C`case\*(C'\fR blocks with constant expressions. This handling is implemented with a custom operator that will entirely confuse modules like \&\f(CW\*(C`B::Deparse\*(C'\fR or optree inspectors like coverage tools so is not selected by default, but can be enabled for extra performance in critical sections. .SS "default" .IX Subsection "default" A \f(CW\*(C`default\*(C'\fR statement must only appear inside the braces of a \f(CW\*(C`match\*(C'\fR. If present, it must be the final choice, and there must only be one of them. It provides a block of code to run if the controlling expression's value did not match any of the given \f(CW\*(C`case\*(C'\fR labels. .SH "COMPARISONS" .IX Header "COMPARISONS" As this syntax is fairly similar to a few other ideas, the following comparisons may be useful. .SS "Core perl's given/when syntax" .IX Subsection "Core perl's given/when syntax" Compared to core perl's \f(CW\*(C`given/when\*(C'\fR syntax (available with \&\f(CW\*(C`use feature \*(Aqswitch\*(Aq\*(C'\fR), this syntax is initially visually very similar but actually behaves very differently. Core's \f(CW\*(C`given/when\*(C'\fR uses the smartmatch (\f(CW\*(C`~~\*(C'\fR) operator for its comparisons, which is complex, subtle, and hard to use correctly \- doubly-so when comparisons against values stored in variables rather than literal constants are involved. It can be unpredictable whether string or numerical comparison are being used, for example. By comparison, this module requires the programmer to specify the comparison operator. The choice of string or numerical comparison is given in the source code \- there can be no ambiguity. .PP Additionally, the \f(CW\*(C`isa\*(C'\fR operator is also permitted, which has no equivalent ability in smartmatch. .PP Also, the \f(CW\*(C`given/when\*(C'\fR syntax permits mixed code within a \f(CW\*(C`given\*(C'\fR block which is run unconditionally, or at least, until the first successful \f(CW\*(C`when\*(C'\fR statement is encountered. The syntax provided by this module requires that the only code inside a \f(CW\*(C`match\*(C'\fR block be a sequence of \f(CW\*(C`case\*(C'\fR statements. No other code is permitted. .SS "Switch::Plain" .IX Subsection "Switch::Plain" Like this module, Switch::Plain also provides a syntax where the programmer specifies whether the comparison is made using stringy or numerical semantics. \&\f(CW\*(C`Switch::Plain\*(C'\fR also permits additional conditions to be placed on \f(CW\*(C`case\*(C'\fR blocks, whereas this module does not. .PP Additionally, the \f(CW\*(C`isa\*(C'\fR operator is also permitted, which has no equivalent ability in \f(CW\*(C`Switch::Plain\*(C'\fR. .SS "C's switch/case" .IX Subsection "C's switch/case" The C programming language provides a similar sort of syntax, using keywords named \f(CW\*(C`switch\*(C'\fR and \f(CW\*(C`case\*(C'\fR. One key difference between that and the syntax provided for Perl by this module is that in C the \f(CW\*(C`case\*(C'\fR labels really are just labels. The \f(CW\*(C`switch\*(C'\fR part of the statement effectively acts as a sort of computed \f(CW\*(C`goto\*(C'\fR. This often leads to bugs caused by forgetting to put a \&\f(CW\*(C`break\*(C'\fR at the end of a sequence of statements before the next \f(CW\*(C`case\*(C'\fR label; a situation called \*(L"fallthrough\*(R". Such a mistake is impossible with this module, because every \f(CW\*(C`case\*(C'\fR is provided by a block. Once execution has finished with the block, the entire \f(CW\*(C`match\*(C'\fR statement is finished. There is no possibility of accidental fallthrough. .PP C's syntax only permits compiletime constants for \f(CW\*(C`case\*(C'\fR labels, whereas this module will also allow the result of any runtime expression. .PP Code written in C will perform identically even if any of the \f(CW\*(C`case\*(C'\fR labels and associated code are moved around into a different order. The syntax provided by this module notionally performs all of its tests in the order they are written in, and any changes of that order might cause a different result. .SH "TODO" .IX Header "TODO" This is clearly an early experimental work. There are many features to add, and design decisions to make. Rather than attempt to list them all here it would be best to check the \s-1RT\s0 bug queue at .PP .SH "AUTHOR" .IX Header "AUTHOR" Paul Evans