Skip to content

Commit 77c616b

Browse files
authored
readme improvements (#17)
Fixes #6 Fixes #8 Fixes #14 Fixes #15
1 parent 9a310d2 commit 77c616b

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

README.md

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,33 @@
66
[![PkgEval](https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/Z/ZeroDimensionalArrays.svg)](https://JuliaCI.github.io/NanosoldierReports/pkgeval_badges/Z/ZeroDimensionalArrays.html)
77
[![Aqua](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
88

9-
A small software package for the Julia programming language providing zero-dimensional array types. `Ref`-killer.
9+
A tiny software package for the Julia programming language, providing zero-dimensional array types. `Ref`-killer.
1010

11-
Exports three zero-dimensional subtypes of `AbstractArray`:
11+
Exports these zero-dimensional subtypes of `AbstractArray`, differing on topics such as mutability and identity:
1212
* `ZeroDimArray`
1313
* declared with `struct`, not with `mutable struct`
1414
* does not support `setfield!`, or mutating the element otherwise
1515
* `isbits` when the element is `isbits`
1616
* `Box`
1717
* declared with `mutable struct`
18-
* supports `setfield!`
18+
* supports `setfield!` for mutating the element
19+
* acts as a reference to its element
1920
* `BoxConstField`
2021
* declared with `mutable struct`
2122
* does not support `setfield!`, or mutating the element otherwise
22-
* included for completeness, but not likely to be useful often
23+
* acts as a reference to its element
24+
25+
Any zero-dimensional array is an iterator containing exactly one element (this follows from the zero-dimensional shape). `Ref`, too, is a zero-dimensional iterator, however it's not an array. Even though `Ref` supports being indexed like a zero-dimensional array is commonly indexed, without an index: `x[]`.
2326

2427
The motivation for creating this package is:
2528
* To prevent the frequent confusion regarding `Ref` vs `Base.RefValue` by offering a replacement that makes `Ref` unnecessary in many use cases. Previous discussion:
2629
* https://github.com/JuliaLang/julia/issues/38133
2730
* https://github.com/JuliaLang/julia/issues/55321
2831
* https://discourse.julialang.org/t/ref-is-not-a-concrete-type-poorly-documented/120375
29-
* https://discourse.julialang.org/t/ref-t-vs-base-refvalue-t/127886/
30-
* To provide "mutable wrapper" functionality, something `Ref` is often used for:
31-
* `Box` can be a good replacement. Examples:
32+
* https://discourse.julialang.org/t/understanding-type-ref-t/107711
33+
* https://discourse.julialang.org/t/ref-t-vs-base-refvalue-t/127886
34+
* To provide "mutable wrapper" functionality:
35+
* `Box` can be a good choice. Examples:
3236
* make a `const` binding that's mutable:
3337
```julia
3438
const some_const_binding = Box(0.2)
@@ -44,14 +48,55 @@ The motivation for creating this package is:
4448
* previous discussion:
4549
* https://github.com/JuliaLang/julia/issues/40369
4650
* https://discourse.julialang.org/t/dynamic-immutable-type/127168
47-
* to provide a wrapper type for treating a value as a scalar in broadcasting, something `Ref` is often used for:
48-
* `ZeroDimArray` can be a good replacement:
51+
* To provide a [*boxing*](https://en.wikipedia.org/wiki/Boxing_(computer_programming)) feature, for example for data deduplication to avoid excessive memory use. Either `Box` or `BoxConstField` might be a good choice here, depending on whether mutability is desired. Compare:
52+
* ```julia-repl
53+
julia> large_data = ntuple(identity, 8)
54+
(1, 2, 3, 4, 5, 6, 7, 8)
55+
56+
julia> for _ ∈ 1:4
57+
large_data = (large_data, large_data)
58+
end
59+
60+
julia> Base.summarysize(large_data)
61+
1024
62+
63+
julia> Base.summarysize([large_data for _ ∈ 1:1000]) # duplicates `large_data` a thousand times
64+
1024040
65+
66+
julia> using ZeroDimensionalArrays
67+
68+
julia> large_data_reference = Box(large_data);
69+
70+
julia> Base.summarysize([large_data_reference for _ ∈ 1:1000]) # `large_data` isn't stored inline
71+
9064
72+
```
73+
* To provide a wrapper type for treating a value as a scalar in broadcasting:
74+
* `ZeroDimArray` can be a good choice:
4975
```julia-repl
5076
julia> using ZeroDimensionalArrays
5177
52-
julia> isa.(ZeroDimArray([1,2,3]), [Array, Dict, Int])
78+
julia> isa.([1,2,3], [Array, Dict, Int])
79+
3-element BitVector:
80+
0
81+
0
82+
1
83+
84+
julia> isa.(ZeroDimArray([1,2,3]), [Array, Dict, Int]) # now escape the vector from broadcasting using `ZeroDimArray`
5385
3-element BitVector:
5486
1
5587
0
5688
0
5789
```
90+
The other types, `Box` or `BoxConstField` would work for this use case, too, as would any zero-dimensional array, but `ZeroDimArray` is more likely to have zero cost for performance.
91+
* previous discussion regarding `Ref`:
92+
* https://discourse.julialang.org/t/ref-vs-zero-dimensional-arrays/24434
93+
94+
## Comparison with other potential solutions
95+
96+
* Zero-dimensional `Array`:
97+
* `fill(x)`, creating a zero-dimensional `Array` containing `x` as its element, is often used instead of `Ref(x)`.
98+
* `Array{T, 0} where {T}` is very similar to `Box`, albeit less efficient. The inefficiency is due to the fact that the implementation of `Array` supports resizeability (even though that's currently only available to users in the one-dimensional case of `Vector`), implying extra indirection, leading to extra pointer dereferences and extra allocation.
99+
* [FixedSizeArrays.jl](https://github.com/JuliaArrays/FixedSizeArrays.jl):
100+
* Less heavy than `Array`, but still may be less efficient than `Box`.
101+
* [FillArrays.jl](https://github.com/JuliaArrays/FillArrays.jl):
102+
* Zero-dimensional `Fill`, constructible with `Fill(x)`, is equivalent to `ZeroDimArray`.

0 commit comments

Comments
 (0)