Personally I'm happy it doesn't have explicit null and somewhat surprised it supports NaN (which I thought it didn't)
To my mind, a configuration file using "this key doesn't exist" as one of several valid options seems deeply unsound.
I disagree. There is a big difference between configuration language formats and programming languages.
> To my mind, a configuration file using "this key doesn't exist" as one of several valid options seems deeply unsound.
I would agree, but in that case you now often need a secondary key to "turn something off" which is often not done.
You're right! But in my opinion the difference is that a configuration file should have a default state that could be written to disk and loaded with no change in behaviour.
eg you can set `feature = "on"` or `feature = "off"` and if you don't set either it's the same as writing `feature = "off"`.
Having a secret third thing of `feature = null` should be outlawed and I'm glad TOML doesn't encourage it
It's also particularly odd in lists where I have seen `["foo", "bar", {}]` show up in the real world with `{}` being used as a replacement value for null.
In [1]: example = {"foo": None}
In [2]: print(example["foo"])
None
In [3]: print(example["bar"])
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[3], line 1
----> 1 print(example["bar"])
KeyError: 'bar'
In [4]: print(example.get("bar", "sensible default"))
sensible default
If configuration values were arbitrarily nullable I would either need to deal with the possibility of nulls ending up anywhere in my program (back to The Billion Dollar Mistake) or have to have checks on parsing to say which fields are nullable or not.I acknowledge people have done silly things regarding missing keys already - but I think they should be discouraged from continuing to do so, not enabled.
In Rust it's possible to deserialize to an Option.
https://play.rust-lang.org/?version=stable&mode=debug&editio...
In general: I agree. But configuration formats (including TOML) also need to map well to programming languages, so you can't seem them as entirely separate. One argument against Null is that it doesn't elegantly map to a data structure in all programming languages.
TOML has plenty of constructs that don't map well to many languages. Plenty of language implementations do not know how to work with the datetime type, some TOML implementations barf on objects in lists. In some TOML implementations you also end up with nulls showing up when certain empty table constructs are used.
Right, and IMO datetime was a mistake to include in TOML.
- unset: ignore values of this property
- initial: use the default value
These would be useful e.g. with overrides.
I agree that nulls in a config files are often not a good idea because it is not clear how they are going to be interpreted.
PS: Css also has inherit that could mean "use the local default" while initial could mean use system default.
They are about as useful in a configuration language, where the cascading logic also applies.
If you are referring to Tony Hoare, the “billion dollar mistake” was not the existence of nulls per se, but that all references were nullable, with no support in the type system to distinguish between nullable and non-nullable references.
https://github.com/toml-lang/toml/issues/921
https://github.com/toml-lang/toml/issues/803
https://github.com/toml-lang/toml/issues/802
https://github.com/toml-lang/toml/issues/146
https://github.com/toml-lang/toml/issues/30
Obviously there is a bit of a demand, and it certainly has valid use cases, but also has downsides.
And that ignores the issue of nulls in arrays which are sometimes needed. For instance I have been exposed to parameterized SQL queries in TOML where you need to use the empty object to represent NULL, a particularly absurd incarnation of this.
Option-style algebraic types are another, and more or less strictly superior if working in a language with first class support for such, because they force you to explicitly handle the null/None/Empty/whatever case if you’re doing something with it that could fail (basically anything besides assignment or passing it along).
You can add advanced semantics on load, the file does nothing by itself after all.