Skip to content

Support function return types in longdef/shortdef #35

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

Closed
wants to merge 20 commits into from

Conversation

cstjean
Copy link
Collaborator

@cstjean cstjean commented Mar 30, 2017

I also have a function that uses MacroTools to parse any function definition and return (name, arg, kwargs, body, return_type). Would that be accepted?

@cstjean
Copy link
Collaborator Author

cstjean commented Mar 31, 2017

The way I use these functions, I just want to transform an expression, not code-walk over the entire tree. Would you mind if I factored out the @match code into a separate function?

@MikeInnes
Copy link
Member

Looks good, thanks! It would probably make sense to have longdef1(ex) = ...; longdef = prewalk(longdef1). For the test issue, would flatten/striplines help? I think the test is fine as is but if you want to use the full version it should be possible. I think the general function parser would be a useful addition too.

@cstjean
Copy link
Collaborator Author

cstjean commented Apr 19, 2017

For the test issue, would flatten/striplines help?

I tried - still problematic. Let's leave it as is.

@cstjean
Copy link
Collaborator Author

cstjean commented Apr 26, 2017

Added parsedef and parsearg. I feel like parse isn't strictly the right verb to use, but I like it a bit more than destruc or match. Any preference? Returning a Nullable as the third value of parsearg is also not super-pleasing. Otherwise, this is ready to merge whenever you're ready to drop 0.4.

src/utils.jl Outdated
push!(positional_args, x)
end
end
return (positional_args, kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified a bit; :parameters will only be in the first slot, if at all. This should probably also handle expressions of the form f(a, b = c) correctly.

src/utils.jl Outdated
return (positional_args, kwargs)
end

""" parsedef(fdef)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about splitdef?

src/utils.jl Outdated
end
```

and returns `(fname::Symbol, args::Vector{Any}, kwargs::Vector{Any}, body_block::Expr, return_type)`. `return_type` is `:Any` if not specified. """
Copy link
Member

@MikeInnes MikeInnes May 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tuple is a bit long, I suggest a dict instead. It makes sense for certain keys to not be present if they're not written.

src/utils.jl Outdated
function parsedef(fdef)
@match longdef1(fdef) begin
(function fname_(args__) body_ end =>
(fname, split_kwargs(args)..., body, :Any))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense to use nothing here to differentiate from Any being explicitly written (or just leave out the key).

src/utils.jl Outdated
default = Nullable(arg_expr.args[2])
arg_expr = arg_expr.args[1]
else
default = Nullable()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using nothing would amount to the same here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then we couldn't recognize f(x=nothing) from f(x), no? Maybe we could return a special NoVal() object instead? Nullables are cumbersome.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In f(x=nothing), :nothing would be a symbol, so that wouldn't be an issue. You could technically write f(x=$nothing) and break it though, so maybe nullable is the right route.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can error in the f(x=$nothing) case. It seems fair enough for a marginal situation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems reasonable enough to me.

src/utils.jl Outdated
end
end
if !(@capture(arg_expr, name_::typ_))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could simplify this as something like @capture(ex, x_::T_ = default_ | x_::T_ | x_)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, but then how can I distinguish no default from default being $nothing (and trigger the error I proposed above)?

@MikeInnes
Copy link
Member

Yeah this is looking like a really useful set of functionality! Sorry for the late review but hopefully you can take a look at those couple of things and this should be good to go.

@cstjean
Copy link
Collaborator Author

cstjean commented May 10, 2017

Thank you for the comments, I'll do the necessary changes. My main use case for this functionality is to augment a function definition somehow, eg.

fname, args, kwargs, body_block, return_type = parsedef(fdef)
quote
    function $fname($(args...); $(kwargs...))::$return_type
         $body_block
    end
end

I think it would make sense to use nothing here to differentiate from Any being explicitly written (or just leave out the key).

It would be annoying to have to write $fname($(args...); $(kwargs...))::$(return_type==nothing ? :Any : return_type), and there's few cases where I'd want to distinguish between ::Any and no return type.

How about returning an object? It'll be shorter to type than a Dict.

immutable FunctionDefinition
    name
    args
    kwargs
    body_block
    return_type
end

@MikeInnes
Copy link
Member

MikeInnes commented May 15, 2017

You could return Any as opposed to :Any to differentiate, though that strictly has the same issue as nothing above. A bonus of the dict approach is that you can write :(f()::$(get(def, :return, Any)) which is not too cumbersome.

@cstjean
Copy link
Collaborator Author

cstjean commented May 22, 2017

Updated. Thank you for all the suggestions, it looks better now.

@cstjean cstjean force-pushed the pull-request/ebeefff7 branch from ba86652 to ed199a6 Compare June 21, 2017 16:38
@cstjean
Copy link
Collaborator Author

cstjean commented Jun 24, 2017

Any chance that we could revisit this? I've got three packages that rely on half-broken versions of this code...

I added support for where syntax, and it turned out rather messy. I can stash it away in a branch until Julia 0.7 if you prefer.

@cstjean cstjean closed this Aug 9, 2017
@cstjean
Copy link
Collaborator Author

cstjean commented Aug 9, 2017

I need a permanent home for this code, so unless I get some news from you, I'll create a new package in the next few days.

@MikeInnes
Copy link
Member

Hey @cstjean, apologies for letting this slip through the cracks -- this is a great PR. I've merged it in 51391fb. Thanks a lot for the effort and patience!

@cstjean
Copy link
Collaborator Author

cstjean commented Aug 11, 2017

🎉 Thanks! My nemesis package, ExprTools.jl was ready to go and passed CI, but thankfully not registered yet.

I've made some improvements that aren't part of this PR. I'll make a new one today-ish, then we can tag?

@MikeInnes
Copy link
Member

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants