diff --git a/ext/LinearAlgebraExt.jl b/ext/LinearAlgebraExt.jl index dac1f58..ba225fc 100644 --- a/ext/LinearAlgebraExt.jl +++ b/ext/LinearAlgebraExt.jl @@ -1,6 +1,6 @@ module LinearAlgebraExt -import Accessors: set, @set +import Accessors: set, @set, _shortstring using LinearAlgebra: norm, normalize, diag, diagind set(arr, ::typeof(normalize), val) = norm(arr) * val @@ -11,4 +11,6 @@ end set(A, ::typeof(diag), val) = @set A[diagind(A)] = val +_shortstring(prev, o::typeof(norm); is_compact) = is_compact ? "‖$(prev)‖" : "$o($prev)" + end diff --git a/ext/UnitfulExt.jl b/ext/UnitfulExt.jl index e4ef94f..816fcd7 100644 --- a/ext/UnitfulExt.jl +++ b/ext/UnitfulExt.jl @@ -1,10 +1,12 @@ module UnitfulExt -import Accessors: set +import Accessors: set, _shortstring using Unitful # ustrip(unit, _) works automatically because Unitful defines inverse() for it # inverse(ustrip) is impossible, so special set() handling is required set(obj, ::typeof(ustrip), val) = val * unit(obj) +_shortstring(prev, o::Base.Fix1{typeof(ustrip)}; is_compact) = is_compact ? "$prev [$(o.x)]" : @invoke _shortstring(prev, o::Base.Fix1; is_compact) + end diff --git a/src/sugar.jl b/src/sugar.jl index 041a38a..84d1d4d 100644 --- a/src/sugar.jl +++ b/src/sugar.jl @@ -495,13 +495,25 @@ IndexLens(::Tuple{Elements}) = Elements() IndexLens(::Tuple{Properties}) = Properties() ### nice show() for optics -_shortstring(prev, o::PropertyLens{field}) where {field} = "$prev.$field" -_shortstring(prev, o::IndexLens) ="$prev[$(join(repr.(o.indices), ", "))]" -_shortstring(prev, o::Union{Function,Type}) = _isoperator(o) ? "$(_fT_repr(o))$prev" : "$(_fT_repr(o))($prev)" -_shortstring(prev, o::Base.Fix1) = _isoperator(o.f) ? "$(o.x) $(_fT_repr(o.f)) $prev" : "$(_fT_repr(o.f))($(o.x), $prev)" -_shortstring(prev, o::Base.Fix2) = _isoperator(o.f) ? "$prev $(_fT_repr(o.f)) $(o.x)" : "$(_fT_repr(o.f))($prev, $(o.x))" -_shortstring(prev, o::Elements) = "$prev[∗]" -_shortstring(prev, o::Properties) = "$prev[∗ₚ]" +_shortstring(prev, o::PropertyLens{field}; is_compact) where {field} = + is_compact && prev == "_" ? "$field" : + "$prev.$field" +_shortstring(prev, o::IndexLens; is_compact) = "$prev[$(join(repr.(o.indices), ", "))]" +_shortstring(prev, o::Union{Function,Type}; is_compact) = + _isoperator(o) ? "$(_fT_repr(o))$prev" : + is_compact && prev == "_" ? _fT_repr(o) : "$(_fT_repr(o))($prev)" +_shortstring(prev, o::Base.Fix1; is_compact) = _isoperator(o.f) ? "$(o.x) $(_fT_repr(o.f)) $prev" : "$(_fT_repr(o.f))($(o.x), $prev)" +_shortstring(prev, o::Base.Fix2; is_compact) = _isoperator(o.f) ? "$prev $(_fT_repr(o.f)) $(o.x)" : "$(_fT_repr(o.f))($prev, $(o.x))" +_shortstring(prev, o::Elements; is_compact) = "$prev[∗]" +_shortstring(prev, o::Properties; is_compact) = "$prev[∗ₚ]" + +_shortstring(prev, o::typeof(real); is_compact) = is_compact ? "Re($prev)" : "$o($prev)" +_shortstring(prev, o::typeof(imag); is_compact) = is_compact ? "Im($prev)" : "$o($prev)" +_shortstring(prev, o::typeof(abs); is_compact) = is_compact ? "|$prev|" : "$o($prev)" +_shortstring(prev, o::typeof(sqrt); is_compact) = "√$prev" +_shortstring(prev, o::typeof(log10); is_compact) = is_compact ? "log₁₀($prev)" : "$o($prev)" +_shortstring(prev, o::typeof(log); is_compact) = is_compact ? "ln($prev)" : "$o($prev)" +_shortstring(prev, o::Base.Splat; is_compact) = is_compact ? "$(o.f)($prev...)" : "$o($prev)" # compact representation of functions and types # most notably, it deals with the module name in a consistent way: doesn't show it @@ -511,6 +523,7 @@ _fT_repr(o) = repr(o; context=:compact => true) # can f be stringfied using the operator (infix) syntax? # otherwise uses regular function call syntax _isoperator(f::Function) = Base.isoperator(nameof(f)) +_isoperator(::typeof(sqrt)) = true _isoperator(f) = false # does o need parens when nested in another such o? @@ -530,14 +543,15 @@ function show_optic(io, optic) print(io, " ∘ ") end if !isempty(inner) + is_compact = get(io, :compact, false) shortstr = reduce(inner; init=("_", false)) do (prev, need_parens_prev), o # if _need_parens is true for this o and the one before, wrap the previous one in parentheses if need_parens_prev && _need_parens(o) prev = "($prev)" end - _shortstring(prev, o), _need_parens(o) + _shortstring(prev, o; is_compact), _need_parens(o) end |> first - if get(io, :compact, false) + if is_compact print(io, shortstr) else print(io, "(@o ", shortstr, ")") diff --git a/test/test_core.jl b/test/test_core.jl index 07341d7..fefb54a 100644 --- a/test/test_core.jl +++ b/test/test_core.jl @@ -5,6 +5,8 @@ using Accessors: test_getset_laws, test_modify_law using Accessors: compose, get_update_op using ConstructionBase: ConstructionBase using StaticNumbers: StaticNumbers, static +using LinearAlgebra +using Unitful struct T a @@ -461,14 +463,29 @@ end @test sprint(show, (@optic log(_.a[2]))) == "(@o log(_.a[2]))" @test sprint(show, (@optic Tuple(_.a[2]))) == "(@o Tuple(_.a[2]))" @test sprint(show, (@optic log(_).a[2])) == "(@o _.a[2]) ∘ log" # could be shorter, but difficult to dispatch correctly without piracy - @test sprint(show, (@optic log(_.a[2])); context=:compact => true) == "log(_.a[2])" - @test sprint(show, (@optic Base.tail(_.a[2])); context=:compact => true) == "tail(_.a[2])" # non-exported function - @test sprint(show, (@optic Base.Fix2(_.a[2])); context=:compact => true) == "Fix2(_.a[2])" # non-exported type + @test sprint(show, (@optic √log(_.a[2])); context=:compact => true) == "√ln(a[2])" + @test sprint(show, (@optic √(log(_.a[2]) + 1)); context=:compact => true) == "√(ln(a[2]) + 1)" + @test sprint(show, (@optic Base.tail(_.a[2])); context=:compact => true) == "tail(a[2])" # non-exported function + @test sprint(show, (@optic Base.Fix2(_.a[2])); context=:compact => true) == "Fix2(a[2])" # non-exported type # show_optic is reasonable even for types without special show_optic handling: o = Recursive(x->true, Properties()) @test sprint(Accessors.show_optic, o) == "$o" @test sprint(Accessors.show_optic, (@o _.a) ∘ o) == "(@o _.a) ∘ $o" + + # compact handling of specific functions + @test sprint(show, (@o splat(max)(_.a))) == "(@o splat(max)(_.a))" + @test sprint(show, (@o splat(max)(_.a)); context=:compact => true) == "max(a...)" + @test sprint(show, (@o abs(_.a))) == "(@o abs(_.a))" + @test sprint(show, (@o abs(_.a)); context=:compact => true) == "|a|" + @test sprint(show, (@o real(_.a))) == "(@o real(_.a))" + @test sprint(show, (@o real(_.a)); context=:compact => true) == "Re(a)" + @test sprint(show, (@o imag(_.a))) == "(@o imag(_.a))" + @test sprint(show, (@o imag(_.a)); context=:compact => true) == "Im(a)" + @test sprint(show, (@o norm(_.a))) == "(@o norm(_.a))" + @test sprint(show, (@o norm(_.a)); context=:compact => true) == "‖a‖" + @test sprint(show, (@o log10(ustrip(u"km", _.a)))) == "(@o log10(ustrip(km, _.a)))" + @test sprint(show, (@o log10(ustrip(u"km", _.a))); context=:compact => true) == "log₁₀(a [km])" end @testset "text/plain show" begin