Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

do-block with keyword arguments #50395

Open
tpapp opened this issue Jul 3, 2023 · 15 comments
Open

do-block with keyword arguments #50395

tpapp opened this issue Jul 3, 2023 · 15 comments
Labels
feature Indicates new feature / enhancement requests parser Language parsing and surface syntax

Comments

@tpapp
Copy link
Contributor

tpapp commented Jul 3, 2023

I could not find out how to use do-blocks with keyword arguments, so either it is not possible or underdocumented. Cf

just_call(f, args...; kwargs...) = f(args...; kwargs...)
just_call((x; z) -> x + z, 1; z = 2)

and the "logical" do-block version would be

just_call(1; z = 2) do x; z
       x + z
end

which fails with

ERROR: MethodError: no method matching (::var"#17#18")(::Int64; z::Int64)

Closest candidates are:
  (::var"#17#18")(::Any) got unsupported keyword argument "z"
   @ Main REPL[34]:1

(FWIW, this is on Julia 1.9.1).

Cf discourse, I could not find an open issue.

@jishnub
Copy link
Contributor

jishnub commented Jul 4, 2023

Related: #40313

@brenhinkeller brenhinkeller added the feature Indicates new feature / enhancement requests label Aug 4, 2023
@stevengj
Copy link
Member

I think it would need to be

just_call(1; z = 2) do (x; z)
       x + z
end

to avoid ambiguity with the ; ending the line?

@stevengj stevengj added the parser Language parsing and surface syntax label Feb 10, 2024
@o314
Copy link
Contributor

o314 commented Aug 13, 2024

@simonbyrne makes a good point against this here https://discourse.julialang.org/t/allowing-for-keyword-arguments-in-do-block/19244

That syntax wouldn’t really be consistent, since

f(args) do (x,y)
    #body
end

gives an anonymous function which takes a single tuple as an argument, i.e. is

f(function ((x,y),)
    #body
end, args)

The way to specify a multiple argument function is without parentheses, i.e.

f(args) do x,y
   #body
end

Unfortunately using a semicolon as a keyword separator won’t work, as it is already used as an expression delimiter, i.e.

f(args) do x,y; kw=1
   #body
end

is equivalent to

f(function (x,y)
    kw = 1
    #body 
end, args)

Another solution :
Handle a special mode (breathe deeply and enjoy - without any parenthesis juggling) in the parser at the do form level.
Most frequently the do form expects quickly a line break after the statement of the args.
One can capture there the first ';' before first NL, its left side, and give it back as a passing of some named args.
Some work for @c42f ?

@c42f
Copy link
Member

c42f commented Aug 13, 2024

This is highly related to #54915 - the root cause is the same and the "real solution" IMO is to do what Jeff suggested here #54915 (comment)

See #54915 (comment) and #54903 for ways we might achieve this breaking change without waiting for Julia 2.0

@o314
Copy link
Contributor

o314 commented Aug 13, 2024

Thanks for your quick feedback.
I have not seen all those points before.
That seems strange however IIUC, since parenthesis may be peeled differently if we use a do block (right side) or not (left side of the call). I have added another proposal

@cheengshuchin
Copy link

Ok, I may find a solution for This issue.
you may try this....

A=[1,2,3]
B=[4,5,6]
C=[(),(:info => false,),(:info => true, :msg => "tyf")]

function func(a,b;info=true,msg="CSC")
  if info
    @info "Hi, $msg"
    @info "calling func with a=$a , b=$b , info=$info and msg=$msg"
  end
  @info "__________________"
  a+b
end

hh=map(A,B,C) do args...
  func( args[begin : end-1]...; args[end]...)
end

this will produce output as this

[ Info: Hi, CSC
[ Info: calling func with a=1 , b=4 , info=true and msg=CSC
[ Info: __________________
[ Info: __________________
[ Info: Hi, tyf
[ Info: calling func with a=3 , b=6 , info=true and msg=tyf
[ Info: __________________
3-element Vector{Int64}:
 5
 7
 9

As you can see, this helps send needed keyword arguments via Do-Block Args easily without modifying the native Julia code base.

Happy Coding
Cheeng Shu Chin
[email protected]

@o314
Copy link
Contributor

o314 commented Oct 20, 2024

If that can help

# def
f(g, args...; kwargs...) = g((; kwargs...), args...)
# use
f(1,2,3; a=1, b=2) do kwargs, args...; (args..., kwargs) end
(1, 2, 3, (a = 1, b = 2))

@cheengshuchin
Copy link

I think it should be

f(g, args...; kwargs...) = g(kwargs, args...)
f(1,2,3; a=1, b=2) do kwargs, args...
       (args..., (kwargs...,))
end

generate output as

(1, 2, 3, (:a => 1, :b => 2))

which can be used in Do Block..

Could you correct me if I'm wrong/miss something??

Happy Coding
Cheeng Shu Chin
[email protected]

@cheengshuchin
Copy link

cheengshuchin commented Oct 20, 2024

then we can modify and improve it

A=[1,2,3]
B=[4,5,6]
C=[(),(:info => false,),(:info => true, :msg => "tyf")]

in this case, function func not need to modify

function func(a,b;info=true,msg="CSC")
         if info
           @info "Hi, $msg"
           @info "calling func with a=$a , b=$b , info=$info and msg=$msg"
             end
         @info "__________________"
           a+b
       end
hh=map(C, A, B) do kwds,args...
       func( args...; kwds...)
       end

which generated the same output but did not need to use Range Unit..

[ Info: Hi, CSC
[ Info: calling func with a=1 , b=4 , info=true and msg=CSC
[ Info: __________________
[ Info: __________________
[ Info: Hi, tyf
[ Info: calling func with a=3 , b=6 , info=true and msg=tyf
[ Info: __________________
3-element Vector{Int64}:
 5
 7
 9
julia> hh
3-element Vector{Int64}:
 5
 7
 9

hope this can help!!

Happy Coding
Cheeng Shu Chin
[email protected]

@o314
Copy link
Contributor

o314 commented Oct 20, 2024

Somewhat a matter of choice.
In your solution you only uses tuples (last one is a tuple of pair), not namedtuple (nt), that are useful. My personal opinion about this is

  • keep using nt eagerly as long as one can and downcast them lazily, often when an external function call or an assign will require it.
  • (old) code written only with tuple of pairs tends to be quite long and hard to read . see IOContext, quite polluted with puncts everywhere.
  • autodocument code by returning namedtuple from private functions. Compiler will check them a bit.

About the syntax. form (kwargs...,) works but (; kwargs...) is more consistent

using Test
nt = (a=1, b=2)
@test nt == (a = 1, b = 2)
@test (nt..., c=3) == (a = 1, b = 2, c = 3)
@test (nt..., ) == (1, 2)

nt = (; a=1, b=2)
@test nt == (; a=1, b=2)
@test (; nt..., c=3) == (; a=1, b=2, c=3)
@test (; nt...) == (; a=1, b=2)

@cheengshuchin
Copy link

yes, it's a matter of choice.
if I change the C to nt

C=[(),(info = false,),(info = true, msg = "tyf")]

the result will still be the same.
but the core question here is to figure out a way to pass the keyword-assigned arguments via Do-Block syntax,
which is not possible (in general comments on the web) with minimum modification in the native Julia code base currently.

somehow NT is more convenient for accessing data compared to Pairs, but I think Kwds is more like a Pairs rather than NT in nature. maybe it is related to the speed or performance of Julia in runtime.

as Julia is New compared to other programming languages, Polluted and hard to read are common,
we should help to improve/make it more friendly and easy to use/explore.... :)

Happy Coding
Cheeng Shu Chin
[email protected]

@o314
Copy link
Contributor

o314 commented Oct 20, 2024

Quite agree with your points.
Collecting keywords on the first arg is the way Julia works under the hood.
see https://docs.julialang.org/en/v1/devdocs/functions/#Keyword-arguments
Even if things are not completely stabilized for now, that's a wise choice among any options for the future
Keep learning.
Happy coding too :)

@cheengshuchin
Copy link

Yes, wise choice among others...

Happy Coding
Cheeng Shu Chin
[email protected]

@tpapp
Copy link
Contributor Author

tpapp commented Oct 21, 2024

@cheengshuchin: It is understood that you can work around this issue with a closure (and a macro, etc), that is not the point of opening an issue. Please continue unrelated discussion on Discourse.

@cheengshuchin
Copy link

cheengshuchin commented Oct 21, 2024

So this is a discussion thread for feature requests by throwing an issue thicket in here?!
if this is the case, I have to say "Sorry" to all...

as I just trying to help by offering another way to solve it via encapsulating Keyword Arguments in normal positional arguments in normal Do-Block without using any fancy technique like closure or macro etc (Which I using in Decorator/Convolution via Julia Do-Block Syntac).

Sorry as I did not deliver what you expected for.

Happy Coding and Nice Discussion
Cheeng Shu Chin
[email protected]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests parser Language parsing and surface syntax
Projects
None yet
Development

No branches or pull requests

7 participants