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

Implement splitdef/combinedef #66

Merged
merged 13 commits into from
Aug 27, 2019
Merged

Implement splitdef/combinedef #66

merged 13 commits into from
Aug 27, 2019

Conversation

omus
Copy link
Member

@omus omus commented Aug 21, 2019

After looking over the usage of MacroTools in #60 I found that the proposed implementation wasn't handling the various function definitions that can occur. I found the splitdef and combinedef implementations provided by MacroTools but I discovered that those functions were not fully tested and were quite slow. Additionally, the extra dependences MacroTools added seemed unnecessary here.

julia> using BenchmarkTools, MacroTools, Mocking

julia> @btime MacroTools.splitdef(:(f(x) = 1))
  346.797 μs (543 allocations: 35.33 KiB)
Dict{Symbol,Any} with 5 entries:
  :name        => :f
  :args        => Any[:x]
  :kwargs      => Any[]
  :body        => quote
  :whereparams => ()

julia> @btime Mocking.splitdef(:(f(x) = 1))
  810.213 ns (14 allocations: 1.14 KiB)
Dict{Symbol,Any} with 4 entries:
  :head => :(=)
  :args => Any[:x]
  :body => quote
  :name => :f

I think it would be good to make these functions into a separate package for general usage but for now they can live in Mocking (I'm not sure what a good name for this package would be).

A quick list of issues with the MacroTools variants:

  • MacroTools.splitdef does not handle anonymous functions
  • MacroTools.splitdef does not handle empty generic functions
  • MacroTools.combinedef can only create long-form functions
  • MacroTools.combinedef always includes Expr(:parameters, ...)

@omus omus requested a review from oxinabox August 21, 2019 20:37
then `nothing` will be returned for invalid definitions.
"""
function splitdef(ex::Expr; throw::Bool=true)
def = Dict{Symbol,Any}()
Copy link
Contributor

@iamed2 iamed2 Aug 21, 2019

Choose a reason for hiding this comment

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

Why not a type or a NamedTuple?

EDIT: I see you mutate it

Copy link
Member Author

Choose a reason for hiding this comment

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

I would be good with returning a type. I ended up using a Dict to mostly keep compatibly with MacroTools' version

@codecov
Copy link

codecov bot commented Aug 21, 2019

Codecov Report

Merging #66 into master will increase coverage by 0.61%.
The diff coverage is 96.59%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #66      +/-   ##
==========================================
+ Coverage   94.44%   95.05%   +0.61%     
==========================================
  Files           5        5              
  Lines         108      182      +74     
==========================================
+ Hits          102      173      +71     
- Misses          6        9       +3
Impacted Files Coverage Δ
src/Mocking.jl 93.84% <100%> (-0.19%) ⬇️
src/expr.jl 96.59% <96.05%> (-3.41%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e5e316e...939a357. Read the comment docs.

return esc(result)
end

@testset "splitdef / combinedef" begin
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the objective is to keep the same API as MacroTools then it might be good to make MacroTools a test dependency and also compare all the cases that should be the same and explicitly show which cases MacroTools won't handle.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure that would be worth the effort. The functions here definitely don't have the same API (note :type is never returned by MacroTools.splitdef). I'll update the description to mention what isn't handled by MacroTools

@omus
Copy link
Member Author

omus commented Aug 22, 2019

I'll try to put this code into a a separate package. I think ExprTools.jl is a reasonable name. My reason for not putting back into MacroTools.jl is so that we don't have to add unnecessary dependencies here.

I'll note ExprTools.jl already exists and defines splitdef and combinedef but only works on 0.6

Copy link
Member

@oxinabox oxinabox left a comment

Choose a reason for hiding this comment

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

A few edge cases I think want tests still.

src/Mocking.jl Outdated Show resolved Hide resolved
src/Mocking.jl Outdated Show resolved Hide resolved
src/Mocking.jl Outdated Show resolved Hide resolved
src/expr.jl Outdated Show resolved Hide resolved
src/expr.jl Outdated Show resolved Hide resolved
src/expr.jl Outdated Show resolved Hide resolved
src/expr.jl Outdated Show resolved Hide resolved
src/expr.jl Outdated Show resolved Hide resolved
test/expr.jl Show resolved Hide resolved
@omus omus requested a review from oxinabox August 23, 2019 21:03
src/Mocking.jl Outdated Show resolved Hide resolved
src/Mocking.jl Outdated Show resolved Hide resolved
test/expr.jl Outdated Show resolved Hide resolved
test/expr.jl Show resolved Hide resolved
@omus
Copy link
Member Author

omus commented Aug 26, 2019

I realized I never benchmarked combinedef:

julia> using BenchmarkTools, MacroTools, Mocking

julia> def1 = @btime MacroTools.splitdef(:(f(x) = 1))
  327.960 μs (543 allocations: 35.33 KiB)
Dict{Symbol,Any} with 5 entries:
  :name        => :f
  :args        => Any[:x]
  :kwargs      => Any[]
  :body        => quote
  :whereparams => ()

julia> def2 = @btime Mocking.splitdef(:(f(x) = 1))
  547.733 ns (12 allocations: 1.09 KiB)
Dict{Symbol,Any} with 4 entries:
  :args => Any[:x]
  :body => quote
  :name => :f
  :head => :(=)

julia> @btime MacroTools.combinedef($def1)
  675.561 ns (11 allocations: 704 bytes)
:(function f(x; )
      #= REPL[4]:1 =#
      1
  end)

julia> @btime Mocking.combinedef($def2)
  1.524 μs (7 allocations: 416 bytes)
:(f(x) = begin
          #= REPL[7]:1 =#
          1
      end)

I investigated the slow down and it turned out to be a difference between Expr and :(...). After the performance tweak:

julia> @btime MacroTools.combinedef($def1)
  673.331 ns (11 allocations: 704 bytes)
:(function f(x; )
      #= REPL[3]:1 =#
      1
  end)

julia> @btime Mocking.combinedef($def2)
  442.657 ns (7 allocations: 416 bytes)
:(f(x) = begin
          #= REPL[4]:1 =#
          1
      end)

@omus
Copy link
Member Author

omus commented Aug 27, 2019

Applying fixups before merging.

@omus omus merged commit 023eb2b into master Aug 27, 2019
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.

patching with where Mocking does not support Nothing kwarg defaults
4 participants