I've done some work with Dart recently, and I thought that Nim could perhaps benefit from a look at their approach to null safety:
You can read about it here: https://dart.dev/null-safety
The syntax is so much better, in my opinion, as it is more minimal. For example in Nim you'd write:
var abc: Option[string]
if abc == none(string):
abc = some("test")
The same code in Dart:
String? abc;
if (abc == null) {
abc = "test"
}
Going back to Nim I see my code is full of "some" and "none" keywords, where as Dart tries to minimize this syntax.
Is this something Nim could learn from? Nim 2.0 perhaps?
Well it's not overly complicated to make simpler. One issue is didnt use isSome or isNone for the if. There are also utility modules such as optionsutils to make it safer and easier to work with.
import std/options
converter toOpt[T](t: T): Option[T] = some(t)
var abc: Option[string]
if abc.isNone:
abc = "test"
assert abc.get == "test"
You can sprinkle converters beyond the proposal of Elegant beef until you get the syntax you want. For example converting options to bool for "more readable" if branches.
Personally, I think those tricks become quickly problematic in a team setting and as the codebase grows. Not even talking about converter triggering where you wanted the compiler to throw an error.
Put together you could write:
var abc: string?
abc = "testing"
I don't know what the first point means, but to the second it's not a postfix but the following works.
import std/options
template `?`(a: typedesc): typedesc = Option[a]
converter toOpt[T](t: T): ?T = some(t)
var abc: ?string
if abc.isNone:
abc = "test"
assert abc.get == "test"
Going back to Nim I see my code is full of "some" and "none" keywords, where as Dart tries to minimize this syntax.
I usually store the "none" indicator elsewhere and avoid Option[T]. It works much better because op(T) doesn't have to be lifted to op(Option[T]) everywhere.
I don't have answers. I have no problem with the Option[T] method used by Nim myself.
But, has a whole, most languages have a hard time differentiating between four completely different concepts:
nil: memory is not allocated; a C term.
null: per SQL and other definitions, this means "unknown".
empty: means "nothing", a lack of information, or zero. Depends on the type.
none: means there is no such thing as a such a value.
An easy way to demonstrate them is with JSON. The array "abc" in the following:
null:
{
"foo": 1,
"abc": null,
"bar": 3
}
empty:
{
"foo": 1,
"abc": [],
"bar": 3
}
none:
{
"foo": 1,
"bar": 3
}
A nil does not apply to JSON. That is more of a pointer thing. Though, in some languages, nil and null are interchangeable; but not all of them.
Philosophically, I've been thinking about this for the last few years. No answers jump out yet, but I've been thinking about it.
I prefer Nims explicitness.
String? abc;
Makes me cringe.
I tried it option autoconverter, but it doesn't work, for example this code would fail
import options, sugar, sequtils
converter to_option*[T](t: T): Option[T] = some(t)
template pick*[T](list: openarray[T], field: untyped): untyped =
list.map((v) => v.`field`)
echo @[(name: "John"), (name: "Sarah")].pick(name)
Works fine once you call the correct map:
import sugar, sequtils
import std/options except map
converter to_option*[T](t: T): Option[T] = some(t)
template pick*[T](list: openarray[T], field: untyped): untyped =
list.map((v) => v.`field`)
echo @[(name: "John"), (name: "Sarah")].pick(name)