Hey everyone,
I’ve been struggling to find a way to implement an array view or wrapper or something like that such that it has access to the Array
methods but it returns either itself or a wrapped array when appropriate. For instance,
class View(T)
def initialize(@buffer : Array(T))
end
...
end
ary = (0..4).to_a
view = View.new ary # => View<#1>{0, 1, 2, 3, 4}
view.size # => 5 , it returns an unwrappable value
view.map(&.to_s) # => ["0", "1", "2", "3", "4"] , it returns an unwrapped array as the value type changed
view.select(&.even?) # => View<#2>{2, 4} , it returns a new wrapped value (copy)
view.reverse! # => View<#1>{4, 3, 2, 1, 0} , it returns itself same as #reverse!
view # => View<#1>{4, 3, 2, 1, 0}
ary # => [4, 3, 2, 1, 0]
I’ve tested using the method_missing
macro but the method information (e.g., return type) is missing to be able to know if it’s mutating the array or whether the result can be wrapped. So I tried different variations of having a hard-coded list of “annotated” methods such that #reverse
is marked as wrappable, #reverse!
as mutating and returning itself, #first
as a non-mutating and non-wrappable method, and #first(n)
as non-mutating and wrappable method. But of course, this does not handle methods added to Array
.
The idea is to add functionality unique to such type, so methods can be chained easily, otherwise view.reverse.custom_method
would fail to compile.
Any help or guidance would be much appreciated.