-
Notifications
You must be signed in to change notification settings - Fork 19
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
Add conditional and passmissing #89
Changes from 4 commits
1516580
3bf10c0
f947bcf
e775f2b
4cc1a5e
7f491bf
8c0bd5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
module Missings | ||
|
||
export allowmissing, disallowmissing, ismissing, missing, missings, | ||
Missing, MissingException, levels, coalesce | ||
Missing, MissingException, levels, coalesce, passmissing | ||
|
||
using Base: ismissing, missing, Missing, MissingException | ||
|
||
|
@@ -165,4 +165,30 @@ function levels(x) | |
levs | ||
end | ||
|
||
struct PassMissing{F} <: Function end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we probably want: struct PassMissing{F} <: Function
f::F
end |
||
|
||
@generated (::PassMissing{F})(xs...;kw...) where {F} = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the |
||
:(any(ismissing, xs) || any(ismissing, values(values(kw))) ? missing : F(xs...; kw...)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, the inclusion of keyword arguments here seems off to me; what if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think my stab at this definition would be more along the lines of: struct PassMissing{F} <: Function
f::F
end
function (f::PassMissing{F})(x) where {F}
if @generated
return x === Missing ? missing : :(f.f(x))
else
return x === missing ? missing : f.f(x)
end
end
function (f::PassMissing{F})(xs...; kw...) where {F}
if @generated
for T in xs
T === Missing && return missing
end
return :(f.f(xs...; kw...))
else
return any(ismissing, xs) ? missing : f.f(xs...; kw...)
end
end with specialize methods for 1 argument, 2 argument, maybe 3. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. Though with generated functions you don't need to specialize on the number of arguments. Also why do you think keyword arguments should be excluded from the missingness check? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the comments. I have done additional benchmarking and it looks you are right with the optimizations 👍 (I did some tests earlier that did allocate). I do not understand how and why this
Could you please explain? Regarding other issues:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The For keyword arguments, it just feels more arbitrary. Like, it's hard to imagine a case where I'd be If anything, I'd say we leave keyword arguments out of the mix for now since they can always be added later. My main issue with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think there are any examples of functions using I see your point about keyword arguments generally being about options, while the data is passed as positional arguments, but I'd find it problematic to make this a rule. Overall it doesn't seem we have strong use cases for either behavior, and in such situations I tend to favor the simplest rule (i.e. "return
The problem is, changing that would be breaking. The only way to be able to choose any behavior later is to throw an error if a keyword argument is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We could define lift for functions that do not take keyword arguments for the time being. It is easy enough to wrap a function requiring kwargs in an anonymous function. |
||
|
||
""" | ||
passmissing(f) | ||
|
||
Return a function that returns `missing` if any of its positional or keyword arguments | ||
are `missing` and otherwise applies `f` to those arguments. | ||
|
||
# Examples | ||
```jldoctest | ||
julia> passmissing(sqrt)(4) | ||
2.0 | ||
|
||
julia> passmissing(sqrt)(missing) | ||
missing | ||
|
||
julia> passmissing(sqrt).([missing, 4]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful to start with a simpler example with a single scalar (first non-missing, and then missing). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added |
||
2-element Array{Union{Missing, Float64},1}: | ||
missing | ||
2.0 | ||
""" | ||
passmissing(f::Base.Callable) = PassMissing{f}() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be BTW, have you done some benchmarking to check there are no allocations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK if I wanted to use I will post benchmarks for the current implementation in the main thread so that they do not disappear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in most practical uses, the struct would never allocate. It's the same idea w/ the new iteration protocol: every call to |
||
|
||
end # module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer
Missings.propagate
if we're still bikeshedding the name here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like
propagate
too, the only gripe is that it's quite long. But well...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But do we want to export it or not? If yes then
propagate
is ok, butMissings.propagate
is a bit long.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately
propagate
is quite general to claim it for this feature...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, but that is why I thought the idea was
propagatemissing
which tab-completes.