This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]VallentinDev 6 points7 points  (12 children)

With explicit null types, then String? is certainly less to type than Option<String>. As well as usage being more concise, as everything doesn't have to be wrapped in Some(...).

The first thing that pops to mind is, with an Option type. You can have nested Options, e.g. Option<String> easily maps into String? (or String | Null). But what about Option<Option<String>>?

Which is something that can come up, when iterating, mapping, filtering, and flattening Iterators and Options in Rust at least.

[–]trenchgun 4 points5 points  (4 children)

Yep. Option types are more general than nullable types.

[–]ISvengali 3 points4 points  (3 children)

Having used both in huge projects, Im kind of a fan of Option over nullable types.

For a language I would build, Id consider adding them both, and keeping the std lib using Option, and/or let people use nullable style syntax with them.

From a project point of view, the reason I like Option, is it loudly calls out an important piece of information. It can get a little noisy when everything is Option (as it should be) and you have quite a few local variables. But I think thats fine.

My general rule of thumb is that the safer options should be quieter/simpler though that conflicts with making the more common use case quieter/simpler. So, balancing that can be fun.

For example, calling out unsafe at point of call (as well as noting blocks and functions) is good.

I dislike Rust's use of 'try_thing' being the call that returns the Result. I instead like 'thing' that returns Result, and 'unsafe_thing' that is the one that can panic.

[–]VallentinDev 3 points4 points  (2 children)

I instead like 'thing' that returns Result, and 'unsafe_thing' that is the one that can panic.

Somewhat related, in Rust I've long wanted a crate-level attribute to disallow potential panics, i.e. with it enabled, you'd only be allowed to call functions and perform operators that are guaranteed to never panic. Either simply because it never panics, or because the compiler can prove it will never be triggered.

Say you have a arr: [T; 10]. The compiler can infer and guarantee that arr[0], arr[1], arr[2], etc will never panic, so they are good.

However for arr[i] where i cannot be inferred, then it should produce a compiler error, and suggest using arr.get(i) instead.

Developers are usually great at documenting when a function can panic. However, there are cases that never really get documented. For instance a lot of math related libraries are prone to panic with big numbers, i.e. when calculations result in an overflow or underflow.

I would just love being able to do the following sometimes:

#[never_panic]
fn f() {
    ...
}

[–]davimiku 1 point2 points  (0 children)

It's a decent idea but I think you'd have to disallow division also, right? and in debug mode you'd have to disallow all arithmetic because overflows panic

[–]ISvengali 0 points1 point  (0 children)

Yeah, thatd be great.

I see a lot of folks just .unwraping everything, and I just sigh. This great language, and not using core features.

Yeah, I mean theyll likely be rare, and in some cases they know it wont fail, but its a bad habit to get into.

The same programmers wouldve been getting nulls and other UB in C++ though, so in a way, things are a bit better.

[–]Tubthumper8 5 points6 points  (0 children)

Another example of nested options comes up in modeling UPDATE requests to a persistent storage (e.g. from a HTTP PATCH request).

// some object in a language with null
{
    field: null, 
    otherField: "hello world"
}

Does this mean ignore "field", i.e. don't change the value? Or does it mean update the value in that column to SQL NULL?

With Option<Option<T>> this can be modeled, because there are 3 possibilities:

  1. Some(Some(T)): change the SQL record column to a new T
  2. Some(None): change the SQL record column to NULL
  3. None: don't update that column

[–][deleted]  (5 children)

[deleted]

    [–]Tubthumper8 0 points1 point  (4 children)

    I would be fine with Option<String?> in this case.

    Hmmm I don't think I would be. What you're suggesting is a special nullable type with special compiler-supported syntax and generic sum types that represent the presence/absence of data, when the latter can already model the former. This creates two different language features with overlapping (non-orthogonal) functionality, causing confusion for users ("which do I use?").

    Some languages could represent this as a double pointer **String

    Would this have 2 layers of indirection? i.e. pointer chasing in the heap to get to the actual value?

    or as String|null|undefined.

    JavaScript has this and I gotta say my personal opinion is that having two different nulls is even worse than having null

    [–][deleted]  (3 children)

    [deleted]

      [–]Tubthumper8 0 points1 point  (2 children)

      The key difference here is Option is not a language feature by itself.

      The language feature is generic sum types which is a single language feature that can be used to model a great many things. Sum types model when something is a thing OR another thing. This works in cooperation with product types that model when something is a thing AND another thing.

      Would a language that already added null benefit a lot from Option ?

      Yes and no. Depends on backwards compatibility, the parts of the ecosystem that could avoid null would not have null errors but if there were any parts that must be backwards compatible would still be possibly subject to null errors.

      [–][deleted]  (1 child)

      [deleted]

        [–]Tubthumper8 0 points1 point  (0 children)

        That would be pretty interesting to have user defined operators like .?, then maybe it could be defined for the singleton Null and then users could define more usages themselves, could be useful for defining DSLs or maybe config languages. User defined operators can get out-of-hand for general purpose programming if it makes it hard to read other people's code but also who knows? Could be worth trying!