Tuple vs tuple

What is the rationale for Tuple() not working as a constructor as a struct type would?

It’s fine to use tuple() instead for general purposes, but it messes with the pattern of using types as general constructors when working in the AST. You have to specifically handle Tuple to use tuple() instead of just constructing X(args…).

Obviously not going to be changing right now, but it would be good to understand the rationale for it.

1 Like

I regularly have similar issues with String vs string. I’m sure there’s a reason, but I can never remember when I should use one vs the other.

2 Likes
julia> Tuple([1, 2, 3])
(1, 2, 3)

julia> tuple([1, 2, 3])
([1, 2, 3],)

But why is it so :wink:

It could easily be the other way around, and this would be more intuitive:

struct Example
    a
    b
    c
end

julia> typeof(Example(1, 2, 3)).name.wrapper(1.0, 2.0, 3.0)                                            
Example(1.0, 2.0, 3.0)                                                                                  

julia> typeof((1, 2, 3)).name.wrapper(1.0, 2.0, 3.0)                                                    
ERROR: MethodError: no method matching Tuple(::Float64, ::Float64, ::Float64)  

There might be counterexample of the opposite kind of unintuitive if it were switched, so maybe it was a coin flip

string does not guarantee you to return String. In general you can get other subtype of AbstractString. As an input you can pass any objects.

String guarantees that you will get String but has a limited number of methods - it will only accept String, SubString{String}, Symbol or several variants of UInt8 or AbstractChar arrays.

Oh, interesting. I think I’ve only ever used string() to get string representations of numbers (Ints and Floats) and Regexs, both of which do return String. What are examples where it returns something else?

Looking at this a little more, I think string() actually works for all my uses, including those where I currently use String(). I wonder if it would be a good idea to just use that function as a general case rather than trying to use the converting version.

The simplest example from Julia Base is:

julia> string(s"12")
s"12"

So SubstitutionString{String} remains a SubstitutionString{String}. But it is pretty obscure case. However, when you start using e.g. https://github.com/JuliaString/Strs.jl this would become a serious issue.

Actually there was a recent PR regarding this Specialize string and repeat for SubString{String} by bkamins · Pull Request #28149 · JuliaLang/julia · GitHub (and there you can see why a discussion why string does not always return String although this is what I have initially proposed :slight_smile:). In Julia 0.6.4 when you run string on SubString{String} it remains a SubString{String}. In Julia 0.7 it will be a String.

1 Like

There is a convention that when T is a collection type, you can construct it from another similar collection using T(other). Examples:

julia> Array(1:3)
3-element Array{Int64,1}:
 1
 2
 3

julia> Tuple(1:3)
(1, 2, 3)

julia> Dict(i=>i for i in 1:2)
Dict{Int64,Int64} with 2 entries:
  2 => 2
  1 => 1

I agree this is somewhat at odds with the default constructors for struct types, which accept field values as separate arguments. However, structs aren’t collections of their fields! A struct might represent something totally different, e.g. a single number.

6 Likes