# Do-Block Syntax with Where Keyword

**URL:** <https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131>\
**Category:** General Usage\
**Tags:** finalization\
**Created:** [June 3, 2024, 5:14pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131 "2024-06-03T17:14:56Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 3, 2024, 5:14pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/1 "2024-06-03T17:14:56Z")

</div>

I want to use a `finalizer` for a parametric type using the type parameter in the finalizer. I have a solution for that, but I wondered whether I can use the `do` syntax, but I simply do not know where the `where` needs to be put to be syntactically correct.  
The following examples need the REPL to fire the finalizer.

Using the do syntax without a parameter works:

```julia
mutable struct MySimpleStruct
    x::String
end
s = MySimpleStruct("What is the meaning of life?");
finalizer(s) do s
    @async println("Finalizing: $(s.x)")
    return s
end;
finalize(s)

```

With a parametrized type, this gets a “`T` not defined” error, because `where` is missing and I do not know where to put it:

```julia
mutable struct MyStruct{T}
    x::T
end
s = MyStruct("What is the meaning of life?");
finalizer(s::T) do s
    @async println("Finalizing: $(s.x) using $(T |> nameof)")
    return s
end

```

Working around this is possible, but misses the typical Julia elegance:

```julia
mutable struct MyStruct{T}
    x::T
end
s = MyStruct("What is the meaning of life?");
function myfinalize(s::T) where T
    @async println("Finalizing: $(s.x) using $(T |> nameof)")
    return s
end;
finalizer(myfinalize, s);
finalize(s)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [June 3, 2024, 5:26pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/2 "2024-06-03T17:26:24Z")

</div>

~~I don’t recall anonymous functions (which is what `do` clauses really are) supporting `where` clauses~~ see below for an example by @savq — the main purpose of `where` is to control dispatch, which doesn’t arise for anonymous functions, since they don’t (normally) have multiple methods.

You can just do `T = typeof(s.x)` in the body of your function (`do` clause) to get the type.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [June 3, 2024, 5:49pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/3 "2024-06-03T17:49:50Z")

</div>

You could also pass an anonymous function without using `do`:

```julia
mutable struct MyStruct{T}
    x::T
end

s = MyStruct("What is the meaning of life?");

finalizer(
    function(s::MyStruct{T}) where T
        println("Finalizing: $(s.x) using $(T)")
    end,
    s
)

```

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [June 3, 2024, 7:20pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/4 "2024-06-03T19:20:05Z")

</div>

> [@stevengj](#):
>
> the main purpose of `where` is to control dispatch, which doesn’t arise for anonymous functions, since they don’t have multiple methods.

I think this is a parser thing, not a functionality thing. It’s doable with anonymous functions:

```julia
julia> f = ((x::Vector{T}) where T) -> println(T)
#1 (generic function with 1 method)

julia> f(rand(4))
Float64

julia> f(rand(Float32,4))
Float32

```

However, doing the same with the `do` syntax fails because it’s parsed differently.

```julia
julia> dump(:(((x::Vector{T}) where T) -> println(T)))
Expr
  head: Symbol ->
  args: Array{Any}((2,))
    1: Expr
      head: Symbol where
      args: Array{Any}((2,))
        1: Expr
          head: Symbol ::
          args: Array{Any}((2,))
            1: Symbol x
            2: Expr
              head: Symbol curly
              args: Array{Any}((2,))
                1: Symbol Vector
                2: Symbol T
        2: Symbol T
...

```

whereas the do arguments are parsed diffently:

```julia
julia> dump(:(finalizer(v) do ((x::Vector{T}) where T); println(T) end))
Expr
  head: Symbol do
  args: Array{Any}((2,))
    1: Expr
      head: Symbol call
      args: Array{Any}((2,))
        1: Symbol finalizer
        2: Symbol v
    2: Expr
      head: Symbol ->
      args: Array{Any}((2,))
        1: Expr
          head: Symbol tuple
          args: Array{Any}((1,))
            1: Expr
              head: Symbol ::
              args: Array{Any}((2,))
                1: Symbol x
                2: Expr
                  head: Symbol where
                  args: Array{Any}((2,))
                    1: Expr
                      head: Symbol curly
                      args: Array{Any}((2,))
                        1: Symbol Vector
                        2: Symbol T
                    2: Symbol T
   
 ...

```

i.e. a `tuple` arg, not a `where` arg for the `->` operator.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 3, 2024, 8:49pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/5 "2024-06-03T20:49:59Z")

</div>

> [@stevengj](#):
>
> which doesn’t arise for anonymous functions, since they don’t have multiple methods

NB: this is incorrect:

```julia-repl
julia> f = () -> 3
#1 (generic function with 1 method)

julia> (::typeof(f))(::Any) = 7

julia> f
#1 (generic function with 2 methods)

julia> methods(f)
# 2 methods for anonymous function "#1":
 [1] (::var"#1#2")()
     @ REPL[1]:1
 [2] (::var"#1#2")(::Any)
     @ REPL[2]:1

```

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 4, 2024, 5:00am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/6 "2024-06-04T05:00:19Z")

</div>

Thanks for the insights. Sorry that I didn’t mention it, but inside the `finalizer` `T` is used to hand over a parameter to a `@ccall` (as `Ref{T}`), so using `typeof` is unfortunately not an option, as it results in:

`ERROR: LoadError: could not evaluate ccall argument type (it might depend on a local variable)`

Although `where`’s main purpose is to control dispatch, it’s not its only purpose.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 4, 2024, 5:21am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/7 "2024-06-04T05:21:25Z")

</div>

That’s pretty ingenious.  
Following from that, this is probably trivial, but I needed to see it, too, for anonymous functions created with a do-block:

```julia
julia> global global_f

julia> savef(f) = global global_f = f
savef (generic function with 1 method)

julia> savef() do x
           "any"
       end
#1 (generic function with 1 method)

julia> (::typeof(global_f))() = 7

julia> global_f
#1 (generic function with 2 methods)

```

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 4, 2024, 5:26am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/8 "2024-06-04T05:26:31Z")

</div>

Do you know if there is a technical reason (conflicting syntax) why the parser cannot be extended to support the where clause?

In my opinion we have now three good reasons why the do-block should support the `where` clause and I should fill a feature request:

- To support multiple dispatch in case of multiple method definitions
- To support cases where the type is used statically
- For consistency reasons: Ideally each way of defining functions should support all functionality

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 4, 2024, 6:10am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/9 "2024-06-04T06:10:26Z")

</div>

This is interesting, as based on that, the following will also work without stretching the `finalizer` call:

```julia
function(s::MyStruct{T}) where T
    println("Finalizing: $(s.x) using $(T)")
end |> Base.Fix2(finalizer, s)

```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 4, 2024, 2:19pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/10 "2024-06-04T14:19:44Z")

</div>

> [@PatrickHaecker](#):
>
> Do you know if there is a technical reason (conflicting syntax) why the parser cannot be extended to support the where clause?

Rather than `finalizer(s::T) do s` (with a where… somewhere), the syntax should be something like:

```julia-auto
finalizer(s) do s::T where T
    #...
end

```

Unfortunately this does currently mean something — it’s an anonymous function like:

```julia-auto
function anon(s::T where T)
    # no T binding available here :(
end

```

The `do` syntax doesn’t use parentheses, so I don’t think there’s a way to “move” the where clause _outside_ of parentheses that don’t exist. I suppose we _might_ be able to use something like `do (elt::T) where T`, but boy is that fiddly — especially when you start adding multiple args.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [June 4, 2024, 8:58pm UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/11 "2024-06-04T20:58:29Z")

</div>

It’s actually possible with a quite simple macro which rewrites the `do` syntax to `->` syntax. I haven’t tested it thoroughly. I suspect it’s just a bit of luck with the parser.

```julia
using MacroTools: @capture

macro wheredo(ex)
    @capture(ex, F_(fargs__) do args__; body__; end) || 
        error("Can't find do construct: $ex")
    :($F($(args...) -> $(body...), $(fargs...))) |> esc
end

@wheredo map(1:4, ["a","b","c","d"]) do (x::S, y::T) where {S, T}
    println("S: ", S, ", T: ",T)
    string(x) * string(y)
end

```

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 5, 2024, 5:50am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/12 "2024-06-05T05:50:52Z")

</div>

Maybe it would be possible to change the parsing of

```julia
finalizer(s) do s::T where T
    #...
end

```

to mean:

```julia
function anon(s::T) where T
    # T binding available here :)
end

```

I see the following advantages for new code:

- As there are no parentheses, that’s exactly what I would expect seeing this code
- Every use case where `T` is only available in the function arguments should also be possible if `T` is additionally available in the function body
- If there should come up the need of having `T` only available in the function arguments this could be added at any time by supporting a notation using parentheses (adding previously non-existing parentheses is far simpler than removing previously non-existing ones ;-))

I understand that, strictly speaking, this would be an incompatible change. However, I assume the risk is really low, because

- If someone had used `do` together with `where` that would have been based on undocumented behavior
- The current syntax seems to support only single arguments, where the `T` is only available in function arguments, and this is rather pointless
- The behavior would only change if there is an identically named identifier (typically `T`) already used inside the `do` function body

The compound probability of all three small probabilities seems to be very small. If we wanted to be even more careful, we could probably parse the packages registered in the Julia package registry before and after the parser change and verify that the Exprs are unchanged.  
The risk of non-public code being changed would not be eliminated, but it could strengthen the assumption that the probability for this is very low.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 5, 2024, 6:16am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/13 "2024-06-05T06:16:07Z")

</div>

> [@sgaure](#):
>
> `macro wheredo(ex)`

Interesting solution, thanks. Both using the parentheses after the `do` and the curly braces after the `where` seem to be essential in order to have this working.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [June 5, 2024, 7:04am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/14 "2024-06-05T07:04:11Z")

</div>

I think it might be fairly easy to allow `... do (x::T) where T`, i.e. with parentheses. And curly braces around `{T, S}` if more type variables. It’s already being parsed, and the simple rewriting to `->` in the above `@wheredo` is sufficient. The parser rewrites to `->` as well, but in a slightly different manner which makes this construction fail. However, who knows if this would break something.

However, the precedence of `where` must allow constructions like `f(x::S, y::T where S<:T<:Real) where S = x+y` being parsed as `(T where S<:T<:Real)`. For this reason the `@wheredo` macro above will fail with `... do x::T where T`, i.e. `T` will not be visible inside the body. This form of `do` is already legal and works, but `T` is not visible.

For this reason, `where` has a different precedence if it follows a `::`. That’s why constructions like `f(x::S, y::T)::S where {S,T} = x + y` don’t work, you need parentheses: `(f(x::S, y::T)::S) where {S,T} = x + y`.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 5, 2024, 8:43am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/15 "2024-06-05T08:43:34Z")

</div>

> [@PatrickHaecker](#):
>
> could probably parse the packages registered in the Julia package registry before and after the parser change and verify that the Exprs are unchanged

The Julia project already uses something like this as part of some workflows, Nanosoldier uses PkgEval to run the test suites of all (or selected) registered packages before and after a change to Julia. See here:

> **[GitHub - JuliaCI/PkgEval.jl: Keeping tabs on the julia ecosystem](https://github.com/JuliaCI/PkgEval.jl)**
>
> Keeping tabs on the julia ecosystem

> **[GitHub - JuliaCI/Nanosoldier.jl: A package for running JuliaCI services on MIT's...](https://github.com/JuliaCI/Nanosoldier.jl)**
>
> A package for running JuliaCI services on MIT's Nanosoldier cluster

> **[GitHub - JuliaCI/NanosoldierReports: A repository for human-readable reports generated...](https://github.com/JuliaCI/NanosoldierReports)**
>
> A repository for human-readable reports generated by the Nanosoldier.jl CI system.

---

<div class="post-metadata">

**Author:** ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)\
**Post date:** [June 6, 2024, 7:57am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/16 "2024-06-06T07:57:18Z")

</div>

I figured out it’s a two-liner in `JuliaSyntax/src/parser.jl` to support `where` clauses in `do`, provided the argument list is enclosed in parentheses.

```julia
2308c2308,2309
< emit(ps, m, K"tuple")
---
> k = peek_behind(ps, position(ps)).kind
> k != K"where" && emit(ps, m, K"tuple")

```

Then you can do things like:

```julia
julia> foreach([1,2.0,"foo", false]) do (x::T) where T; println(T); end
Int64
Float64
String
Bool

```

I haven’t got the time to follow up a PR, but please do anyone.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [June 7, 2024, 6:46am UTC](https://discourse.julialang.org/t/do-block-syntax-with-where-keyword/115131/17 "2024-06-07T06:46:28Z")

</div>

Thanks for this extra explanation. I never understood before, why the parentheses are necessary in the case of defining a return type.
