7

I came across this JS code snippet:

function append(demo = "", file = "") {
    const extra = "ctl=1&embed=1";
    if (demo && file)  return `/${demo}/?file=${file}&${extra}`;
    if (!demo && file) return `/?file=${file}&${extra}`;
    if (demo && !file) return `/${demo}&${extra}`;
    return `?${extra}`; 
}

I can port this easily to Raku using ifs but I thought using given/when would be a nice way of showcasing that feature. I came up with the following:

sub append(Str :$demo, Str :$file) {
    my $extra = "ctl=1&embed=1";
    given ($demo, $file) {
        when $demo.so,  $file.so  { "/$demo/?file=$file&$extra" } 
        when $demo.not, $file.so  { "/?file=$file&$extra" } 
        when $demo.so,  $file.not { "/$demo?$extra" } 
        default                   { "?$extra" }
    }
}

say append :file("here"), :demo("there"); # /there/?file=here&ctl=1&embed=1
say append :file("here");                 # /?file=here&ctl=1&embed=1
say append :demo("here");                 # /here?ctl=1&embed=1
say append;                               # ?ctl=1&embed=1

However it's quite repetitive and I'm not even sure if it's idiomatic Raku so I figured I could do the following:

sub append(Str :$demo = '', Str :$file = '') {
    my $extra = "ctl=1&embed=1";
    given ($demo.so, $file.so) {
        when (True,  True)  { "/$demo/?file=$file&$extra" } 
        when (False, True)  { "/?file=$file&$extra" } 
        when (True,  False) { "/$demo?$extra" } 
        default             { "?$extra" }
    }
}

This doesn't work as expected though. Thus, is it even possible to bind a list of values to the topic variable $_ and smartmatch against it with whens? I (mis)remember Daniel "codesections" Sockwell doing something to this extent but I cannot remember where.

jubilatious1
  • 1,999
  • 10
  • 18
uzluisf
  • 2,586
  • 1
  • 9
  • 27
  • 1
    Shouldn't the`when` blocks contain `$^a` and `$^b` anonymous variables, or similar? – jubilatious1 May 14 '23 at 20:01
  • 1
    @jubilatious1 I get `Too few positionals passed;` if I do that. Also – uzluisf May 15 '23 at 00:01
  • 1
    Maybe helpful? https://stackoverflow.com/questions/66233465/haskell-like-pattern-matching-in-raku – jubilatious1 May 15 '23 at 02:15
  • Thanks! My example is different but I think it boils down to the same question posed in that other post. So this question can be marked as duplicate. – uzluisf May 16 '23 at 22:10
  • 1
    But you show manipulation of two variables? So different, I think. – jubilatious1 May 16 '23 at 22:17
  • That's a good point. I was thinking of a list of values as a single unit, which then get "destructured" (is this even a thing in Raku?) into two variables. The answers there made it clear to me why what I'm trying to do isn't (currently) possible. – uzluisf May 17 '23 at 12:44
  • 1
    @codesections references: https://www.codesections.com/blog/try-some-pattern-matching/ and also https://www.codesections.com/blog/pattern-matching-2/ – jubilatious1 May 17 '23 at 14:11

2 Answers2

4

I ran your code and got this error:

Use of uninitialized value of type Str in string context.
Methods .^name, .raku, .gist, or .say can be used to stringify it to something meaningful.

You can just add default values in the append() signature like this to initialize the argument values:

sub append(Str :$demo='', Str :$file='') {

Oh and no need for do given, just plain given is fine.

librasteve
  • 6,832
  • 8
  • 30
  • Thanks, I had that example with default args but it seems I copied the version w/o default values :'( – uzluisf May 14 '23 at 22:53
  • I added the default values to the 2nd version but I just ran the 1st one and it doesn't throw any error, which is probably why I mistakenly left them out in the 2nd version. I'm now wondering the reason for discrepancy. – uzluisf May 14 '23 at 23:02
  • 1
    so what you are saying is that you do not have a question for the SO community, right? if that's the case, then perhaps you can delete it. if not you will need to rephrase / edit the original question for me to understand it ... – librasteve May 16 '23 at 18:09
  • 1
    No, my comment is about having corrected the "uninitialized value of type Str in string context" error, which isn't related with the question at hand, i.e., can I bind a list of values to the topic variable $_ and smartmatch against it with `when`s? – uzluisf May 16 '23 at 22:03
2

I came up with the following

This works by a coincidence. You use smartmatch against True

You do not need given in this case:

sub append(Str :$demo, Str :$file, :$extra = "ctl=1&embed=1") {
    when $demo.so  &  $file.so  { "/$demo/?file=$file&$extra" }
    when              $file.so  { "/?file=$file&$extra" }
    when $demo.so               { "/$demo?$extra" }
    default                     { "?$extra" }
}

can I bind a list of values to the topic variable $_ and smartmatch against it with whens?

Yes, you can:

sub append(Str :$demo, Str :$file, :$extra = "ctl=1&embed=1") {
    given ($demo, $file) {
        when :so , :so  { "/$demo/?file=$file&$extra" }
        when :!so, :so  { "/?file=$file&$extra" }
        when :so , :!so { "/$demo?$extra" }
        default         { "?$extra" }
    }
}

You can use smartmatch against Signature:

sub append(|c (Str :$demo, Str :$file)) {
    my $extra = "ctl=1&embed=1";
    given c {
        when :(:demo($)!, :file($)!)  { "/$demo/?file=$file&$extra" }
        when :(:file($)!)             { "/?file=$file&$extra" }
        when :(:demo($)!)             { "/$demo?$extra" }
        default                       { "?$extra" }
    }
}

but it is more fun to use multi-dispatch

constant $extra = 'ctl=1&embed=1';
proto append (Str :$demo,Str :$file) {*}
multi append (:$demo!, :$file!) { "/$demo/?file=$file&$extra" }
multi append (:$file!)          { "/?file=$file&$extra" }
multi append (:$demo!)          { "/$demo?$extra" }
multi append ()                 { "?$extra" }
wamba
  • 3,883
  • 15
  • 21