Inconsistency of type argument positions

I happened to met the depwarn

readstring(x) is deprecated to read(x, String)

And noticed some inconsistency. Note the following functions:

julia> parse(Int, "1234")

julia> floor(Int, 1.2)

So, is read(String, io) make any sense?

edit: here’s what I found from the julia repo itself,


  • zeros(Int, N)
  • getindex(Int8, 1, 2, 3)
  • ceil(Int,N/2)
  • reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))
  • map(Int,dims)
  • bitcast(Int, x)
  • convert(Int, sz)
  • tryparse(Int, env_str)
  • trunc(Int,x)
  • round(Int, 1.7)
  • floor(Int, log10(abs(x)))
  • parse(Int, "1234")
  • rationalize(Int, x; kvs...)
  • unsafe_trunc(Int,n)
  • tryparse(Int, env_cores)
  • tryparse_internal(Int, sbuff, startpos, endpos, 0, false)
  • ones(Int, 4, 4)
  • typeparam(Int, rand(Int, 2))
  • transcode(String, val)
  • LibGit2.get(String, cfg, "branch.$branchname.remote")
  • collect(Float64, 1:2:5)
  • ounding.setrounding_raw(Float64, Rounding.JL_FE_TONEAREST)
  • splitprec(Float64, i)
  • Base.unsafe_convert(Int32, Base.cconvert(Int32, x))
  • SparseArray(Float64, 3, 3)
  • Base.sitofp(Float64, %9)
  • rand(Float64,10,10)
  • hilb(Float64,n)
  • onediag(Float64, m, n)
  • tridiag(Float64, m, n)
  • sprand(Float64, 3, 0.75)
  • spzeros(Float64, 3, 3)
  • Base.typed_hcat(Float64, B, B, B)

List 2:

  • empty([1.0, 2.0, 3.0], String)
  • read(`$shell -c "printf %s $(shell_escape_posixly(dir))"`, String)
  • isa(s[2], String)
  • similar(s, Int)
  • rand(m, Int)
  • π (%1, Float64)

There’s been some discussion about this. The problem is that we have conflicting rules in this case, since we also usually put I/O streams as first arguments so that the do syntax works (see e.g. open) (EDIT: this is wrong, see below). IIRC it’s been decided that I/O streams take priority over return type parameters for that reason.

Okay, thanks for the info~

How about support these two forms simultaneously?

I thought it was functions that took precedence as the first argument because of the do syntax (open returns an I/O stream, but the body of the do becomes an anonymous function with is the first argument to open), not I/O streams.

I’ve done that, in the Strs.jl package, where I wanted to support the do syntax, but the first argument was otherwise a Type, allow the function first or the type.


Woops, yes, actually I confused read with open. We don’t actually care about passing the I/O stream as the first argument, that’s only useful for functions. So whether to pass the I/O stream or the type as first argument is mostly arbitrary.

Does it make sense to open an issue/pr on this? As in the first post, almost no function put type argument on non-first position

In this case, I think the more important factor is that the first argument (I/O stream or filename) is always needed, but the type to read it into is optional.
I wouldn’t like to have to put String in 99% of the time, just to be consistent with the “rule” that the type should go first.

It is optional no matter at first or second position. Just like floor

But no function puts the I/O stream in non-first position either. You can’t satisfy both conventions at the same time.

1 Like

fine :man_shrugging:

The order is given in official conventional argument precedence · Issue #19150 · JuliaLang/julia · GitHub

(idk if this is in the docs somewhere as well, it should be, if not that is an issue to open.)

Important part is: Jeff’s comment:

I propose the following priority order (highest priority listed first):

  1. Function argument (for HOFs accepting one function, or for use with do blocks)
  2. IO stream
  3. Thing being mutated (Edit: to hold the main result of the computation)
  4. Type
  5. Collection (not being mutated)
  6. Key
  7. Value
  8. Everything else

Exception: first argument to convert is always the type.


I wonder why Jeff considered convert an exception?
Type is #4, and Value is #7, so that follows Jeff’s list.
I’m not aware of any convert methods that allow 1,2, or 3.

For convert, the ordering also makes a lot of sense given the similarity to constructors and the order that assignment works: dest = source, so DestType(source) or convert(DestType, source) all scan well.