Skip to content

Commit

Permalink
Add castout parameter to MultiLens
Browse files Browse the repository at this point in the history
It looks like a good way to go as I can reduce `get` implementation to
one.
  • Loading branch information
tkf committed May 26, 2019
1 parent 21eb4d6 commit d65304c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 14 deletions.
28 changes: 28 additions & 0 deletions src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ _default_show(io, obj) = print_apply(io, typeof(obj), _getfields(obj))

Base.show(io::IO, lens::KaleidoLens) = _default_show(io, lens)

"""
prefer_singleton_callable(f)
Convert `f` to an callable singleton object if possible. Useful if `f`
is a `Type`.
# Examples
```jldoctest
julia> using Kaleido: prefer_singleton_callable
julia> sizeof((Int,))
8
julia> sizeof((prefer_singleton_callable(Int),))
0
julia> prefer_singleton_callable(Int)(1.0)
1
```
"""
prefer_singleton_callable(::Type{T}) where T = SingletonCallable{T}()
prefer_singleton_callable(f) = f

struct SingletonCallable{T} end
(::SingletonCallable{T})(x) where T = T(x)

## Specialized foldl

_tail(t) = Base.tail(t)
_tail(t::NamedTuple{names}) where names = NamedTuple{Base.tail(names)}(t)

Expand Down
25 changes: 17 additions & 8 deletions src/multilens.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
MultiLens(lenses::Tuple)
MultiLens(lenses::NamedTuple)
MultiLens([castout,] lenses::Tuple)
MultiLens([castout,] lenses::NamedTuple)
# Examples
```jldoctest
Expand Down Expand Up @@ -31,17 +31,26 @@ MultiLens

const Lenses{N} = NTuple{N, Lens}
const NamedLenses{N, names} = NamedTuple{names, <:Lenses{N}}
const AnyLenses{N} = Union{Lenses{N}, NamedLenses{N}}

struct MultiLens{N, L <: Union{Lenses{N}, NamedLenses{N}}} <: KaleidoLens
lenses::L
struct MultiLens{N, TL <: AnyLenses{N}, TO} <: KaleidoLens
castout::TO
lenses::TL

global _MultiLens(castout::TO, lenses::TL) where {N, TL <: AnyLenses{N}, TO} =
new{N, TL, TO}(castout, lenses)
end

MultiLens(castout, lenses) =
_MultiLens(prefer_singleton_callable(castout), lenses)

MultiLens(lenses::Lenses) = MultiLens(identity, lenses)
MultiLens(lenses::NamedTuple{names, <:Lenses}) where names =
MultiLens(NamedTuple{names}, lenses)

_getall(obj, lenses) = map(l -> get(obj, l), lenses)

Setfield.get(obj, ml::MultiLens{N, <:Lenses{N}}) where {N} =
_getall(obj, ml.lenses)
Setfield.get(obj, ml::MultiLens{N, <:NamedLenses{N, names}}) where {N, names} =
NamedTuple{names}(_getall(obj, ml.lenses))
Setfield.get(obj, ml::MultiLens) = ml.castout(_getall(obj, ml.lenses))

Setfield.set(obj, ml::MultiLens, val) = _set(obj, ml, val)

Expand Down
7 changes: 7 additions & 0 deletions test/test_base.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module TestBase

include("preamble.jl")
using Kaleido: prefer_singleton_callable

expressions_block = quote
MultiLens(((@lens _.x), (@lens _[1])))
Expand Down Expand Up @@ -39,4 +40,10 @@ end
end
end

@testset "prefer_singleton_callable" begin
@test sizeof((Int,)) > 0
@test sizeof((prefer_singleton_callable(Int),)) == 0
@test sizeof((prefer_singleton_callable(identity),)) == 0
end

end # module
32 changes: 26 additions & 6 deletions test/test_multilens.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ using InteractiveUtils
(y=(z="y.z",), x="x", a=0)
end

function test_namedtuple_set(ml)
@testset for val in [(a="x", b="y.z")
(b="y.z", a="x")]
@test set((x=1, y=(z=2,)), ml, val) === (x="x", y=(z="y.z",))
@test set((y=(z=2,), x=1, a=0), ml, val) === (y=(z="y.z",), x="x", a=0)
end
@test set((x=1, y=(z=2,)), ml, (a=:x, b="y.z")) === (x=:x, y=(z="y.z",))
end

@testset "NamedTuple" begin
ml = MultiLens((
a = (@lens _.x),
Expand All @@ -27,12 +36,23 @@ end
@test get((x=1, y=(z=2,)), ml) === (a=1, b=2)
@test get((y=(z=2,), x=1, a=0), ml) === (a=1, b=2)

@testset for val in [(a="x", b="y.z")
(b="y.z", a="x")]
@test set((x=1, y=(z=2,)), ml, val) === (x="x", y=(z="y.z",))
@test set((y=(z=2,), x=1, a=0), ml, val) === (y=(z="y.z",), x="x", a=0)
end
@test set((x=1, y=(z=2,)), ml, (a=:x, b="y.z")) === (x=:x, y=(z="y.z",))
test_namedtuple_set(ml)
end

@testset "castout" begin
# Input is a `NamedTuple`; Output is a `Tuple`
ml = MultiLens(
Tuple,
(
a = (@lens _.x),
b = (@lens _.y.z),
)
)

@test get((x=1, y=(z=2,)), ml) === (1, 2)
@test get((y=(z=2,), x=1, a=0), ml) === (1, 2)

test_namedtuple_set(ml)
end

function codegen_multilens_tuple()
Expand Down

0 comments on commit d65304c

Please sign in to comment.