diff --git a/docs/src/index.md b/docs/src/index.md index 07f6c64..85248b4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -32,6 +32,7 @@ Legolas.read ```@docs Legolas.lift +Legolas.construct Legolas.assign_to_table_metadata! Legolas.gather Legolas.locations diff --git a/src/Legolas.jl b/src/Legolas.jl index 15b0125..76381ec 100644 --- a/src/Legolas.jl +++ b/src/Legolas.jl @@ -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") diff --git a/src/lift.jl b/src/lift.jl new file mode 100644 index 0000000..c5b7676 --- /dev/null +++ b/src/lift.jl @@ -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) diff --git a/test/runtests.jl b/test/runtests.jl index 75ca032..7d8ae1c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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