Currently the methods String#bytes and Char#bytes return an Array(UInt8). However the alias Bytes is a Slice(Uint8). This creates issues semantically in the following situation:
def byte_frequency(data : Bytes) : Hash(UInt8, Int32)
data.reduce({} of UInt8 => Int32) do |acc, byte|
acc.has_key?(byte) ? (acc[byte] += 1) : (acc[byte] = 1)
acc
end
end
puts byte_frequency("Hello world".bytes)
One would expect this to work because semantics say that "Hello world".bytes will return something that can be passed into a method that accepts data : Bytes, but this will fail because an Array(UInt8) is not the same as Bytes.
This post can be reduced to:
I expect the word "bytes" to always mean the exact same thing
and that doesn't convince me.
Your type annotation there is wrong indeed, and needlessly narrows the usefulness. Should be Iterable(UInt8).
To further dismantle this logic,
One would expect this to work because semantics say that
"Hello world".hashwill return something that can be passed into a method that acceptsdata : Hash
(and no, semantics doesn't say either of those things)
As a side point, I do think that Bytes , and Hash more so, are badly named.
I just don't think it makes sense for there to be a set type of Bytes that's a Slice(UInt8) and then have a String#bytes that returns an Array(UInt8). Yes I know my example isn't perfect, but that doesn't make my point any less valid.
The point has no validity in the first place because it pivots only on the name to establish some kind of inconsistency. Then you talk about only one of many equally (if not more) valid possibilities to resolve the alleged inconsistency.
I think Array(UInt8) is a more appropriate return type here than Slice(UInt8).
If I were to consider this an inconsistency, my first choice would be to remove the Bytes alias. Slice really should be reserved to I/O and bindings, as it defines many unsafe methods and fewer generally useful ones. But this alias somehow makes it seem like a generally acceptable type to work with.
And you just said,
I just don't think it makes sense for there to be a set type of Bytes that's a Slice(UInt8) and then have a String#bytes that returns an Array(UInt8).
-- sure, I probably even agree with that, but I take issue with the jump to a conclusion.
This has been discussed in the past as well: #3551.
Duplicate of #3551