Skip to content

Commit

Permalink
Add construct function to assist lift (#45)
Browse files Browse the repository at this point in the history
* Create `construct` function

Method ensure compatibility with types which do not define constructing
instances of themselves.

* Move lift functions to separate file

* Attach docstring to more general `lift` method

* Ensure curried-function only accepts types

Co-authored-by: Jarrett Revels <[email protected]>
Co-authored-by: Alex Arslan <[email protected]>
  • Loading branch information
3 people authored May 20, 2022
1 parent 1be43f7 commit f3bee00
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 17 deletions.
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Legolas.read

```@docs
Legolas.lift
Legolas.construct
Legolas.assign_to_table_metadata!
Legolas.gather
Legolas.locations
Expand Down
18 changes: 1 addition & 17 deletions src/Legolas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,11 @@ module Legolas

using Tables, Arrow

"""
lift(f, x)

Return `f(x)` unless `x isa Union{Nothing,Missing}`, in which case return `missing`.
This is particularly useful when handling values from `Arrow.Table`, whose null values
may present as either `missing` or `nothing` depending on how the table itself was
originally constructed.
"""
lift(::Any, ::Union{Nothing,Missing}) = missing
lift(f, x) = f(x)

"""
lift(f)
Returns a curried function, `x -> lift(f,x)`
"""
lift(f) = Base.Fix1(lift, f)

const LEGOLAS_SCHEMA_QUALIFIED_METADATA_KEY = "legolas_schema_qualified"

include("lift.jl")
include("rows.jl")
include("tables.jl")

Expand Down
59 changes: 59 additions & 0 deletions src/lift.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
lift(f, x)
Return `f(x)` unless `x isa Union{Nothing,Missing}`, in which case return `missing`.
This is particularly useful when handling values from `Arrow.Table`, whose null values
may present as either `missing` or `nothing` depending on how the table itself was
originally constructed.
See also: [`construct`](@ref)
"""
lift(f, x) = f(x)
lift(::Any, ::Union{Nothing,Missing}) = missing

"""
lift(f)
Returns a curried function, `x -> lift(f,x)`
"""
lift(f) = Base.Fix1(lift, f)


"""
construct(T::Type, x)
Construct `T(x)` unless `x` is of type `T`, in which case return `x` itself. Useful in
conjunction with the [`lift`](@ref) function for types which don't have a constructor which
accepts instances of itself (e.g. `T(::T)`).
## Examples
```jldoctest
julia> using Legolas: construct
julia> construct(Float64, 1)
1.0
julia> Some(Some(1))
Some(Some(1))
julia> construct(Some, Some(1))
Some(1)
```
Use the curried form when using `lift`:
```jldoctest
julia> using Legolas: lift, construct
julia> lift(Some, Some(1))
Some(Some(1))
julia> lift(construct(Some), Some(1))
Some(1)
```
"""
construct(T::Type, x) = T(x)
construct(::Type{T}, x::T) where {T} = x
construct(T::Type) = Base.Fix1(construct, T)
27 changes: 27 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,36 @@ include(joinpath(dirname(@__DIR__), "examples", "tour.jl"))
@test ismissing(Legolas.lift(sin, nothing))
@test ismissing(Legolas.lift(sin, missing))
@test Legolas.lift(sin, 1.0) == sin(1.0)
@test Legolas.lift(Some, Some(1)) == Some(Some(1))

@test ismissing(Legolas.lift(sin)(nothing))
@test ismissing(Legolas.lift(sin)(missing))
@test Legolas.lift(sin)(1.0) == sin(1.0)
@test Legolas.lift(Some)(Some(1)) == Some(Some(1))
end

@testset "Legolas.construct" begin
@test Legolas.construct(Int, 0x00) === 0
@test Legolas.construct(Some, Some(1)) === Some(1)

@test Legolas.construct(Int)(0x00) === 0
@test Legolas.construct(Some)(Some(1)) === Some(1)

# Restrict `construct` to types only
@test_throws MethodError Legolas.construct(sin, 1.0)
@test_throws MethodError Legolas.construct(sin)

@testset "undefined identity constructor" begin
mutable struct PR45
x::Int
end
Foo = PR45 # Alias to unique struct name

x = Foo(1)
@test x !== Foo(1)
@test_throws MethodError Foo(x) # Type does not define an identity constructor
@test Legolas.construct(Foo, x) === x
end
end

@testset "Legolas.location" begin
Expand Down

2 comments on commit f3bee00

@omus
Copy link
Member Author

@omus omus commented on f3bee00 May 20, 2022

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/60666

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.4 -m "<description of version>" f3bee0028709310efc6f00af473be8e1ad351d07
git push origin v0.3.4

Please sign in to comment.