17 ms·
Perl 6's given: switch on steroids
- CDSlice 7y agoThis looks a lot like Rust's match blocks (which were inspired by pattern matching in ML languages). It's nice to see pattern matching constructs come to more languages.
- lizmat 7y agoBut, this has really not a lot to do with pattern matching. Please see my comment at https://www.reddit.com/r/perl6/comments/bvhzwh/perl_6s_given_switch_on_steroids_matéu/eppm0oz/ https://www.reddit.com/r/perl6/comments/bvhzwh/perl_6s_given...
- agumonkey 7y agothe condition tree flattening reminds me of pattern matching compilation though
- Sharlin 7y agoSurely it has everything to do with pattern matching? A C-style switch statement is an extremely primitive version of pattern matching, and in the article the flattened equivalent of the nested switch structure is just tuple destructuring and matching, just like in Rust I could write match (foo, bar, baz) { (1, 2, 3) => something(), (3, 2, 1) => somethingelse() }
- nemetroid 7y agoFrom that explanation, it sounds very similar to pattern matching in general, and Erlang's case statement in particular. Would you care to elaborate?
- b2gills 7y agoRust's match blocks appear to be less powerful than the smart-match feature in Perl6. Rust doesn't appear to allow arbitrary code in the condition. --- given 1 { when (0 ≤ * ≤ 9) { 'digit' } } Expanding out the definition of the `when` above: given 1 { { my $initial-result = (0 ≤ * ≤ 9); # get the result of the expression my $result = $initial-result.ACCEPTS($_); # ask it if it accepts the topic if $result { succeed 'digit' } } } The condition part a `when` is just like the right side of the `smart-match` feature, and the `where` clause. It does double code execution. First as just a statement. (In this case the result is a lambda that will compare against `0` and `9`.) Then it asks the result of the statement if it `ACCEPTS` the current topic value. (In this case it runs the lambda with topic as its argument.) If the second level result is truish then `when` runs its block and calls `succeed` with its last value. `succeed` throws a `CX::Succeed` control exception which the `given` catches. --- This double code execution allows for a lot of flexibility. Also since smart-match asks the initial result if it `ACCEPTS` the topic, it gets to decide how it matches. class Match-Always { method ACCEPTS ( $ --> True ){} } class Match-Never { method ACCEPTS ( $ --> False ){} } # match 50% of the time arbitrarily class Match-Half { method ACCEPTS ( $ --> Bool ){ Bool.pick } } given 1 { when Match-Half { … } when ( $_ == 1 ?? Match-Always !! Match-Never ) { … } } (Note that `True` works like `Match-Always` and `False` like `Match-Never`, so there is not a good reason to add those two types like I did.)
- cracauer 7y agoOf course if you'd use Lisp in the first place you could make a new syntax to input those things in the visually safest manner without waiting for a language revision. Compile-time computing is precisely about this, e.g. https://medium.com/@MartinCracauer/a-gentle-introduction-to-compile-time-computing-part-3-scientific-units-8e41d8a727ca https://medium.com/@MartinCracauer/a-gentle-introduction-to-...
- xisukar 7y agoDo you mind explaining how is it related to the article? I'm genuinely interested.
- cracauer 7y agoThe article reasons that you should use the syntax that makes it safest to enter and later change that table you need.
- riffraff 7y agoperl6 also has syntax macros (sort of, I think rakudo does not implement the full perl6 macro stuff).
- b2gills 7y agoPerl6 is “supposed” to have syntax macros. Rakudo has a macro feature, but the current design was deemed not good enough to be part of the Perl6 specification testsuite yet. There has been design work in this area in the 007 project. https://github.com/masak/007 https://github.com/masak/007
- dan-robertson 7y agoPerl 6 includes the ability to modify the grammar and parser on the fly in a way which is much more convenient than reader macros in Common Lisp (and different to macros in CL). Saying “new syntax” is also slightly disingenuous because the only convenient new syntax available is made of parens. That said, this isn’t really an article about syntax but how several language features fit together. Making something slightly similar in CL could be something like: (defmacro with (it &body body) `(let ((it ,it)) (block nil ,@body))) (defmacro having (condition &body body) `(if (smart-match ,condition it) (return-from nil (progn ,@body)) (values))) (defgeneric smart-match (matcher thing)) ... But this makes the behaviour of `having` a bit weird. I feel like this article is much more about how certain perl6 features compose nicely together (in particular smart matching, $_, expressions returning values) in a way that the existing features of given and when compose to give a useful switch mechanism for free.
- xisukar 7y agoThe `when` block seems to exhibit a different behavior when used as a block vs as a statement modifier. The latter lets things falls through until it matches the most general case. For instance, given class Audience { has $.number; has $.gender; has $.formality; has $.country; } my @vosotros = <Spain EquitorialGuinea WesternSahara>; my $audience = Audience.new: :number(100), :gender('feminine'), :formality('informal'), :country('Spain') ; Contrast: my $message = do given (.number, .gender, .formality, .country given $audience) { when 1, 'masculine', 'informal', * { '¿Cómo estás mi amigo?' } when 1, 'masculine', 'formal', * { '¿Cómo está el señor?' } when 1, 'feminine', 'informal', * { '¿Cómo estás mi amiga?' } when 1, 'feminine', 'formal', * { '¿Cómo está la señora?' } when * > 1, 'feminine', 'informal', @vosotros.any { '¿Cómo estáis mis amigas?' } when * > 1, *, 'informal', @vosotros.any { '¿Cómo estáis mis amigos?' } when * > 1, *, * , * { '¿Cómo están ustedes?' } } say $message; #=> «¿Cómo estáis mis amigas?» with this: my $message = do given (.number, .gender, .formality, .country given $audience) { '¿Cómo estás mi amigo?' when 1, 'masculine', 'informal', *; '¿Cómo está el señor?' when 1, 'masculine', 'formal', *; '¿Cómo estás mi amiga?' when 1, 'feminine', 'informal', *; '¿Cómo está la señora?' when 1, 'feminine', 'formal', *; '¿Cómo estáis mis amigas?' when * > 1, 'feminine', 'informal', @vosotros.any; '¿Cómo estáis mis amigos?' when * > 1, *, 'informal', @vosotros.any; '¿Cómo están ustedes?' when * > 1, *, * , *; } say $message; #=> «¿Cómo están ustedes?»
- unixhero 7y agoWhoa
- nxrabl 7y agoFortunately, the second example does throw warnings for each line of the block but the last: Useless use of constant string "¿Cómo estás mi amigo?" in sink context
- raiph 7y agoYes, that's deliberate. If you use an ordinary `when` (with its statement(s) on the right) then P6 assumes it should succeed with the value of the last statement of the matched `when`. To succeed means to leave the block and return that last value if the block's value is being kept (eg via a `do`). If you wish instead to proceed to the next statement rather than succeed, you must write `proceed`. Conversely, if you use a modifier `when` (with its statement(s) on the left) then P6 assumes it should proceed as explained above after matching. If you wish instead to succeed as explained above you must write `succeed` (with a value unless you wish to succeed with a value of `Nil`). This is great bit of language design work. Thank you Larry.
- combatentropy 7y agoThis is nice. In other languages, I would settle for a switch-statement like Go's, which by default breaks instead of falls through. The common switch-statement seems like a prank: If you omit the break, the compiler throws no error, like when you forget a closing brace. Instead the program silently does the opposite of what you meant. I am less afraid of forgetting one, more of one day accidentally deleting one. Every once in a while in Vim I order my keystrokes wrong and a random line disappears. Usually it catches my eye, but it is unnerving. The risk is eternal. You would want to leave the switch-statement as is, for backward compatibility, and come up with a new key word to set off the improved version, like Perl did with "given." With all of the ongoing improvements to JavaScript, I'm surprised this has not been addressed. And in general with most languages I am surprised that their flow control is so stunted.
- codr7 7y agoFalling through sort of makes sense in C, which is underdefined by design to allow pulling whatever dirty tricks needed to get where you're going. Copying the idea to higher level languages makes little sense to me. Blindly copying any feature into a language design is a bad idea from my experience.
- rat9988 7y agoHonestly I'd rather use goto than have fallthrough by default in C. Or even better, a Fallthrough keyword as in GO.
- codr7 7y agoWow, had no idea about fallthrough in Go and I've written plenty. Thanks. That's a seriously ugly solution, but I guess that could be considered a good thing.
- b2gills 7y agoPerl6 has a fallthrough keyword named `proceed`. It also has a keyword named `succeed` which is the default for `when` blocks. given 1 { when Int { say 'integer'; proceed } if 0 ≤ $_ ≤ 9 { say 'digit'; succeed } when 1 { say 'one'; proceed } } The above will print two lines `integer` and `digit`. It will not get a chance to run the second `when` block because of the `succeed` that gets run before it. If you want the `given` block to return a value, `succeed` can be given a value. my $result = do given $example { when 1 { 'one' } when 2 { succeed 'two' } # succeed is redundant here if $_ == 3 { succeed 'three' } # acts like a `when` block and returns "three" … } --- Perl6 is “supposed” to have a `goto` keyword, but nobody has implemented it. Mainly because of a lack of need. One of the few good uses of `goto` in a higher level language is to have a portion of a function always run at the function exit. Perl6 has `LEAVE`, `KEEP`, and `UNDO` which handles this nicely. sub foo (){ my $fh = open 'example.txt', :r; LEAVE $fh.close; # close $fh at scope exit … # use the opened file handle } Technically it would probably be better to have the `.close` be conditional on whether the $fh was successfully opened. sub foo (){ my $fh = open 'example.txt', :r; LEAVE with $fh {.close} without $fh {.throw}; # throw the error in `$fh` # (This isn't necessary, as it will get thrown as soon as you use it.) … # use the opened file handle }
- codr7 7y agoI'm usually looking for two kinds of switching, most implementations support one of them. Switching on a list of conditions in priority order like CL's COND, and switching on a value. Simply making the value optional and injecting it in specified conditions if present would allow using the same construct for both. Note that this may well be possible in P6, most things are; but I have yet to come across and example of COND-behavior, they're all switching on values from what I can see.
- aasasd 7y agoBehold: switch (true) { case a > b: ... case isFullMoon(): ... }
- codr7 7y agoThanks, I have yet to come across anything that P6 doesn't have a feature for. The languages [0] I've designed so far don't even come close in complexity, and I'm still balancing the edge of my abilities to keep track of everything. If they manage to make that monster run reasonably correct and fast, I'm seriously impressed. [0] https://github.com/codr7/g-fu/tree/master/v1 https://github.com/codr7/g-fu/tree/master/v1
- aasasd 7y agoWell, that's not specifically Perl above―it should work in most languages with `switch`. It's also highly questionable to my taste, as I don't see why `if` wouldn't do. I've chastised a dude many a time for its use, but that was only one of his dubious choices in life, so...
- rwmj 7y agoThey've reinvented OCaml / SML pattern matching. I wonder if they also compile it to efficient machine code? Xavier Leroy's famous ZINC paper goes into how you do that, and it's pretty complicated (page 64 of http://caml.inria.fr/pub/papers/xleroy-zinc.pdf http://caml.inria.fr/pub/papers/xleroy-zinc.pdf)
- dan-robertson 7y agoNot really. Let’s first consider how this code would be translated to ocaml. One would expect to use variants instead of strings, so e.g. gender should be: type t = | Masculine | Feminine | Mixed And then one can match on them and match on tuples and so on. But what about countries? In this case you could write out all the cases separately (difficult to maintain), or you could use polymorphic variants to achieve the @foo.any behaviour: type vosotros = [ `Spain | ... ] type dialect = [ vosotros | ... ] And then do e.g. match dialect with | #vosotros -> ... But this has the disadvantages that polymorphic variants usually bring (difficulties with type inference/exhaustiveness checks). So instead one would probably have some function to decide if a dialect Is vosotros, and then match on the result of that, a slightly annoying to read boolean. And one would also need to match on the (perhaps) nonsense case of singular mixed-gender. One can get around this with types in one of two annoying ways. Either combine gender and number: type gender = Masculine | Feminine type genders = Masculine | Mixed | Feminine type number = | Singular of gender | Plural of genders Which is not great because it is now hard to extract the gender if that is all one wants (but it is perhaps not grammatically useful); or one can use GADTs: type singular = [ `singular ] type plural = [ `plural ] type 'number gender = | Masculine : 'a gender | Feminine : 'a gender | Mixed : plural gender type 'number number = | Singular : singular number | Plural : plural number type t = T : { number : 'num number; gender : 'num gender } -> t But this is also unpleasant. And how does one do other smart-match predicates? For example * > 0. This could either be: match number, ... with | num, ... when num > 1 -> ... Which separates the important part (num > 1) from the unimportant part (giving it the name when you don’t care), and you still need a catch all because of the exhaustiveness checks. So maybe instead you would have a helper function to change the number into a variant like One | Zero | Many. This also ignores the case when one might want to write some more traditional style Perl and put regular expressions as the predicates of those when blocks. This isn’t really an ocaml/SML thing to do but that doesn’t mean it should be disregarded as something bad/useless.
- skywhopper 7y agoI feel like you can approach most of this with Ruby’s `case`. Not sure about the `when @ary.any` but it wouldn’t be hard to monkeypatch a fix (not that it would be a good idea).
- absm 7y agoWhat is with this incessant indoctrination in "tech"? The most important question of the 21st century seems to be the kind of thing that traditionally was a major topic among 4 year olds in kindergarten.
- ohithereyou 7y agogiven/when is also available in Perl 5 from 5.10.1 onward[1]. There are times when it is very helpful. [1] https://perldoc.perl.org/perlsyn.html#Switch-Statements https://perldoc.perl.org/perlsyn.html#Switch-Statements
- Grinnz 7y agoDon't use it in Perl 5. You have to first read the whole documentation on what when does[1] and then the whole documentation on what smartmatch does[2] and then you probably still won't know what will happen if you compare two values that might or might not be numbers, because Perl does not make this distinction for you, so smartmatch guesses. There is a reason every other operator has explicit stringy and numeric comparison versions, and optional new stringy bitwise operators were added because they had the same ambiguity[3]. [1] https://perldoc.pl/perlsyn#Experimental-Details-on-given-and-when https://perldoc.pl/perlsyn#Experimental-Details-on-given-and... [2] https://perldoc.pl/perlop#Smartmatch-Operator https://perldoc.pl/perlop#Smartmatch-Operator [3] https://perldoc.pl/feature#The-'bitwise'-feature https://perldoc.pl/feature#The-'bitwise'-feature
- Grinnz 7y agoA more explicit switch statement for only strings or numbers is available from CPAN: https://metacpan.org/pod/Switch::Plain https://metacpan.org/pod/Switch::Plain
- harryf 7y agoEvery time I see something about Perl 6 I think this is awesome. Shame that a younger generation of programmers will never use it, because it’s not the new cool. If they renamed Perl 6 into something cool like “Liquid” or some think you find in the kitchen, like Chilli. Then hide away it’s origins a bit, they’d have a language kids would be raving about. I know that’s not the point but I can’t think of any programmer I know today that doesn’t talk about Perl with a certain contempt. “It’s Perrrrl” eyes up
- noir_lord 7y agoIf you want to make Perl 6 'cool' just create an alternate syntax that maps to Perl 6. cough Elixir cough
- dnautics 7y agoTo be fair, elixir mostly inherited pattern matching case from erlang
- Retra 7y agoIf you think merely rebranding something will make it more acceptable, then perhaps you've found the real reason people don't like Perl: it is a language that appears to be built around the superficial. Like someone who takes 30 mins crafting their bed hair in an effort to prove they don't care about appearances.
- microwavecamera 7y ago> then perhaps you've found the real reason people don't like Perl: it is a language that appears to be built around the superficial How so? That's an odd statement without citation or clarification. If anything the opposite is true. It's become a trend in the industry to let superficial attitudes shape popular opinion. You're not "cool" if you use languages like Perl and PHP. You're supposed to criticize Perl even if you never used it or even understand it. The idea that Perl 6 should be called something else isn't because of the superficiality of Perl but of popular misconceptions and misguided opinions erroneously excepted as truisms in the tech community. Perl 6 is not a the next "version" of Perl 5. It's a completely different language but because it's still called "Perl" people tend to dismiss it out of hand. That and it's confusing to call it "Perl 6" when it isn't related to Perl 5 and doesn't even share a codebase with Perl 5.
- makecheck 7y agoI find it odd that no language seems to have a syntax short-cut (like operators) for something as common as switches; the closest thing might be a shell script! This is a very common thing to write. Why couldn’t a switch avoid all keywords and look more like a list...such as: ?(value): - 0: { doThing(); } - 1: { otherThing(); } - *: { doDefault(); }
- rbonvall 7y agoHaskell's syntax is pretty minimal: f 0 = doThing f 1 = otherThing f _ = doDefault