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

Tuples and Inline Tables: A Motivation #219

Closed
wycats opened this issue Jun 24, 2014 · 51 comments
Closed

Tuples and Inline Tables: A Motivation #219

wycats opened this issue Jun 24, 2014 · 51 comments

Comments

@wycats
Copy link
Contributor

wycats commented Jun 24, 2014

This builds on #154, which would be very useful regardless of whether this proposal is accepted.

Hurray for tuples! @mojombo

Currently, there is no syntax for inline tables, which means that you're forced to create a new section for simple cases. In Cargo:

[dependencies.hammer]

version = "1.0.0"
git = "https://github.com/wycats/hammer.rs"

We have a shorthand for the case where a dependency only needs to specify a version:

[dependencies]

hammer = "1.0.0"

With tuples, we could do something like:

[dependencies]

hammer = ("1.0.0", "git", "https://github.com/wycats/hammer.rs")

I would like to propose a syntax for inline tables:

[dependencies]

hammer = ("1.0.0", git: "https://github.com/wycats/hammer.rs")
# or alternately
hammer = ("1.0.0", git = "https://github.com/wycats/hammer.rs")

I would be comfortable with restricting inline tables to the last element of a tuple, to eliminate the need for additional braces or something around the table.

@wycats
Copy link
Contributor Author

wycats commented Jun 24, 2014

For what it's worth, this would significantly clean up the biggest areas (so far) where Toml has been an awkward fit for Cargo.

@wycats
Copy link
Contributor Author

wycats commented Jun 24, 2014

I should also note that you get a pseudo-map syntax that you can use in any value position for free via: (foo: "bar") even with the "last-element-in-tuple" restriction.

@wycats
Copy link
Contributor Author

wycats commented Jun 24, 2014

cc @mojombo @BurntSushi

@BurntSushi
Copy link
Member

I am torn about tuples. I very much wanted it >1 year ago (I even implemented it in a separate and long-obsolete branch), but part of me feels like too much time has passed. In my view, adding tuples implies fixing arrays to be properly homogeneous. That would make this existing TOML invalid: key = [["a"], [1]].

Of course, we could add tuples without fixing arrays (this would be backwards compatible), but then we lose some advantages of the type safety that it would bring us and arrays and tuples become less orthogonal.

In any case, I am pretty solidly opposed to inline maps. TOML has a very nice property that there is only one way to construct a map. It'd be great if we could stick to that. The cost here is some vertical space, which I don't think we should be afraid to pay.

With that said, if we added tuples (regardless of fixing arrays), then you could have your inline map by using an association list:

key = [("version", "1.0.0"), ("git", "git://...")]

If that would be an acceptable compromise, then we could throw inline tables out and focus on whether tuples (and maybe fixing arrays) is worthwhile or not.

@wycats
Copy link
Contributor Author

wycats commented Jun 25, 2014

I'm obviously open to a compromise solution, but I think the inline table solution presented above is both well-motivated and far more ergonomic than the just-tuple approach to the problem.

Is there a reason you object to the limited inline table above? Is there a particular form of abuse you're worried about?

[UPDATE] I missed some of your initial response. I don't think that "there is only one way to construct a map" is actually that nice of a property. Can you explain some of the benefits you think it provides and what problems you feel a limited form of inline table might introduce?

@BurntSushi
Copy link
Member

It's not necessarily about abuse, it's about keeping the specification as simple as possible. Stated differently, keep features orthogonal as much as possible. The benefit of inline tables over existing TOML is less vertical space and the benefit of inline tables over TOML w/ tuples is fewer parens and quotes. My feeling is that these aren't enough of a benefit to sacrifice orthogonality. Certainly, reasonable people may disagree. Perhaps we need more opinions? @mojombo? Others?

@alexcrichton
Copy link
Contributor

I definitely like the ability to define heterogeneous tuples, and with the original restriction mentioned adding inline tables would keep the feature sectioned off to only enable use cases that are otherwise impossible.

For example, if tuples were added, an array of tables is possible to express, but a tuple with a table would be impossible to express. The inline tables mentioned would allow you to only have a table at the end of a tuple, but delimiters could likely be introduced to allow tables in the middle (and the delimiters are optional for the last table).

It seems that if inline tables are left out then of the three container types (tuples, arrays, tables), only tuples cannot contain other tables. On the other hand, if inline tables are added, they'd likely only be accepted in tuples, which seems like a similar gotcha.

The succinctness of inline tables at the end of tuples is indeed quite nice, and it likely opens up the door to a bunch of interesting forms of configuration (as seen for cargo), so I'd be slightly in favor, but I can see where it may not necessarily fit in quite well with the rest of the language so far.

@wycats
Copy link
Contributor Author

wycats commented Jun 25, 2014

For what it's worth, I think the before/after is pretty compelling:

Before

[dependencies.hamcrest]

version = "0.5.0"

[dependencies.hammer]

version = "1.0.0"
git = "https://github.com/wycats/hammer"
branch = "wip"

[dependencies.simplediff]

version = "0.2.0"
git = "https://github.com/carlhuda/simplediff"
tag = "v0.2.0"

After

[dependencies]

hamcrest = "0.5.0"
hammer = ("1.0.0", git: "https://github.com/wycats/hammer", branch: "wip")
simplediff = ("0.2.0", git: "https://github.com/carlhuda/simplediff", tag: "v0.2.0")

@DAddYE
Copy link

DAddYE commented Jun 25, 2014

For what it's worth, I think the before/after is pretty compelling:

YES! IT IS!

Considering that in a "normal" project you'll ended up with more dependencies your proposal looks much more important.

@BurntSushi
Copy link
Member

Please also consider what it would look like as an association list:

[dependencies]

hamcrest = "0.5.0"
hammer = [("version", "1.0.0"), ("git", "https://github.com/wycats/hammer"), ("branch", "wip")]
simplediff = [("version", "0.2.0"), ("git", "https://github.com/wycats/hammer"), ("tag", "v0.2.0")]

I'm personally not that miffed by the "before" version, but I recognize that that is up to taste.

@alexcrichton You make some good points. Personally, if we're really going to go down the path of inline tables, then I'd rather not dance around it and add a real dictionary syntax: {key: val [, ...]}.

@wycats
Copy link
Contributor Author

wycats commented Jun 26, 2014

I think the association table is clearly less ergonomic:

Association Table

[dependencies]

hamcrest = "0.5.0"
hammer = [("version", "1.0.0"), ("git", "https://github.com/wycats/hammer"), ("branch", "wip")]
simplediff = [("version", "0.2.0"), ("git", "https://github.com/wycats/hammer"), ("tag", "v0.2.0")]

Inline Tables

[dependencies]

hamcrest = "0.5.0"
hammer = ("1.0.0", git: "https://github.com/wycats/hammer", branch: "wip")
simplediff = ("0.2.0", git: "https://github.com/carlhuda/simplediff", tag: "v0.2.0")

Additionally, you now have to communicate to the decoder that you intend to represent a table with the array of tuples somehow.

We could support inline dictionaries, but I think a rule that allows eliding {} at the tail of a tuple significantly reduces noise.

@mojombo
Copy link
Member

mojombo commented Jun 26, 2014

Allow me to step back and wonder about the fundamental utility of a tuple. Tuples are ordered, typed data. Useful for things like expressing xy coordinates (2.3, 5.9) or aggregate data like name/age/location ("Tom", 35, "Iowa"). The problem with tuples in config files is that they eliminate the key-name of each element, meaning that configuration becomes less obvious. TOML seeks obviousness, and desires it for its config files. Tuples may reduce the amount of text in a config, but they do so at the very high cost of forcing humans to search through documentation to figure out what the hell each tuple element corresponds to. That's a mean trick to play on an innocent configurator.

And yet, the brevity and simplicity of tuples aligns with the minimalist aspirations of TOML. What to do!?

I propose we forget tuples and embrace single-level inline tables. Then we get both minimal AND documented data:

coords = { x: 2.3, y: 5.9 }
person = { name: "Tom", age: 35, location: "Iowa" }

By limiting inline tables to a single level, we keep them simple. We no longer have to worry about 0-tuples or 1-tuples or such nonsense. They map to an unambiguous dictionary, just like everything else in TOML. They eliminate the need for a new data type and complex syntax.

@wycats Would this serve your needs?
@BurntSushi Does this appeal to your sensibilities?

@parkr
Copy link

parkr commented Jun 26, 2014

I propose we forget tuples and embrace single-level inline tables.

👍

@BurntSushi
Copy link
Member

@mojombo I can get on board with that. I like your reasoning about tuples! Let's try flushing it out.

To be clear, this would be disallowed?

person = { name: "Tom", geography: {"lat": 1.0, "long": 2.0}}

Also, this:

person = { name: "Tom", age: 35, location: "Iowa" }

Should have precisely the same representation as

[person]
name = "Tom"
age = 35
location = "Iowa"

Right?

The single level restriction is interesting, otherwise a valid TOML document could start to suspiciously look like JSON. Consider what would happen if we had nested inline dictionaries:

key = {
  a: {
    b: {
      c: 5
    }
  }
}

A few more clarifications:

  • Should we be using key: instead of key =? The latter would be more consistent. It'd be nice to have one rule for all keys.
  • Is the only restriction on inline dictionaries that they cannot be nested? Can they appear in arrays? e.g., [{lat: 5, long: 5}, {lat: 10, long: 10}]. I'd suggest probably not, since we already have table arrays. This also avoids weird cases where there are dictionaries with arrays that have dictionaries. If so, the rule is simple: an inline dictionary may appear as a "top level" value and nowhere else.

@wycats
Copy link
Contributor Author

wycats commented Jun 26, 2014

I think I can get on board with that. Let's do a quick comparison:

Inline Table + Tuple

[dependencies]

hamcrest = "0.5.0"
hammer = ("1.0.0", git: "https://github.com/wycats/hammer", branch: "wip")
simplediff = ("0.2.0", git: "https://github.com/carlhuda/simplediff", tag: "v0.2.0")

Inline Table Only

[dependencies]

hamcrest = "0.5.0":
hammer = { version: "1.0.0", git: "https://github.com/wycats/hammer", branch: "wip" }
hammer = { version: "1.0.0", git: "https://github.com/carlhuda/simplediff", tag: "v0.2.0" }

The primary benefit of the inline tuple version is that it allows stretching the single-value right-hand-side to additional configuration.

@mojombo I think it's worth considering this kind of scenario, where a single value represents some default concept (like a version here), and you may not want to name it simply because you're expanded into dictionary form. That said, I can appreciate that it might be abused. You tell me!

@mojombo
Copy link
Member

mojombo commented Jun 26, 2014

To be clear, this would be disallowed?

person = { name: "Tom", geography: {"lat": 1.0, "long": 2.0}}

Correct, that is disallowed.

Also, this:

person = { name: "Tom", age: 35, location: "Iowa" }

Should have precisely the same representation as

[person]
name = "Tom"
age = 35
location = "Iowa"

Correct.

The single level restriction is interesting, otherwise a valid TOML document could start to suspiciously look like JSON. Consider what > would happen if we had nested inline dictionaries:

key = {
  a: {
    b: {
      c: 5
    }
  }
}

Yes, that's the idea. This syntax is designed to clean up what would otherwise be overly verbose configurations. I definitely don't want them abused to flesh out hierarchies.

  • Should we be using key: instead of key =? The latter would be more consistent. It'd be nice to have one rule for all keys.

Let's see what it looks like with = instead:

person = { name = "Tom", age = 35, location = "Iowa" }

I'm not convinced. I'm worried that being so similar to the normal table syntax will encourage people to always use this format when they should really be using the regular table syntax instead. The colons make it feel like a slightly different, more cohesive object, rather than just a more concise table syntax. I think there's value in that mental distinction. But it does introduce more complexity in key naming rules. @wycats What's your take on this?

  • Is the only restriction on inline dictionaries that they cannot be nested? Can they appear in arrays? e.g., [{lat: 5, long: 5}, {lat: 10, long: 10}]. I'd suggest probably not, since we already have table arrays. This also avoids weird cases where there are dictionaries with arrays that have dictionaries. If so, the rule is simple: an inline dictionary may appear as a "top level" value and nowhere else.

Hmm, I could be convinced either way on this one. There is definitely utility to allowing it, for collections of very simple objects, but you're right about the crazy nesting it would allow. I'm actually leaning towards allowing it, though, mostly because I like the consistency of just saying that an inline table is a value and can be used in the same way as any other value. At some point we have to assume that users will be reasonable in how they write their config files.

@wycats
Copy link
Contributor Author

wycats commented Jun 26, 2014

I'm worried that being so similar to the normal table syntax will encourage people to always use this format when they should really be using the regular table syntax instead. The colons make it feel like a slightly different, more cohesive object, rather than just a more concise table syntax. I think there's value in that mental distinction. But it does introduce more complexity in key naming rules. @wycats What's your take on this?

I agree with you. I think there is value in keeping the two syntaxes different. I also think that the = syntax is just longer and noisier, and people are used to the : syntax from JS/Ruby/Python for inline maps/dictionaries.

@wycats
Copy link
Contributor Author

wycats commented Jun 26, 2014

At some point we have to assume that users will be reasonable in how they write their config files.

I think this should be a guiding principle here. If we provide facilities for sane config files, people will likely use them. Today, it's possible to represent your entire config as a single string-encoded JSON, but people don't usually do it.

I think the ergonomics of "sections" for most cases will clearly win out over people trying to do weird JSON stuff, but there are real cases for small dictionaries in different positions that will win out when the ergonomics favor it.

In other words, people will lean towards doing sane things, all else being equal.

@wycats
Copy link
Contributor Author

wycats commented Jun 26, 2014

Question: is the current status quo heterogeneous Arrays?

@redhotvengeance
Copy link
Contributor

While I understand the utility of this added functionality, I am concerned that it might cause confusion for the TOML user. Curly braces are so familiar (especially with JSON) that it I don't think it will be obvious why TOML would only allow a single level when using them. Anybody coming from JSON will assume curly braces denote an object, and objects can be multi-leveled.

Would it perhaps be better to use parenthesis instead of curly braces? When I see this:

person = (name: "Tom", age: 35, location: "Iowa")

...it strikes me as a special TOML syntax and I'm more prone to understanding why I can't do this:

person = (name: "Tom", geography: ("lat": 1.0, "long": 2.0))

@steveklabnik
Copy link

But TOML is not JSON. I come from Ruby, and "lat": 1.0 isn't correct Hash notation. So by the same token, I should be confused, as well.

I don't have a strong opinion on which way this decision goes, but I think that assuming TOML should be like any other format is a red herring.

@redhotvengeance
Copy link
Contributor

@steveklabnik A good point, to be sure. Though I wasn't assuming TOML should be like any other format, and rather pointing out that users might assume TOML shares conventions with other formats. In most data structures that use {}, nesting is allowed and even essential.

I'm not really advocating for parenthesis, either. I'm just trying to think of a way to format the syntax of this particular feature that makes it more apparent that it has a single-level restriction. The curly braces format is so familiar (including Ruby with {some: {other: {thing: 1.0}}}) that this restriction may cause a lot of confusion.

@BurntSushi
Copy link
Member

@wycats Arrays are homogeneous. The following are not valid TOML values: [1, 1.0], [1, "a"] and [1, []]. However, TOML defines the type of an array as just Array, so these are all valid values: [[1], [1.0]], [[1], ["a"]] and [[1], []].

@BurntSushi
Copy link
Member

@mojombo

I'm not convinced. I'm worried that being so similar to the normal table syntax will encourage people to always use this format when they should really be using the regular table syntax instead. The colons make it feel like a slightly different, more cohesive object, rather than just a more concise table syntax. But it does introduce more complexity in key naming rules.

I'll just make my case and then defer to you. I'd personally take = because it keeps the spec simpler. It's this:

Inline dictionary keys have the exact same syntactic structure as regular keys, except they start after an opening { or a delimiting ,.

Compared to something like this:

Inline dictionary keys start with the first character that isn't whitespace and end with the first : character. Keys cannot contain # or : characters.

We already have two subtly different syntactic categories for keys (keys + table names). This would add a third. Is it a huge deal? Probably not. But I want to give spec simplicity a voice.

FWIW, I actually prefer the key: val syntax in every day usage.

I'm actually leaning towards allowing it, though, mostly because I like the consistency of just saying that an inline table is a value and can be used in the same way as any other value.

We can't quite say that though. We have to say that inline dictionaries can be used like any other value except they may not be used directly as a value in another inline dictionary. And we would assuredly have to clarify that, e.g., {"key": [{"sub": 5}]} is actually allowed and that it is exactly equivalent to the following:

[[key]]
sub = 5

At some point we have to assume that users will be reasonable in how they write their config files.

I'm kind of torn on this one too. But if we're going to give users the alternative to use inline dictionaries to write table arrays, then we might as well go whole hog and let them use inline dictionaries to write nested hashes.

So we've got three options on the table:

  1. Crippled inline dictionaries. They can never appear in inline arrays or other inline dictionaries.
  2. Somewhat crippled inline dictionaries. They can never appear directly in other inline dictionaries, but may appear in arrays.
  3. Inline dictionaries are values like anything else.

@wycats
Copy link
Contributor Author

wycats commented Jun 27, 2014

Both (2) and (3) have perfectly good longer-form representations that would often be appropriate by the time you're nesting.

Instead of:

bins = [
  { name: "conduit", path: "src/main.rs" },
  { name: "routable", path: "src/examples/routable.rs" }
]

You can do:

[[bins]]

name = "conduit"
path = "src/main.rs"

[[bins]]

name = "routable"
path = "src/examples/routable.rs"

My gut feeling is that once you get to multiple levels of nesting, you're better off expanding into long-form anyway. That said, that's probably being too paternalistic here, and we can let people decide on a good trade-off between terseness and clarity for themselves, I think.

@wycats
Copy link
Contributor Author

wycats commented Jun 27, 2014

A lot of ^^ probably has to do with how many keys are involved. An array of tables with a small number of keys (2-3 max) seems totally reasonable inline. An array of tables with 10 keys would definitely be inappropriate for inline treatment.

@BurntSushi
Copy link
Member

@wycats You're probably right about the relationship between # of keys and whether to use inline maps or not. But probably not a good idea to codify that in the spec. (Wasn't sure if you were suggesting that or not.)

@ChristianSi
Copy link
Contributor

I like the idea and proposed syntax of single-level inline tables 👍

@wycats
Copy link
Contributor Author

wycats commented Jul 3, 2014

But probably not a good idea to codify that in the spec. (Wasn't sure if you were suggesting that or not.)

No, definitely not.

@trans
Copy link

trans commented Jul 3, 2014

Maybe just allowed inlined sections.

[person] name = "Tom", age = 35, location = "Iowa"

Or more to the need at hand.

[dependencies]
[dependencies.hamcrest] version = "0.5.0"
[dependencies.hammer] version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip"
[dependencies.simplediff] version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

It can be all lined up real nice like if you want.

[dependencies]
[dependencies.hamcrest]   version = "0.5.0"
[dependencies.hammer]     version = "1.0.0", git = "https://github.com/wycats/hammer",       branch = "wip"
[dependencies.simplediff] version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

Or made something in between.

[dependencies]

[dependencies.hamcrest]
version = "0.5.0"

[dependencies.hammer]
version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip"

[dependencies.simplediff]
version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

Does it satiate the @wycats?

@wycats
Copy link
Contributor Author

wycats commented Jul 3, 2014

@trans This seems worse than any of the other proposals :/

@trans
Copy link

trans commented Jul 3, 2014

LOL 😄 Well, I figured it couldn't hurt to try a minimalist approach.

(I'll just quietly tip-toe back over to my YAML world now).

@wycats
Copy link
Contributor Author

wycats commented Jul 3, 2014

@trans sorry for being curt 😉

@ChristianSi
Copy link
Contributor

In defense of @trans 's proposal I would like to point out that, together with my equally dead proposal to allow newlines as alternative element separator in arrays (#227), it would offer a pretty flexible and simple unified model for writing tables and arrays. Roughly put:

  • Table contents are written as key = value pairs after a table header ([table.header] oder [[table.header]]). Key = value pairs are separated by a newline or a comma.
  • Array contents are written within [ ... ] (after =). Elements are separated by a newline or a comma.
  • In either case, multiple subsequent separators (e.g. newline after comma) are treated just like a single one, and trailing separators are ignored.

This would allow writing tables like this:

[person]
first = "Tom"
last = "Smith"
age = 35
location = "Iowa"

This:

[person]
first = "Tom",
last = "Smith",
age = 35,
location = "Iowa",

This:

[person]
first = "Tom", last = "Smith", age = 35, location = "Iowa"

Or even this:

[person] first = "Tom", last = "Smith"
         age = 35, location = "Iowa"

And arrays like this:

colors = [
    "red"
    "yellow"
    "green"
    "blue" 
]

This:

colors = [
    "red",
    "yellow",
    "green",
    "blue",
]

This:

colors = [ "red", "yellow", "green", "blue" ]

Or even this:

colors = [ "red", "yellow",
           "green", "blue" ]

In either case, the "even" form would be bad taste and should be avoided, but it would pose no problems for the parser.

I don't expect that this proposal will be accepted but still want to point out that it would be quite simple and yet powerful -- certainly simpler than adding a whole new syntax for inline tables, while offering similar benefits.

@wycats
Copy link
Contributor Author

wycats commented Jul 3, 2014

Compare:

With inline dictionaries

[dependencies]

hamcrest = "0.5.0":
hammer = { version: "1.0.0", git: "https://github.com/wycats/hammer", branch: "wip" }
hammer = { version: "1.0.0", git: "https://github.com/carlhuda/simplediff", tag: "v0.2.0" }

With inline sections

[dependencies]
[dependencies.hamcrest] version = "0.5.0"
[dependencies.hammer] version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip"
[dependencies.simplediff] version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

I think inline dictionaries are clearly better here. To my eyes, the alternative crosses the acceptable noise barrier.

@trans
Copy link

trans commented Jul 4, 2014

@ChristianSi Nicely done 👊

@wycats Out of curiosity,

[[dependency]] name = "hamcrest", version = "0.5.0"
[[dependency]] name = "hammer", version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip"
[[dependency]] name = "simplediff", version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

Hmm... ok, bit of a stretch but maybe it would even be possible to have array-table eliding,

[[dependency]] "hamcrest", version = "0.5.0"
[[dependency]] "hammer", version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip"
[[dependency]] "simplediff", version = "0.2.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0"

@BurntSushi
Copy link
Member

@ChristianSi Here's the issue. It seems to me that the whole point of adding inline tables is for aesthetic reasons. Your proposal has an interesting uniformity in syntax, but it isn't very aesthetically pleasing IMO. You've added optional commas to places that don't need them (key-values) and make commas optional in places that traditionally have them (arrays).

My personal view leans toward rejecting inline tables. However, I agree with @wycats that inline tables can really reduce noise in legitimate use cases. I agree with his before/after. I'm just not sure if it's important enough to add inline tables.

If we do go with inline tables, then I fully support the { ... } syntax. This is used in many languages and other config formats, so it is immediately recognizable. It's also reasonably lightweight. The only other thing to decide is what our nesting semantics are. Personally, I'd rather they either not be allowed to be nested at all or have them be first class values like everything else.

@redhotvengeance
Copy link
Contributor

👍 Agree with @BurntSushi, including preferring to not add inline tables. I can see the benefits in edge cases, but I feel like it duplicates functionality in the spec.

@wycats
Copy link
Contributor Author

wycats commented Jul 4, 2014

@redhotvengeance I really do not agree that my motivating cases here represent edge-cases.

@wycats
Copy link
Contributor Author

wycats commented Jul 4, 2014

@BurntSushi I am comfortable starting off with non-nested inline tables, and seeing whether we find a strong motivation for supporting further nesting.

At the moment, I find it hard to believe that you could reasonably fit nested tables in a single line, and I think using inline tables for multi-line cases is probably an anti-pattern.

@ChristianSi
Copy link
Contributor

@BurntSushi

Your proposal has an interesting uniformity in syntax, but it isn't very aesthetically pleasing IMO.

I don't agree. Admittedly the simplicity of the proposal would allow ugly variants, but nobody would be forced to use them. My personal aesthetic preferences would be:

  • Always write table headers in a line of their own.
  • If the whole contents of a table or array fit into a single line (max. 78 chars or so), write them in a single line, comma-separated.
  • Otherwise write them in multiple lines, newline-separated.
  • Never mix commas and newlines as separators, use one or the other.

So, I would write tables like this:

[person]
first = "Tom", last = "Smith", age = 35, location = "Iowa"

Or this:

[person]
first = "Tom"
last = "Smith"
age = 35
location = "Iowa"

And arrays like this:

colors = [ "red", "yellow", "green", "blue" ]

Or this:

colors = [
    "red"
    "yellow"
    "green"
    "blue" 
]

I think that's pretty aesthetic. (Indeed, my original motivation for proposing newlines as alternative array separators was purely aesthetic.)

I do agree that the { ... } syntax for inline tables (for simplicity, without nesting) is a reasonable alternative. But because of the simplicity of my proposal (no need for a new separate syntax for inline tables, unified separators for tables and arrays) I think it deserves more serious consideration than you've given it so far.

@mojombo
Copy link
Member

mojombo commented Jul 4, 2014

I find that when there are no simple answers it's best to focus on the big goals of TOML: obviousness and minimalism. I'll endeavor to ask the outstanding questions here, and then answer them with those principles in mind.

Does TOML need an inline syntax at all? Doesn't that go against being minimal?

Everything added to TOML makes it less minimal. But, like Einstein said, things should be as simple as possible, but not simpler. I think @wycats makes a very compelling case for the existing TOML spec being too simple. So I'm still +1 on the idea.

Should we use : or = as the assignment operator?

At first I thought that colons would be better, but I think that's just because I'm used to them from Ruby. After sitting with the various syntax forms for a while I don't mind them with = and @BurntSushi is very much right that introducing ANOTHER syntax for keys is non-obvious and non-minimal. Sharing rules with regular keys is better for both writer and implementor.

person = { name = "Tom", age = 35, location = "Iowa" }

Can inline tables be nested?

I'm going to say yes. It's the answer that's most obvious and means there's less to explain. Inline tables should just be values, treated like all other values. This means that people could abuse them, but I think we should optimize for the best case, not the worst case.

person = { name = "Tom", geography = { lat = 1.0, lon = 2.0 } }

Can inline tables appear in arrays?

Yes, they are just values.

coords = [ { x = 1, y = 3 },
           { x = 2, y = 4 },
           { x = 3, y = 5 } ]

Are inline tables single-line only (no newlines allowed within the curly braces)?

If we want to prevent abuse, then this seems like the best way to do it. I think people will naturally avoid long lines, and thus avoid using inline tables for undesirable nesting and other inappropriate uses. This dovetails nicely with the previous two answers, allowing for consistent value semantics while only constraining formatting.

@wycats said "I think it's worth considering this kind of scenario, where a single value represents some default concept (like a version here), and you may not want to name it simply because you're expanded into dictionary form."

I don't like that option, it feels weird to me. Every key should have an explicit value, period. Obvious. Minimal. Unambiguous mapping to a hash table is the most important thing that TOML will ever have.

@ChristianSi
Copy link
Contributor

I can live very well with everything @mojombo said and withdraw my own proposal. (Not that I seriously expected it to be accepted anyway.)

@redhotvengeance
Copy link
Contributor

Same. I can get onboard with @mojombo's proposal. 👍

@wycats
Copy link
Contributor Author

wycats commented Jul 5, 2014

👍 on @mojombo's conclusion 😄

Let's get this merged so we can get to implementing it!

@wycats
Copy link
Contributor Author

wycats commented Jul 5, 2014

Just a quick sanity check for posterity:

[dependencies]

hamcrest = "0.5.0":
hammer = { version = "1.0.0", git = "https://github.com/wycats/hammer", branch = "wip" }
hammer = { version = "1.0.0", git = "https://github.com/carlhuda/simplediff", tag = "v0.2.0" }

@wycats
Copy link
Contributor Author

wycats commented Jul 5, 2014

It's slightly more verbose and the = to the left of the dictionary and inside kind of bleed together, but I can live with it.

@BurntSushi
Copy link
Member

👍 @mojombo. I just want to flesh out one thing:

If we want to prevent abuse, then this seems like the best way to do it. I think people will naturally avoid long lines, and thus avoid using inline tables for undesirable nesting and other inappropriate uses. This dovetails nicely with the previous two answers, allowing for consistent value semantics while only constraining formatting.

So would something like this be disallowed?

key = { some_array = [
    1, 2, 3, 4
] }

If not, then the "new line" restriction would only apply to the syntax of { ... } itself rather than values in them. (I think only arrays and future multi line strings would allow the introduction of new lines.)

I think I like allowing my example above because it avoids restricting other values inside inline dictionaries.

I'm happy either way though.

@wycats
Copy link
Contributor Author

wycats commented Jul 5, 2014

@BurntSushi Sounds like the idea is that the opening { and closing } of an inline table must appear on the same line.

@wycats
Copy link
Contributor Author

wycats commented Jul 5, 2014

(so yes, disallowed)

@mojombo
Copy link
Member

mojombo commented Jul 16, 2014

I've opened PR #235 to push this forward. Closing this issue; all future conversation should happen over there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants