Write method for object

struct Foo end

open("/tmp/foo", write=true) do io
    write(io, Foo())
end


ERROR: LoadError: MethodError: no method matching write(::IOStream, ::Foo)
Closest candidates are:
  write(::IO, ::Any) at io.jl:611
  write(::IO, ::Any, ::Any...) at io.jl:612
  write(::IOStream, ::UInt8) at iostream.jl:357
  ...
Stacktrace:
 [1] write(::IOStream, ::Foo) at ./io.jl:611
 [2] (::var"#5#6")(::IOStream) at 

Why doesn’t write(::IOStream, ::Foo) match write(::IO, ::Any)?

Seems like a bug, even @which agrees with you. It is possible that the method is being called and then explicitly throwing a MethodError exception.

julia> struct Foo end

julia> open("/tmp/foo", write=true) do io
           @which write(io, Foo())
       end
write(io::IO, x) in Base at io.jl:611

EDIT: yep, this is the code of the function:

write(io::IO, x) = throw(MethodError(write, (io, x)))

I do not find this to be a good pattern, probably ArgumentError should be used instead. I would do a PR if the devs want to change it.

1 Like

  write(io::IO, x)
  write(filename::AbstractString, x)

  Write the canonical binary representation of a value to the given I/O stream or file. Return the number of bytes written into the stream.
  See also print to write a text representation (with an encoding that may depend upon io).

Shouldn’t it just call a function like canonical_binary_representation(x) instead of manually raising an error?

I am not sure if such a thing exists. I do believe Julia objects have some options of persistent storage with different trade-offs and none of them is considered canonical.

I am only taking the concept of “canonical binary representation” from the docstring of write.

I think the docstring means the canonical representation is left to the implementer of the struct.

That’s what I mean too. Instead of implementing write(x), author should implement canonical_binary_representation(x), which write(x) should call. Currently it’s mixing IO with string-building and I don’t see why it should.

Oh, now I see your point.

If you are concerned with performance and generality, you often will avoid having a function that creates a String object which optionally be called in a write function. The best flow is, in fact, the opposite. You will have a write function that serializes the generic object into an IO object, and if you just want to create a String representation then you will call this write function passing an IOBuffer and extract the String from it after the writeing operation.

Yeah, I agree that explicitly throwing a MethodError is unhelpful, precisely because it creates confusing messages just like this. This is the same problem that replace has, and it’s also confusing: Cannot find overloaded method - #7 by GunnarFarneback