Why no do-blocks for regex matching

I’m just wondering why the standard library doesn’t include a method for match defined as

function match(f::Function, r::Regex, args...)
    m = match(r, args...)
    if !isnothing(m)
        f(m)
    end
end

This allows to write

match(rx, string) do m
    @show m
end

as opposed to

m = match(rx, string)
if !isnothing(m)
    @show m
end

A do-block for regex matching feels like a very natural pattern (one that Python nicely enabled with the “walrus operator” in Python 3.8).

You can employ syntax almost identical to the linked Python example’s in Julia, because Julia’s assignment operator (=) has always behaved much like Python’s new-fangled := in that it returns its right-hand side. e.g.

if (m = match(rx, str)) !== nothing
    @show m
end

(That being said, Python currently allows := in more contexts than Julia, e.g. Julia doesn’t allow assignments inside array literals.)

6 Likes

In general, Julia’s standard library typically supports do block syntax for functions that naturally accept function arguments (e.g. map), functions that require resource cleanup (e.g. open), and for cases that the user cannot emulate efficiently/concisely with a sequence of separate calls (e.g. get! on a dictionary).

match seems to fall into none of those categories. Even without putting the assignment into the if, there’s nothing particularly bad about puting m = match(...) on a separate line from if isnothing(m) as far as I can tell.

3 Likes

Well, I would argue for the “conciseness” case, but fair enough :wink:

The most immediate answer to your “why” question is that nobody has tried to make it happen (write a PR, advocate for it, successfully get it merged). If they had, we’d be able to find/cite a PR with reasons for or against. It’s not terribly unlike this idea for replace, though (#24598) — and there you’ll find little excitement and some other concerns.

1 Like

That case is quite different: replace already accepts a function argument, so the proposal was merely to move it to the first argument to exploit do syntax.

2 Likes

I quite like this syntax but I think it would be clearer to introduce an ifmatch function:

ifmatch(re, str) do m
    # body
end

A downside of this compared to using an actual if statement is that there’s no way to add an else clause, but that may be fine.

4 Likes

The syntax is a little bit tricky, but assignment inside array literals does seem to work:

julia> xyz
ERROR: UndefVarError: xyz not defined

julia> [(xyz = 2;), 3]
2-element Vector{Int64}:
 2
 3

julia> xyz
2

4 Likes

What about a higher-order function like when-let?

function when_let(body, val)
    if val !== nothing
        body(val)
    end
end

That would be a bit more general, and apply to any function that uses Nothing as a sentinel value:

julia> when_let(match(r"\d+", "a42b")) do m
           println(m.match)
       end
42

julia> a = collect(11:15);
julia> when_let(findfirst(iseven, a)) do i
           println("$i => $(a[i])")
       end
2 => 12
4 Likes