Extending Base.read, issue with keyword arguments

In the process of implementing a library (ITensors.jl), we were planning to extend the Base.read methods to support the custom types introduced by our library. So these extensions, in their most basic form, would work by the user calling read(io,x) where x could be one of our library-specific types.

However, we are running into a problem when trying to do this in a generic way.

The problem is that it’s important for us to pass optional keyword arguments saying how the data is to be read, because e.g. we have older versions of the library written in a different language and sometimes want to read in those files. So we are currently allowing a keyword argument that indicates the input should be interpreted according to the layout of those files.

The problem comes when we call Base.read inside our own custom Base.read extensions, because Base.read implementations in the Julia standard library do not accept any keyword arguments. So if we want to make a routine that reads in a Vector{T} where T could be either one of our custom library objects or built-in types like Int, we have to choose whether to pass keyword arguments to read(io,t::T) in which case it will work for our objects but not Julia built-ins, or vice versa.

What’s the solution here? Should we just not extend Base.read and come up with a different function name for our purposes? Or should we not have our extensions rely on optional keyword arguments? But if we do that, how can we tailor the behavior depending on what kind of file the io argument is a handle to?

Thanks,
Miles

P.S. As an example, here is one of our Base.read extensions currently:
https://github.com/ITensor/ITensors.jl/blob/master/src/itensor.jl#L688

1 Like

That problem statement is approximately what IOContext was created for. Doest that help?

2 Likes

Thanks Jameson. That’s helpful about IOContext because we could use it and also your comment gives me a better idea of the design the Julia language team has in mind for extending functions.**

Looking closer at HDF5.jl, I see now that it takes a somewhat similar approach, but introducing an HDF5File type and overloads of read which accept that type as the first argument. So we’ll probably follow that route, making overloads of read which take HDF5File for reading HDF5 etc.

Best regards,
Miles

** For myself I wish that functions not taking keyword args would silently accept and just ignore keyword args if they are passed, but I understand also why that’s not allowed.

I would recommend doing this instead; maybe with a generic fallback to Base.read when that works:

f(io::IO, ::Type{T}; options...) where {T <: Union{Int16,...}} = read(io, T)
1 Like