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

Add conditional and passmissing #89

Merged
merged 7 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion src/Missings.jl
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
Copy link
Member

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.

Copy link
Member

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...

Copy link
Member Author

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, but Missings.propagate is a bit long.

Copy link
Member

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...

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 know, but that is why I thought the idea was propagatemissing which tab-completes.


using Base: ismissing, missing, Missing, MissingException

Expand Down Expand Up @@ -165,4 +165,57 @@ function levels(x)
levs
end

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...) where {F}
if @generated
for T in xs
T === Missing && return missing
end
return :(f.f(xs...))
else
return any(ismissing, xs) ? missing : f.f(xs...)
end
end

"""
passmissing(f)

Return a function that returns `missing` if any of its positional arguments
are `missing` (even if their number or type is not consistent with any of the
methods defined for `f`) and otherwise applies `f` to these arguments.

`passmissing` does not support passing keyword arguments to the `f` function.

# Examples
```jldoctest
julia> passmissing(sqrt)(4)
2.0

julia> passmissing(sqrt)(missing)
missing

julia> passmissing(sqrt).([missing, 4])
Copy link
Member

Choose a reason for hiding this comment

The 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).

Copy link
Member Author

Choose a reason for hiding this comment

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

added

2-element Array{Union{Missing, Float64},1}:
missing
2.0

julia> passmissing((x,y)->"\$x \$y")(1, 2)
"1 2"

julia> passmissing((x,y)->"\$x \$y")(missing)
missing
"""
passmissing(f::Base.Callable) = PassMissing{typeof(f)}(f)

end # module
8 changes: 8 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,12 @@ using Test, Dates, InteractiveUtils, SparseArrays, Missings

# MissingException
@test sprint(showerror, MissingException("test")) == "MissingException: test"

# Lifting
@test passmissing(sqrt)(4) == 2.0
@test isequal(passmissing(sqrt)(missing), missing)
@test isequal(passmissing(sqrt).([missing, 4]), [missing, 2.0])
@test passmissing((x,y)->"$x $y")(1, 2) == "1 2"
@test isequal(passmissing((x,y)->"$x $y")(missing), missing)
@test_throws ErrorException passmissing(string)(missing, base=2)
end