arr = hcat(rand(Char, 10), rand(Int, 10))
writecsv("test.txt", arr)
mx = readcsv("test.txt")
collect(Char, mx[:,1])
# ERROR: MethodError: Cannot `convert` an object of type SubString{String} to an object of type # Char
# This may have arisen from a call to the constructor Char(...),
# since type constructors fall back to convert methods.
# in copy!(::Base.LinearFast, ::Array{Char,2}, ::Base.LinearFast, ::Array{Any,2}) at #./abstractarray.jl:412
# in collect(::Type{Char}, ::Array{Any,2}) at ./array.jl:209
# in eval(::Module, ::Any) at ./boot.jl:234
# in macro expansion at ./REPL.jl:92 [inlined]
# in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:46
Since arr isn't homogenous, I can't do readcsv("test.txt", Char)
I think the simplest thing to do would be
Char(x::Substring{String}) = x[1]
Another solution would be to alllow readcsv to accept multiple type arguments, one for each column. Something like readcsv("test.txt", (Char, Int))
It only makes sense to convert an AbstractString to Char if the string is one character long. Otherwise it should be an error.
I think convert(String, ::Char) and convert(Char, ::AbstractString) methods make sense.
That would be like converting between a scalar and an iterable, which we don't usually allow with numbers and tuples or arrays.
There are a non-zero number of cases where that is allowed with collection-like objects.
julia> convert(Ref{Int}, 1)
Base.RefValue{Int64}(1)
julia> convert(Nullable{Int}, 1)
Nullable{Int64}(1)
Neither of these have supported unconvert operations. Personally, I'm not a huge fan of the asymmetry. But it could be feasible, if we want to stay with this "only convert to box, never to unbox" rule, to define just convert(String, ::Char).
But yes, the point with iterables is understood. I would make the argument that Strings are conceptually more similar to Chars than Array{Int,1} is to Int, though.
That would be like converting between a scalar and an iterable, which we don't usually allow with numbers and tuples or arrays.
Maybe we should revisit this decision? Is there a strong reason why wouldn't want to allow this?
convert usually implies some sort of equivalence. changing the dimensionality or level of wrapping seems like a different operation. the nullable conversion was specifically put in place as sugar for assigning to nullable fields, and personally I don't like it since what you put in with a.f = x is not wrapped, but what you get out with a.f is.
Maybe we can have these as constructor methods then? String('x') makes sense, as does Char("x"). This will not fix the problem mentioned here, however. We could possibly have syntax like scratch that; that won't fix it either. I think the readcsv("test.txt") as Vector{Char} that lowers to as(Vector{Char}, readcsv, "test.txt") to handle these cases, and possibly more general cases also.convert methods are the best option.
I think this bidirectional conversion makes sense, it's just partial in one direction. Now that we don't treat Char like an integer, there's a lot less danger here.
I don't think it makes sense to convert a 1-element sequence to its first element. The CSV package lets you request column types, so I'm going to close this.