Name this method extension technique, piracy evasion

mkitti
Sun, 02 Jul 2023 23:52:06 +0000
General Usage

Let’s say I want to change how Base.write works with a Base type, NTuple{N,UInt8} from a package.

Currently, writing NTuple via Base.write errors.

julia> iob = IOBuffer()
IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> data = (0x01,0x02,0x03)
(0x01, 0x02, 0x03)

julia> Base.write(iob, data)
ERROR: MethodError: no method matching write(::IOBuffer, ::Tuple{UInt8, UInt8, UInt8})

julia> typeof(data)
Tuple{UInt8, UInt8, UInt8}

julia> typeof(data) == NTuple{3,UInt8}
true

Under type piracy rules, I should not do the following because

1. I did not originate the types.

2. I did not orginate the method.

function Base.write(io::IO, data::NTuple{N,T}) where {N,T}
    sum(map(1:N) do i
        Base.write(io, data[i])
    end)
end

julia> write(iob, data)
3

To evade commiting piracy, I will originate a new method called Foo.write.

module Foo
    # Edit: comment out the export here. People should do this more explicitly.
    # export write
    # Fallback to Base implementation
    write(args...; kwargs...) =
        Base.write(args...; kwargs...)

    function write(io::IO, data::NTuple{N,T}) where {N,T}
        sum(map(1:N) do i
            Base.write(io, data[i])
        end)
    end
end

This is not piracy because I did not violate both criteria I outlined above. I can use this as follows in a fresh REPL session.

# Explicitly indicate the use Foo.write instead of Base.write
julia> using .Foo: write

julia> @which write
Main.Foo

julia> iob = IOBuffer()
IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)

julia> data = (0x01,0x02,0x03)
(0x01, 0x02, 0x03)

julia> write(iob, data)
3

julia> write(iob, 4)
8

julia> write(iob, 0x5)
1

julia> take!(iob)
12-element Vector{UInt8}:
 0x01
 0x02
 0x03
 0x04
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x05

One could also use a baremodule to not have Base imported.

Another important effect is that I do not invalidate any uses of Base.write and thus do not force recompilation for any other packages.

Using the language of object oriented programming, Foo.write is a submethod that inherits from Base.write. Foo.write does everything Base.write does but it also writes NTuple. Is there a preexisting name for this?

Can you think of a better name?

Edit: Commented out the export write from the example.

22 posts - 6 participants