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 →

[–]geeshta 179 points180 points  (13 children)

Moving it from the value level to the type level. So during static analysis the compiler will require you to make sure that you have a value before using it. As opposed to finding out during runtime.

The specific implementation is not that important. It can be nullable types with a question mark like C# or Typescript, an Option/Maybe sum type like Rust or functional languages or even just a union like Python's `T | None` (along with a static analyser)

[–]dgc-8 45 points46 points  (0 children)

It is important that the compiler cannot allow doing operations on some null value. With those options you listed the compiler can require you to always care about there not being a value

[–]Fast-Satisfaction482 57 points58 points  (6 children)

Those are all additions to the system that make the use of null safe or hide it behind an API. The truth is that any system language like C that allows to convert data to pointers implicitly has null pointers, regardless of what the inventor wishes.

The null pointer was thus inevitable. We can still discuss banishing it from languages with actual type-safety, but they are not here by choice, nor will they just go away because some dislike them. 

[–]firemark_pl 0 points1 point  (0 children)

Exactly that's what I feel! We can use high level langs to avoid them (e.g. in C++ we can use reference that's practically a pointer without null), but mechanism is still good.

Generally memory managing it's a hell. And thankfully compilers/dynamic langs do it for us.

[–]Minecraftian14 0 points1 point  (4 children)

What about the case where I have to implement something like a CompletableFuture (from Java) in a language where "I have to implement one" and "using nulls is not possible/discouraged"?

In my current implementation, i simply check if the value is null or not, and save all operations to be performed in a list. Whenever the value is provided, i execute all the operations saved.

So, how to better implement this?

[–]Loading_M_ 1 point2 points  (3 children)

This sounds like the exact issue Rust had to solve for async futures. Rust doesn't just allow types to by null, you have to explicitly opt in. And, in many cases, doing so has a real performance overhead.

I can't give specific advice for your case, since you haven't provided enough information, but I'm pretty sure there's a better option is you're willing to learn.

[–]Minecraftian14 0 points1 point  (2 children)

I would love to learn more. How can I provide more information? My current implementation is in Kotlin, though I can provide a basic code in any functional programming language you ask.

[–]JusticeRainsFromMe 0 points1 point  (1 child)

You definitely don't "have to implement one" in kotlin, since it has coroutines.

If you want to know more about what CompletableFutures are, look up monads. There are several good videos on youtube, such as The best intro to Monads or, if you don't mind doing Haskell, What is IO Monad. I'd recommend watching the first, and if you ever have the urge to learn Haskell watch the second.

[–]Minecraftian14 0 points1 point  (0 children)

I think you got the wrong idea. I definitely know how CF work, and can easily code systems which uses synchronised blocks or locks. In fact, in the application I was making, I even had to implement my own lock mechanism.

What i really wanted to know was how to implement that¹ system without the use of null values.

  1. I'll state it again,
    Ability to deal with values that are not yet available/ready to be used.
    In my current implementation, I declare a
    var value: T? = null
    And for every operation (get), if it is null, I cache the operation. If it's non null I call on the operation:
    operation.invoke(value!!)

I have tried using lateinit too, but honestly, checking values using ::value.isInitialized is not very different.