Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crazy idea: change the type keyword #19157

Closed
JeffBezanson opened this issue Oct 29, 2016 · 167 comments · Fixed by #20418
Closed

crazy idea: change the type keyword #19157

JeffBezanson opened this issue Oct 29, 2016 · 167 comments · Fixed by #20418
Labels
breaking This change will break code needs decision A decision on this change is needed

Comments

@JeffBezanson
Copy link
Member

JeffBezanson commented Oct 29, 2016

Over time, this keyword has increasingly bothered me. There are several problems:

  • type is a very generic word and I'd just as soon leave it available for variable names (and keyword arguments!)
  • There are now several kinds of types, so it seems strange to use type to refer only to concrete, mutable, struct-like types.
  • type is the most obvious type-defining keyword, but usually immutable is preferable and recommended.

Here are some alternate keyword suggestions:

  • mutable -- opposite of immutable!
  • struct -- at least says something about what sort of type it is
  • reftype -- for "reference type", which also conveys some of its key properties
@JeffBezanson JeffBezanson added needs decision A decision on this change is needed julep Julia Enhancement Proposal labels Oct 29, 2016
@johnmyleswhite
Copy link
Member

Making the word type usable in other contexts would be great.

@quinnj
Copy link
Member

quinnj commented Oct 29, 2016

I do like mutable for consistency.

@tknopp
Copy link
Contributor

tknopp commented Oct 30, 2016

If this is done, it should be done ASAP so that it can be included in 0.6

@joa-quim
Copy link

If this is going to happen, than use what everybody recognizes before thinking: struct

@KristofferC
Copy link
Member

I also think type is a bit awkward. When I explain it to other people I usually say "like a struct" so +1 for that.

@andyferris
Copy link
Member

andyferris commented Oct 30, 2016

If this is going to happen, than use what everybody recognizes before thinking: struct

I thought immutable was like a struct?

+1 for mutable

@tknopp
Copy link
Contributor

tknopp commented Oct 30, 2016

immutable has the memory layout of a C struct
type has the mutability of a C struct

Thus struct is related to both. I think mutable instead of struct is better to clarify this.

@joa-quim
Copy link

Any variable is mutable. Assigning that label to a universally recognized name (a struct) seams all but clear for me.

@yuyichao yuyichao added the breaking This change will break code label Oct 30, 2016
@goszlanyi
Copy link

For someone coming from Fortran the keyword type is already natural.
For someone coming from C the keyword struct would be natural.
For someone using immutable a lot, the opposite keyword mutable would be natural.

So maybe we should leave it as is.

@tknopp
Copy link
Contributor

tknopp commented Oct 30, 2016

struct{mutable}
struct{immutable}

or

record{mutable}
record{immutable}

if a more descriptive form is desired.

@JeffBezanson
Copy link
Member Author

If we use struct then the syntax for immutables can be immutable struct (with the struct part being optional at least initially).

I suspect Fortran programmers are familiar with the term "struct".

@barche
Copy link
Contributor

barche commented Oct 30, 2016

+1 for mutable because of consistency. If struct were chosen, wouldn't consistency dictate changing immutable to something like const struct? ;)

@JeffBezanson
Copy link
Member Author

I think struct and immutable struct would be pretty good actually; struct is a very familiar term and we'd only need to add one new keyword. Allowing mutable struct might make sense too, even though it isn't strictly required.

@JeffBezanson
Copy link
Member Author

I should clarify that I think struct being a noun is an advantage. It would be better not to only use adjectives to name these things.

@mauro3
Copy link
Contributor

mauro3 commented Oct 30, 2016

Having to add the struct to immutable struct adds unnecessary line-noise, even more so having been used to not having to type this.

@StefanKarpinski
Copy link
Member

Otoh, I believe Fortran has a type keyword that does something very similar to what ours does. I'm not averse to changing this, however.

@JeffBezanson
Copy link
Member Author

@KristofferC above has it right: I also find myself saying "struct" when explaining what this is. Even in internal discussions type has been a problem; somebody will say "suppose x is a type", and it's not clear whether it means "thing declared with the type keyword" or "type" in another sense.

@vtjnash
Copy link
Member

vtjnash commented Oct 30, 2016

If these names are really on the table (s/type/new name/ seems slightly obnoxious), I'd suggest we might want to make the most of this opportunity to make the default declaration an immutable.
So struct would replace immutable, and mutable [struct] would replace type.

Just as a thought, another possible term for mutable might also be Ref.

@stevengj
Copy link
Member

Another possible name would be composite, since a generic CS term for this is a composite type. But I still prefer mutable Foo and immutable Foo to mutable struct Foo and immutable struct Foo — the struct (or composite, or class, or whatever) seems superfluous.

@timholy
Copy link
Member

timholy commented Oct 30, 2016

👍 to noun(s)

@andyferris
Copy link
Member

andyferris commented Oct 30, 2016

I always liked type more than struct in Julia (even though my background is C), because Julia's datatypes are a whole bunch more powerful and subtle and I think "struct" might be a bit simplistic for the idea. I also feel like since this is a new language we shouldn't be bound by convention that seasoned programmers are used to - you should also consider "what would be the best word to explain this to a person who's first language is Julia".

I also find myself saying "struct" when explaining what this is

Imagine introducing a newcomer to struct T then telling them T::DataType and you can dispatch using ::Type{T}, all while explaining Julia's "powerful type system". I think this is just kicking the bucket further down the road.

being a noun is an advantage

👍 to noun(s)

Probably worth noting that Latin-language-family adjectives like "mutable" are grammatically also allowed to be nouns in there own language. That's why when we ask "what is the noun form of mutable" we shrug our shoulders. But we always say "a variable" and in Julia-speak I would say "let's introduce an immutable for this". Unfortunately it feels awkward at first because of the Germanic roots of English grammar - but this happens really often in English (because we are also based on French and Latin) but you get used to it with time.

Finally, since the topic is "crazy idea", these keywords more-or-less construct a new DataType so could we remove the keywords entirely and just use a constructor?

# Similar-ish to current
MyType = DataType(
    a::Int,
    b::Float64
)

# A DataFrame-like constructor
MyType = DataType(a = Int, b = Float64)

I guess these would have to be specially (statically) parsed like ccall is and they can only be called at toplevel for now. (Also - not sure if we'd need Mutable <: DataType and Immutable <: DataType and BitsType <: DataType to cover all possibilities, and probably const for the binding).

@andyferris
Copy link
Member

I thought immutable was like a struct?

immutable has the memory layout of a C struct
type has the mutability of a C struct

Right... I think I was thinking of Swift, where:

This is the biggest difference between structs and classes. Structs are copied and classes are referenced. ... Thus, they will be passed as values not as a reference in the case of large objects.

which sounds like immutable vs type, and it is only the reference which allows the mutability in the context of the semantics of Julia bindings.

@JeffBezanson
Copy link
Member Author

DataType includes all nominal types: abstract, bitstypes, immutable and mutable structs. So it doesn't add much clarity to replace type with DataType. I also draw a different conclusion from the "newcomer" scenario: ::Type and ::DataType select supersets of the things defined by type T, so it's not crazy for them to have different names. "struct is one kind of Type" makes more sense than "type is one kind of Type".

I agree the adjectives can in practice serve as nouns. One can say e.g. "this is an immutable". I just think they are insufficiently descriptive. Rather than telling you what the thing is, it just tells you that whatever it is, you can/can't mutate it.

@bramtayl
Copy link
Contributor

bramtayl commented Oct 30, 2016

I like structure and mutable structure for types. maybe a = 1 and mutable a = 1 for variable assignment. Going off the idea of wanting consistency, no abbreviations, and immutability by default.

Edit: maybe constant instead of const in the mutability by default case

@JeffBezanson
Copy link
Member Author

Or a version that involves having fewer keywords would be struct vs. const struct. Making all variables const by default would be too disruptive at this point I think.

@andyferris
Copy link
Member

andyferris commented Oct 31, 2016

I just think they are insufficiently descriptive. Rather than telling you what the thing is, it just tells you that whatever it is, you can/can't mutate it.

That's a good point - but I would consider the referencing behaviour vs "value" behaviour much more important and interesting to describe than "is it a struct or a bitstype?" (mostly because bitstype doesn't come up very frequently - every Julia type I've created is a struct). Immutability is just a consequence of the binding semantic and pass-by-value.

To completely obviate the need for struct vs bitstype we could also include this idea from @quinnj which was

immutable 32 Int32 <: Integer

or you could have un-adorned fields as number of bits

immutable Int32 <: Integer
   32
end

which (in general when mixed with named fields) could also be useful for inserting padding when necessary.

Then it's just a choice between a value type (what is currently immutable) vs a reference type (what is currently mutable - but potentially with const fields in the future which is another reason to not call it mutable) and appropriate names are needed to distinguish these. And finally, using struct for the value reference type will confuse Swift programmers...

@JeffBezanson
Copy link
Member Author

It's true that bitstype has all but disappeared since immutable was introduced. But there are more things to distinguish than struct vs. bits: there are abstract types, tuples (also immutable), and likely immutable records in the future.

We originally picked immutable since we felt that would be the most visible distinguishing feature: unlike other objects, you can't modify them. In fact, they are not always passed by value; immutability means you can't tell the difference. Some immutables are inlined into arrays and other objects, and some aren't. Immutability means that difference causes no problems for plain julia code; it only becomes an issue when memory layout really matters e.g. for C interop.

@JeffBezanson
Copy link
Member Author

pulling in all the confusing const-ness issues of C/C++

No renaming of this keyword would do that. Those issues have to do with static checking and casts, which aren't involved here at all.

the intent of being a value type or a shared reference type would have helped me significantly, instead of simply refering to mutability

I don't fully understand this. If an object is immutable there's no way to tell whether it's passed by value or reference, so how can the value/reference distinction be fundamental?

datatype is not a bad choice. But I don't think datatype vs. composite datatype is a good way to convey the mutability distinction.

@m-j-w
Copy link
Contributor

m-j-w commented Nov 19, 2016

@JeffBezanson, with C-like const-ness I meant the possibly implied expectation of the Julia type to have same behaviour as in C/C++: immutable only after construction, modifiable anyway through casting, sometimes being without effect at all for function arguments or return types. Thus, when choosing keywords probably not refering to C/C++ is a good choice, hence please not const struct.

Regarding 'value vs. reference': It took me quite a while to understand why an immutable type embeds the members directly and produces nice and compact fast assembler code, but 'type' didn't and still contains all the calls to objects. Or in other words, why I would typically want to use an NTuple when aiming for speed. Thus, when reading 'immutable' and 'mutable' I'd have expected the meaning as a 'parameter' in Fortran, but not also the change in the layout or kind of implementation of the actual datatype.
Probably, most of the time one might want to have 'immutable value' semantics for small composite types like a Complex, but 'mutable and refering/non-copying' for large data sets like arrays etc.
Then, the underlying intent is probably not the mutability, but avoiding copying data around.

However, that was my experience when learning the Julia low-level machinery to obtain fast assembler code, but I don't know how to better reflect that in naming keywords and how to combine or express that just with mutability.

@andyferris
Copy link
Member

I get where you're coming from @m-j-w, I've been there, but I think you're better off unlearning everything you know about C/C++. Here, it is the const-ness that is immutable and whether something is stack or dynamically allocated, and whether it is passed by reference or value, are free to vary and will be optimization choices for the compiler. You could make a language where everything is dynamically allocated and passed by reference, and this is Julia's default way (for boxed values - it implements "pass by sharing" even if the type is unknown). You (and the rest of us) notice that small immutables are so much faster because that's where optimizations are currently implemented. This is the result of the semantics of immutable making optimizations easier.

But, really, I would prefer large immutables were passed by reference (perhaps by stack pointers, where that makes sense, but it is probably best to heap allocate an NTuple{1_000_000}) and that small mutables of known size are stack allocated (again, where possible). Even garbage collection could be statically determined in some cases, allowing heap-space to be reused inside loops for example. I picture these as future optimization opportunities that require escape analysis (and none of these affect the semantics of the language, just the performance).

I think what I'm saying is that the keywords need to reflect semantics which will stay constant for a decade or two, not implementation details that will change in a couple of years (i.e. efficient small mutables and efficient large immutables).

I meant the possibly implied expectation of the Julia type to have same behaviour as in C/C++

This might be a good enough reason in itself to consider const -> constant, to be honest. I think const might be our only abbreviated keyword at the moment, making it an odd one out anyway.

a simple Alias = DataType statement.

I'm pretty sure this is absolutely fine where it makes sense, but it won't allow more complex modifications of the type, e.g. typealias RealMatrix{T<:Real} Matrix{T}, nor should it (it's a simple binding, not creating a new Type with new TypeVars). Unless you want to define it the other way around: RealMatrix = Matrix{T <: Real} and generalize what apply does for a TypeVar (i.e. makes a TypeConstructor)... actually that's an interesting idea (but it does have syntax issues, which is why typealias is nice...).

@m-j-w
Copy link
Contributor

m-j-w commented Nov 19, 2016

@andyferris, I already 'unlearned', but for other new users it may be easier to get over the 'why is Julia so slow despite compilation' phase. Picking keywords that support a fast development of a mental model of those details can be a huge selling point to new users not having too much of a computer science background. Not to forget 'simple' keywords to avoid other spoken language barriers either.

@andyferris
Copy link
Member

@m-j-w Great, and I do agree with all of that. :)

@ararslan
Copy link
Member

Tell you what, let's try the closest thing we can to a poll using the very best GitHub functionality: reaction emojis.

Here I've tabulated, in no particular order, the suggestions that appear to have the most support. React using the specified emoji to cast your vote for that suggestion. I believe this requires accessing GitHub through a desktop browser; AFAIK reaction emojis haven't been implemented in the mobile interface. Click the face in the upper right corner of this post to access the menu of emojis.

type immutable react with
struct const struct 👍 (+1)
mutable immutable 👎 (-1)
type immutable 😄 (Laugh)
composite const composite 🎉 (Hooray)
datatype const datatype 😕 (Confused)
type const type ❤️ (Heart)

Failure to vote corresponds to an implicit vote for nuple, of course. :trollface:

@TotalVerb
Copy link
Contributor

I have to say, I'm not a huge fan of doing contentious decisions like this "democratically". If discussion has played out with no consensus, I would just defer to the decision of the BDFLs.

@ararslan
Copy link
Member

ararslan commented Nov 19, 2016

Fair point, @TotalVerb. As I said before, I'd be totally fine having the powers that be just give us whatever they think is best. It seems to me like the thread is meandering a bit and @juliohm's point about voting seems reasonable, so I figured I'd give this a go and see what happens. I see it as a way to more clearly show what the consensus is, but I definitely think the final decision should be left to our overlords, regardless of what the community consensus actually is.

@Keno
Copy link
Member

Keno commented Nov 19, 2016

Personally, I don't think I like having two words aesthetically (const type). It also feels a little semantically dissonant. When we say const x = 1. We mean that x is constant. When we say const type; x; end we'd mean that instances of this type don't change after construction. From straight interpolation it feels like saying something is a constant type means that the type itself doesn't change (which is already the case now). See also:

type foo
end
const bar = foo

So bar is const and it's always foo, but yet it's not a const type. It feels like wrt to the existing semantics of const (applying to bindings), the names of types are already const.

@benninkrs
Copy link

I suspect there are no keywords that are concise, convey the intended semantics, and are unlikely to be desirable as variable names. The terminology I feel comes closest to these goals is mutable datatype, immutable datatype, and abstract datatype for the three kinds of declared types.

@wgreenr
Copy link

wgreenr commented Dec 3, 2016

I wish in addition to type and immutable there was an additional mutable value type.
I have seen a few discussions somewhere in issues, but basically at the moment it is impossible to efficiently handle scenarios like this (correct me if I missed something):

immutable T
    a::Int
    b::Float64
    ...
end

t = Vector{T}(10)
t[2].a = 5    # error here
# instead
c = t[2]
t[2] = T(5, c.b, ...)   # works  

The code creates a neat array of value types sizeof(T) * 10, but to change some field one needs to reassign entire T(...), which is typically 3-5 fields long.
So basically my 5 cents:
type - GC collected reference
immutable - value (stack allocated) type which is impossible to change
mutable - value type which is possible to change, especially in arrays

@johnmyleswhite
Copy link
Member

value type which is possible to change

I think this is an impossible-to-achieve goal since allowing changes is precisely what gives something object identity.

Perhaps you would like this approach instead: #11902

@be5invis
Copy link

be5invis commented Dec 5, 2016

except for separating mutables and immutables, it should be better if we split values and references.

abstract Node{T}
immutable Link{T} <: Node{T}
    value :: T
    next :: ref{Link{T}} # next :: Link{T} will be an error: Recursive type
end

@TotalVerb
Copy link
Contributor

There is no need to overcomplicate the language with an artificial distinction between values and references. Mutable and immutable types are semantically much simpler; the fact that a type is a value type is merely an optimization.

@andyferris
Copy link
Member

@Keno makes a good point

Personally, I don't think I like having two words aesthetically (const type). It also feels a little semantically dissonant. When we say const x = 1. We mean that x is constant. When we say const type; x; end we'd mean that instances of this type don't change after construction. From straight interpolation it feels like saying something is a constant type means that the type itself doesn't change (which is already the case now)

As I mentioned earlier, I think the "correct" semantics is given by

type Complex{T}
    const re::T
    const im::T
end
z = Complex(1,2)

Here z.re behaves just like when we type something akin to const z.re = 1, so there is no semantic confusion about exactly which binding is const.

As @JeffBezanson states, this isn't really convenient for large numbers of fields, and isn't compatible with the current implementation of types. Partially mutable, partially constant structs may or may not be implemented in the future. However, there is a syntax which makes sense both currently and into the future:

@const type Complex{T}
   re::T
   im::T
end

The current way this is implemented, a macro can change the from type to immutable in the AST. In the future, if partially-constant structs are implemented, then the macro can still do the manipulations to the AST by adding const to each field, and it will be clear to all and sundry that it is the field bindings which are constant. Aesthetically, it is better than two keywords in a row - we do similar things for functions (@generated, @inline, @proagate_inbounds, etc).

@const could easily be @immutable or something similar (@immutable type Val{T}; end reads OK, IMO) and the macro idea is compatible with struct, composite, and so-on.

@wgreenr
Copy link

wgreenr commented Dec 5, 2016

@be5invis yes, this is a usual way in many languages, e.g. in C# there is struct, which behaves in many ways as immutable: it is passed by copy to functions, is explicitly allocated in arrays (not references to GC-managed objects as in the case of class|type). However it is mutable, and in arrays can be modified by a single field.
What I do believe that in the case of immutable there is a mix of 2 properties:

  1. Immutability (for doing proper functional programming)
  2. Behaving as a value type on the lower level

So basically I would love to see the introduction of a type with just property 2. This won't disrupt the existing codebase (immutable remains), but for those who writes something fast with minimal GC involvement it would be valuable.

@johnmyleswhite thank you for #11902, still believe that it should not be that ugly

@TotalVerb
Copy link
Contributor

@wgreenr As mentioned earlier, and has been discussed many times, that is not possible. It is also not really related to the discussion in this issue.

@StefanKarpinski
Copy link
Member

Anything's possible but introducing mutable value types would be wildly disruptive and a very bad idea. I can understand why it's an apparently simple and appealing idea, but adding a new, entirely different kind of object semantics and bifurcating the type graph into reference and value semantics is not going to make the language better. What it would accomplish is making it impossible to write generic code safely since the same code would do very different things depending on unknown semantics. There's an extensive discussion with examples here. The point of #11902 is to recover benefits of mutable value types without wrecking things.

@TomasPuverle
Copy link

I'm not sure if this is even worth mentioning; but in C/C++ 'struct' also implies something about the layout of the data. On the other hand, it is frequently worthwhile to re-arrange struct members to get better memory layout. If you're considering a breaking change to the syntax, might it be worthwhile to have a mechanism to somehow differentiate between 'structs' (ordered -> C interop) and 'immutable's (could potentially be ordered in any way).

I'm fairly new to Julia, so I'm sorry if I missed something obvious.

I personally have no real preference wrt names; however, I'm surprised that nobody seemed to have suggested "concrete", given that there already exists "abstract".

@vtjnash vtjnash removed the julep Julia Enhancement Proposal label Dec 12, 2016
@ilyagr
Copy link

ilyagr commented Dec 21, 2016

To add yet another suggestion, the following seems best to me:

  • Use struct instead of immutable.

  • Use struct! instead of type. When speaking English, this should be referred to as a "mutable struct".

  • (Perhaps) allow having some of the fields inside a struct! to be marked as const.

Some justification and thoughts:

  1. Unless I missed something obvious, you never want to use a function with a ! to manipulate a value of an immutable type. If that is wrong (e.g. if there is an important case where the immutable type refers to a mutable one, and the function with a ! changes it), this suggestion may not be a good one.

  2. I think that immutable types should generally be preferred over mutable ones, since they are subject to more optimizations and have a more predictable behavior for the programmer.

  3. For this to work, it is probably a prerequisite to implement the optimizations to make immutable types at least as efficient as mutable types in almost all cases. In particular, the compiler should be smart about passing large immutable types by reference.

UPDATE: I used to have record instead of struct, but record! is too much like a verb. Some other options that come to mind: newtype, typedef (which will confuse C programmers for 10 seconds), composite, or concrete as somebody above suggested.

@andyferris
Copy link
Member

Is it likely something will happen in this space before v0.6 feature freeze?

@ararslan
Copy link
Member

Probably not, given that no decision has been made (at least publicly) and we're just ~10 days away from the freeze.

@timholy
Copy link
Member

timholy commented Jan 3, 2017

I haven't followed this carefully, but one comment is that if/when this does change, I'd like to request that we first have really good reporting of the offending file/line number in place. Or at least the module; in 0.5 it's still sometimes difficult even to figure out which package has a @deprecated_binding.

@Tetralux
Copy link
Contributor

Tetralux commented Feb 4, 2017

xref #20418 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking This change will break code needs decision A decision on this change is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.