Few people mentioned how C# has good null handling but I see many are not clear what it does. If you have nullable enabled in your project you will get compile warnings if you use a nullable without checking it first. But you can also configure project to report those warnings as errors. Using the ! operator is like using unwrap in Rust. It is also the only implementation of such a feature that doesn't use generics. There is a generic Nullable in C# but it is only used for value types. Nullable support for reference types is just baked into the language.
It also has the null chaining the video describes as well, using the ?. syntax, plus a ?? operator, which is analogous to java's Optional.orElse() function, which is nice to have
I can feel that C# has everything that other langs are missing An *adequate* language must have: trycatch(Rust?), null safety(python?), event system(java?), async with threads(js and python?), native generic types(java?), LINQ(unique to c#), Unsafe, dynamic, a normal debugger instead console.log Do you want to connect to a db? SqlConnection Do you want to distinguish american and european dates? DateTime.Parse and CultureInfo Do you want JavaScript alike dynamic programming (useful for json)? dynamic and ExpandoObject Meanwhile it's open source and licensed under a MIT licence, available for linux, macos and mobile, definitely faster and safer than interpreted garbage and can be compared only with java and c++
@@Gameplayer55055If you like C#, that's fine. Dismissing everything that isn't exactly like it is pretty silly though. Many of the things you listed have alternatives in those languages, but work in a slightly different way. They are often intentionally different as a feature, not an omission.
@@jocketf3083 Some languages have really fatal flaws: 1. java has no unsigned values and structs - pain in the ass to make a socket server 2. lack of try catch in rust/golang and you call them mainstream after that. thats a step back down to ANSI C 3. js being single threaded 4. inconsistency in build systems (c++ and java SUCK) I've realised, it's acceptable to call bugs features in a dev community. But come on, sometimes it's too much (I like c++, c# and js for yearly innovations that add MUCH)
@@Gameplayer55055What exactly do you want from "try/catch" that Rust doesn't have? It's basically just checked errors. You definitely can have try-like semantics with the try operator (spelled as a question mark "?" postfix operator), and for the catch-site you use a match statement. With rust, you can't really unintentionally ignore an error. With something like go, however, you totally can. Go has no adequate error handling, but for Rust i think ADTs have the job handled and no explicit support beyond some syntax sugar is really necessary. Even so, if you think stack unwinding with unchecked errors is an essential feature of C#, you kinda can do this with `catch_unwind` and `panic_any`
You focused a lot on how Optionals work, but you totally missed the most important part: In a null-safe language, null is not a valid value for most types. This key property is the basis for actually being able to implement null safety at compile time. Optionals, where null (Nothing, Empty, ...) is allowed, are just needed in the (rare) case that we want something to be nullable. In a language that is not null-safe, say Java for example, you could assign null to an Optional. This of course defeats the whole purpose of using Optional, and your static-analysis tool of choice will probably scream at you, but there is nothing in the language that will prevent you from doing that.
What's wrong with an Optional value that is null? I could think of some cases in a database record where a distinction would be made between "None" and a value of "null".
@@musicalintuition In Rust, you can just wrap them again (in some cases, it will be still small, as it can use niches). Kotlin has sadly not that feature, as you cannot have a nullable of a nullable, so you will still need a wrapper type.
@@musicalintuition The whole idea of null-safe languages is that a) values of most types cannot be null (Nothing, Empty, ...) b) for those types where null is allowed (Optional, T?, Maybe, ...), the compiler forces you to handle the case of null so that no null-pointer exception or similar can happen at runtime. Even in a language that is not null-safe like Java, most programmers will implicitly assume when they see "Optional" that a) the optional itself cannot be null and that b) if the optional is not empty, then it contains a non-null value. (In Java, b) is enforced via a runtime check, while trying to violate a) at least gives you a warning in most IDEs.) Using an Optional that can be null might make sense from an implementation standpoint, but it will screw with that intuition and easily lead to bugs. If you need to distinguish various types of failures, it makes sense to either define your own type for that or use a type like Either that supports it out of the box.
IMO Kotlin does null safety best, as it recognizes that this should be a language feature and mandatory. It also has the best syntax for it because of it.
@@isometric_shahil I mentioned Kotlin since it was referenced in the video, but null safety in Dart 2 and up is really similar to how Kotlin does it. However there are still unsound null safety libraries since Dart 1 was not null safe. But basically with modern code I'd say it's really similar, since it's also a language feature with very similar syntax.
Do note that the way that memory allocation is done, just reading one byte off of the memory you're "allowed" to read will likely not cause a segv but it will be permitted because memory is allocated in size of pages, so protection features only extend to pages. But most software/operating systems know this so they add lots of guard pages in between to catch as many out of bound reads as possible to make debugging a lot easier.
I completely disagree with the premise of this entire video. Null dereferences are not an issue at all, and in fact I pray to the computer gods that when a crash dump rolls in that the error is a simple null dereference. They are typically extremely easy to debug with a simple stack trace. The worst bugs are the ones who corrupt memory catastrophically, or the bugs where things _appear_ to run correctly to everyone, but are actually extremely buggy scenarios such as a memory leak, where the program can fail in production environments at random points in the future for random customers. As far as I can tell, there are no good solutions to memory leaks and the like in modern languages.
0:43 "Writing to a memory address that belongs to another process" That's not really a thing. The addresses in your process are virtual, meaning the kernel maps them to an actual address of your hardware. A address can really only be mapped or unmapped (or protected, but let's ignore that for now) for a given process. Thus, the same address can be used in multiple processes to map to distinct locations in your memory, or even the same ones to save memory for identical segments (i.e. when using fork, or linking the same dynamic libraries). Your CPU contains a little chip called the MMU that translates these addresses on-the-fly as instructions execute. It's an incredibly cool concept.
Yeah, PHP having the null safe operator is amazing for keeping code neat. Gone are the days of mindless null checks. Combined with the null coalescing operator, it (basically a null safe ternary operator), coding has become much cleaner. Null safe operator is especially handy in Blade templates in Laravel.
A good reason to use Typescript rather than just pure Javascript. The Typescript compiler is nice as a sanity checker and with editor support it will highlight your nulls as you type.
@@NatoBoram Obvious troll is obvious. Here's a troll snack: TS type guarantees are zero once transpiled and the excess syntax doesn't make code any clearer.
I love demonstrating the Rust Option type. Because really it is just the enum { None, Some(T), } under the hood, just with a load of interface definitions and what amounts to documentation thrown on top
Clang does support NULL safety in C. Instead of `whatever *` you can write `whatever *_Nullable`, which allows this pointer to become NULL and `whatever *_Nonnull` which forbids that pointer to become NULL, whereas `whatever *` in fact means `whatever *_Null_unspecified`, which means it is unknown if that pointer can ever become NULL or not. Also you can write `_Pragma("clang assume_nonnull begin")` at the beginning of a section/file and then all pointers are considered `_Nonnulll` by default, unless otherwise specified; just don't forget to balance that with `_Pragma("clang assume_nonnull end")` at the end of the section/file. When you use these in your C code, the compiler will warn you when you pass a NULL value or a potential NULL value to a function that expects a non-NULL value. It will also warn you when you try to assign NULL (or maybe NULL) to a non-NULL pointer variable. And it will warn you when you try to use a Nullable value without checking it for NULL first if the operation would fail in case the pointer was NULL. And as always with clang, you can tell it to make these warnings errors instead, so your code won't even compile if it violates any of these rules. This feature exists since at least clang 9 (released 2019), current version is clang 17. I never write C code without that. I usually use macros for that, so I can define them to be nothing and the code will also compile on other compilers without that feature, e.g. GCC.
Another engineer here who uses both Go and Kotlin (with a general preference for Go). Yeah, optionals at the language level would be awesome in Go. It's annoying because when you generate the Go server/client code from a protobuf specification, it comes with "getX" functions that have rudimentary nil safety built in (ie. x.getY().getZ() will return nil if either x or x.y is nil). Sure, you can write that code yourself, but it's useful that protoc creates the boilerplate for you. But it't just not as good as the real thing, like in Kotlin.
I like it when people mention Ruby, it's my favorite programming language. Most of my actual work is in C#, but I use Ruby in class at my college, because I can code along with the teacher's C# and explanations in real time. Anyway, I've never interacted with this feature or had a need for it. Someday I probably will though.
Totally agree with you on Go... Easy to learn, love the absence of colored (async) functions, but the missing null safety makes my code crash a bit too often for my taste.
I don't follow go's development closely so idk if they have any plans to add to this, but I see people complain about this on reddit frequently. I wonder if people are vocal enough about it that the go team might address it somehow. Go did recently add generics, which the video claims is important to this feature, so maybe it will come soon.
Fully agree with you. This video started as a complaint about it, but I decided to spin it as a more general video about null safety. I'm going to do another video on looking at Null safety within Go and implementing it from scratch.
@@ttuurrttlle I'm an avid Go dev and I would kill for ?(return err) or !(panic). Something like: file, err := os.ReadFile() if err != nil { ..return err } To: file, ? := os.ReadFile() Would make me incredibly happy.
To the second requirement (3:35) - this is obviously a lie. Option is among the algebraic types widely used in functional programming paradigm. However, it in no way specifies its exact implementation, and especially does not require the language to be compiled. You can create algebraic types like Option in a dynamic language like JavaScript as well, and they will work exactly as we expect, because Option is a wrapper for a value or no value, not a magic feature offered by language compilers....
In C++, optional is more a way to not have to use the freestore in order to get a nullable type. In a sense, std::optional is less safe than a pointer, because a null pointer dereference SIGSEGVS immediately on most platforms, while using `operator*` on an unchecked std::optional will read invalid data. The problem lies in the concept of nullability it self.
@@Zex-4729 An optional is a container that stores zero or one elements. One way to implement it would be to use std::unique_ptr + some additional stuff to make fulfill the requirements of full value semantics. In case the optional has no value, the underlying pointer will be nullptr. An unchecked access `operator*` will cause a SIGSEGV and the program crashes. Using freestore is slow. Therefore, an std::optional stores its data inline, and uses a "valid" flag. If unset, the optional may contain uninitialized data. Reading it will not cause the process to crash, since the data is stored in a mapped page. Most likely the current stack frame. Uninitialized data may be more dangerous since there could be a crash later. Other consequences includes data leak, or unintentional control flow, that ultimately may lead to a root shell. Notice that this weakness assumes that you use `operator*` and not one of `value()` or `value_or`. The former will throw an exception if the value is not set, while value_or will return a default value. See also en.cppreference.com/w/cpp/utility/optional. The comparison assumes that you use an OS with memory protection, and also that it never maps low memory addresses. Linux will by default reserve the first 64 KiB, and will therefore always crash at nullptr address. An interesting side note is that if gcc can figure out that a pointer is always null at the time of dereference, it will generate an ud2 when targeting x86. Probably because it is faster to crash that way than trigger a page table walk.
This doesn't make a lot of sense. First, using pointers is not the same as using the freestore. One can always take the address of an automatic variable or pass in null for a simple "optional" type without using the freestore at all. Second, yes, optional is a value type and so has all the same issues of value types, including reading random junk if you didn't initialize it properly. But the stdlib implementers usually provide a way to enable runtime checks so that the program errors on unchecked access. This can even be enabled for optimized builds should the safety be deemed valuable, making them even safer than raw pointers. But yeah, there is a good point in there. It is something to be aware of and C++ tend to trust the programmer to not do the wrong thing. If you do something unsafe in a release build, you wont have any protection by default and weird things can happen. C++ doesn't do as much handholding as more recent languages.
@@oracleoftroy The worst language with regards to null must be JavaScript. It has two of them in the language. We are lucky that it is run in a safe environment, but native JS would be less safe than C, without ++. The problem with null-ability is that you must check for value validity everywhere if there is no way to have a syntax for fallback, or there is a no fallback. Such checks are dedious and makes the code ugly. And what happens if you fail to do so depends on the language and the runtime environment. You'd better of to never use nullable arguments, but instead, rely on the caller to provide an actual value, and so on, until you get to the point where you must do the check (Missing field in JSON, failed computation...).
@@Zex-4729 std::optional is stack based, so, contrary to dereferencing null pointer (and ending with SEGV), after calling *optional, memory to read bytes from is available. Only state of that memory is undefined, which ends wit undefined behavior.
The same question came from the reddit golang community. even though i really love golang, I really wish golang have the null safety feature as like kotlin at compile time.
Personally like dart and rust's variant of null safety. Rust has the better errror handling in general because exceptions are nonsense, but dart in theory eill eventually have a fully null safe ecosystem.
There are languages that use typestate that can equally prevent null dereferences. You're only allowed to dereference a pointer if the compiler can prove you've already tested it for not being null. By using an "optional" type, you push this from typestate into the type system. The advantage of typestate is it allows you to do the same sort of thing for array indexes, evaluating unassigned variables, and so on.
Good video, thanks for having stressed the importance of null safety. I am using rather heavily C# on some projects, and Dart on some others. The fact that they are both type-safe and null-safe helps me a lot in writing code that runs correctly right from the start, or after very little debugging. On the contrary, javascript is a real nightmare to me.
Speaking of null-safety in C# and Dart, there is a relevant difference between the two when it comes to dictionaries/maps. In C#, accessing a Dictionary with a non-existing key causes a run-time exception. No warning at edit/compile time. In Dart, on the contrary, accessing a Map with a non-existing key gives null. The return value is therefore considered nullable, and the null-safety mechanisms point out the potential error at development time. I like this.
I take issue with the JS part "question-mark operator", the optional chaining operator is specifically `?.` - both characters combined. It is not simply the inclusion of a prefixing question-mark as assuaded to in this video. You cannot optionally chain into bracket-notation by writing `obj?["doSomething"]()` for example, instead you need to fully write out the operator such as `obj?.["doSomething"]()`, another failure point would have been on the function call itself `obj?.doSomething?()` as opposed to `obj?.doSomething?.()` where the latter is valid syntax and would work as expected.
I feel this is an important distinction to make because we have ternaries which also use the question-mark character, and for anyone who only learned of this operator from this video trying to plug it into their code and wondering why it's not working.
0:47 is that really the most common? I highly doubt it honestly. Accessing memory past its lifetime is what I would think is the most common (such as use after free or dangling pointer).
No, those produce garbage data, but at least they're valid memory access as far as the OS and CPU are concerned, since freeing (small amounts of) memory rarely leads to memory pages getting relinquished. The most common invalid address is in fact zero, followed by small positive and sometimes negative integers that result from trying to access a member from a pointer to a structure.
C# also has null chaining. They call it null propogation though. Also a little more boilerplate reduction with null coalescense. Turns val = foo?.bar?.baz; return val == null ? false : val; into return foo?.bar?.baz ?? false; It also has the only non-generic take on optionals I know of. Types don't accept null, unless you flag them as nullable. Nullable types, given the above operators, are a lot like auto-unboxing optionals that need no generics. Plus the compiler knows when one can and can't be null quite well, and will tell you when it needs handling. If it gets it wrong anyways, you can force unwrap with a ! operator
C# doesn't really pick up null errors at compile time, I seem to find a way to find a way to cause a null value error every time I run 😂. I have been loving rust coming from c#, I didn't realise how easy low level language actually are.
@bity-bite I only taught myself and used c# for a couple of years so never knew that was a thing to be honest. I started using rust now and it's really useful to just be shown exactly where the error is when I compile. I really like c# but there is something really clean about rust code that I am enjoying.
Can you let me know where you found the claim that the most common runtime error in C/C++ is segfault? I’m wondering how that’s measured. In professional code segfaults are hardly ever a problem to be honest.
Great video! However how isn't Dart mentioned in a null safety video? They did a full rework of the language a few years ago and now is fully null safe with a lot of the features you explained. They have a large number of null aware operators.
> "Although not strictly necessary, I haven't found an [optional type] implementation that doesn't use them [generics]." Just thought I'd mention that, technically speaking, Zig's implementation uses the language's incredibly versatile `comptime` feature instead of using generics. In practice, this is largely the same as using generics, but it's cool anyway.
To clarify, Zig doesn't have generics as a language feature. Either comptime or some form of type erasure must be used when creating "generic" data types or functions.
Yeah but the null safety problem was solved years before Haskell, with object oriented systems. Nil can be a real object with real behaviour, that can be extended either at compile time or runtime like any other object. In such systems, the notion of an optional does not exist and is not useful. Once the object tries to interpret a message it can either succeed or fail (usually by redirecting to another message), and there is NEVER absence of value. Chaining operators are ok, as sugar coating for using Nil specifically to emulate absence of value which has a lot of use cases. Obviously dynamically typed or untyped systems use this concept (Smalltalk, Ruby, Lisp), but there is also the notable example of the Crystal language, in which values are defined using a union of types. Type unions make inferencing very easy to implement and also include the idea of optionals as a subset out of the box, without needing to deal with generics, a feature that is also excessive in true OOP. Even in an apocalypse scenario where no union types or optionals exist, one can still use the Null Object Pattern to create Null Objects for safeguarding execution from ambiguous datasets. Optionals are a mid solution for a problem that mid languages create.
@@evandrofilipe1526 I was calling C++ and Java mid, and I meant that `Maybe` was a mid solution in comparison for example to union types. Haskell is indeed a very impressive language which I like. I’m just not sure about optionals in a language where everything could be a valid type, or a valid function.
@evandrofilipe1526 @perigord6281 First of all I would like to say that my experience in Haskell is limited and there might be some reason why `Maybe` has a specialised importance in a type system where you can have sum types that I don't grasp. As far I understand in languages like Haskell or Idris, `Maybe` should be a subset of sum types, where all potentially absent values are a sum of the desired type (Just desired type) and `Nothing`. But in that case you could simply use sum types to define the value of `Maybe` which I guess is sort of used under the hood (?). Not only that but you could have more powerful optionals, by combining multiple types. I did not suggest that union types are better/worse than sum types which might more or less provide the solution I was talking about in the comment anyway. I am most likely misunderstanding something or am completely wrong, but in any case, I was referring instead to how in languages which only contain singular ex-nihilo building blocks like pure object oriented languages one could use Nothing or Nil on Void as actual values of the system with user-programmable behaviour. Defining absence of value then only becomes a natural extension of the system, and handling of this absence is decided by the programmer. Smalltalk-based dynamic languages for example use `nil` as a real object. It is possible to add new messages to `nil` so that it understands just as much as any other object. There is never absence of value in the conventional sense in ex-nihilo systems by definition (unless you are breaking the fourth wall of the virtual machine). There are even automations for handling unknown messages that redirect back to the mother object and dispatch `do not understand` style messages which throw. In crystal lang, you can protect yourself from the billion dollar mistake by having union types of multiple types not just `type` and `Nil`. But the important distinction is that instead of disallowing all interaction for this Nil, those languages allow you to modify it, such that under user-defined rules and circumstances, Nil could respond properly. I was talking about this behaviour potentially being superior to simply disabling all functionality on `Nothing`, god forbid there is any imperfection to haskell the language of the gods..
I've done my own null-pointer safety by creating a function that takes a pointer and an action (lambda or function). If that pointer wasn't null, then it is passed as the parameter to the passed action and the action is performed
3:06 This implementation is not only similar, it is identical. All od those macros are just metadata for the compiler to know what versions of rust they are implemented since, except `#[derive]` which implements some additional traits. Other than the lack of trait impls, there is absolutely nothing "naive" about your implementation, and if used with a non-nullable pointer-like type, then it's guaranteed to be one pointer wide. not just the std Option, but also your Optional. The compiler treats them exactly the aame.
Nobody will ever read that but Java's Optional is NOT a way to handle null type. It's only meant to more conveniently handle nullable value in functional programming like code. The correct way to handle null type in Java is through Type Annotations
Swift has a handful of neat things with optionals, you have optionals that you can force unwrap whichll throw errors (!), the chainable optional operator (?). And also stuff like coalescing that sets the variable to some default if the variable is nil (??). You can also declare a variable in an optional unwrap in an if-statement conditional so that the block won't execute if the optional is nil as a method of safe unwrapping
Why do you even need optional in C++? Most variables are stack allocated, or encapsulate all the heap allocations (vector, unordered_map), references are not nullable and smart pointers do the rest, right?
@@jakubl8271 (my reply disappeared, so I hope Im not repeating myself) I would check if the vector is empty before running any functions on it, but I do understand what you mean.
- *Me not understanding a thing because he's using rust and c++* - *Me who never tried the optional type before* : wth dude's yappin' on about?! - *Him finally mentioning javascript* - me: Yo I fully understand what ur saying bro i swear trust me bro i know what ur on about I swear - *Him mentioning optional chaining* - me: I- I'm- I'm hommme 😭
does lua do this too? if you referance an unnasigned variable, it returns a nil value. and you can do ternaries ( newvar = array[some_index] or "default value" ) with the said nil (sorry if i misunderstood, im reletively new to programing ^^;)
in general Lisp and Scheme nil|empty list/false respectively is not as much of a problem due to not having references or nil as a reference, so its less of a problem as a result. in general it also has more language support such as nil being falsity also its less likely to cause a problem. in clojure there's `some->` which can be used to wrap a null returning function on the offchance it gets sprung during a chain
Also in Clojure, most of the core functions handle nil the same as if you passed in an empty collection. Reading a value from a map, but the key is missing or the map is nil? The result is nil. Basically safe navigation as the default. You nearly never get a NullPointerException, even though nils are all over the place. And since nil is such a normal value, you'd always remember to write a test for how your function will handle nil in a graceful manner.
@@EskoLuontola like seeing `when-let` being a common enough macro in generic lisp projects; aswell as i think `car-safe`, `cdr-safe` (which are the main places for nil-related errors), shows that nil-punning is not as atrocious compared to C/C++/C#/Java and that.
In python, use: def func(self, *args, **kwargs) -> None: self._name = None for arg in args: if whateverCondition(arg) and self._name is None: self._name = arg if self._name is None: raise TypeError('Couldn't parse, lmao')
I love these features, they are also implemented in C#. Chaining them on a non-nullable object (such as primitive types like 'int') kinda makes them unreadable because you have to specify a default value for the non-nullable type with '??' operator. Which makes the code more cryptic and less readable for new comers. But its a welcome addition nonetheless
U can still have runtime null reference errors very easily compared to rust though, and it will compile with obvious errors that you see immediately at runtime.
@@maelstrom57 stable interface and API, optional chaining, ability to have nullable value types. You can check for nulls using pattern matching ex: if (a is null)
@@maelstrom57 That it exists at all. C# made the terrible mistake of references being nullable by default. They finally got around to trying to fix that.
@@agedvagabondall null references will have yellow warning squiggles before compiling the code Something must be wrong with your ide, even vs code has that
The pipe operator is the best thing ever and In all of the languages and frameworks I use I implemented it in one way or another if it didn't exist already. It makes writing and reading code so easy imo.
@@yash1152, you have to have operators be definable, or have AST macros. otherwise i don't think its possible without making a list of callbacks and passing it as some sort of mandatory input to the callbacks (i.e. an explicitly passed continuation)
Zig is capable of generics it just does not call it generics and does not have specific syntax for it, but with comptime and type arguments you are essentially writing generic code
@@filipvranesevic8722 well, this is both a correct and wrong statement, I agree it is 90 percent like generics, but comptime is happening all in the compile time which in most other languages (maybe except rust) there is some runtime type converter involved.
@@alirezanet in most of the compiled languages (like Rust, C++ and Go) generics work completely in compile time, for each use of generic function in the code, compiler generates separate function with concrete types and during runtime those functions are called as normal
As bad as it sounds - can't we just solve it on the level of exceptions? Perhaps any function that raises an exception would return a "null" if "env=prod" or something. You see?
I actually like swift’s language design for the most part, except for a few hiccups that causes some common scenarios to have silly implementations, its limited use case in Apple development (and by extension the stupidity of xcode), and its unfortunate compromise for execution speed.
If you're just going to provide a default value every single time a null value is detected, why make the thing nullable? This is easily handled for primitives, but for objects that implement an interface, this requires manually checking for nil, then handling each case accordingly. Usually, there is no default value provided and functions just return an error or panic. In my opinion, this is as it should be.
You can specify a different default value with each reference to the optional value, instead of one default value determined when the optional value is set.
@@nathangreene3 the insight is that partial functions need to be made total but you're a soydev who thinks random exceptions are a good idea so I don't think elaborating is going to do much for you
@@AndreiGeorgescu-j9p I didn't say to throw exceptions. I should have said I work with Go primarily so errors and exceptions are very different things to me.
MODERN ? dude .... T-T - It was in sensible languages from a long time ago... People just finally adopted it into languages they use primarily ... which are awful anyway, but it's nice they are bringing in features from languages designed by people with brain, and not just made to be as similar to existing and as usable by as dumb developers as possible...
bet he isn't using c# as it has a reputation to be basically Windows exclusive (except mono ofc). Which has changed since dotnet core came out thus maybe not knowing that its available for linux and mac.
@@Mempler Unlikely, I'd imagine anyone in the programming content creator sphere would know that it's now platform agnostic. If not, we probably shouldn't be taking advice from them (not saying this is the case). :D Core released in 2016, plenty of time for devs to realise.
I'm pretty sure the next big thing will be Union Types. If you are using a language that supports it like well (C++' implementation is horrible) like Typescript or F# its brilliant.
I have complicated feelings about optional values… when the language supports it, it is a pleasure to write code that uses it, but I find it that it becomes very easy to make everything optional which makes it more difficult to figure out issues based on something being null because anything could have been null. Whenever a throw together a script in JavaScript for a once and done situation I always start by saying that I will be disciplined about it and I always end up not doing so and then spent a long time chasing unexpected behavior. The only reason I have noticed this is because this never happens to me in Go. But I don’t think that I would want to write a one-and-done script in Go.
A language doesn't have to be compiled to have static checks. In JS, you can use JSDocs with tsc to typecheck your code before running it, while still not having a build step.
Not really, adding optionals doesn't make any previous code compile errors. The bigger issue is adoption. Optionals thrive when the ecosystem is designed with them in mind. When it isn't, then you have to do both optional checks and null checks.
C# added "nullable reference types" a few years ago, which added support for `?` for reference types. Previously there were no way to specify that something can be null. This was an optional addition, and only a compile-time thing (but can be checked for at runtime if you really need to, but few things do), but it caused/causes a lot of extra development time to transition - definitely worth it however.
Dude I absolutely loved this video, but I'd like to ask if you could please mention the name of the editor and theme you're using? It looks really pretty!
Worrying about null safety in a non-type safe language like JS seems like fitting an airbag and then saying this means there is no need to wear a seat belt now. I am sure there are cases where it's super useful but I thought the point of dynamic languages was speed of development trumps static analysis catching loads of run-time bugs?
Calling C++ std::optional a null dereference overcome is a joke. Calling nullopt_optional.value() ends with exception. Calling nullopt_optional-> or *nullopt_optional ends with ub. You forgot to check if pointer != nullptr, you gonna forget checking od optional_instance.has_value() as well.
Honestly, I hate null safety, for me it's really annoying and even in C/C++ segmentation faults aren't that hard to debug. Null safety in some languages like C# feels just really annoying to me. Probably a very uncommon opinion but that's just the way I learned to code.
My stages of learning Haskell This is great! WTF? Oh, I get it. Nope. I don't. My head hurts. Oh! I see! WTF? Let's start from scratch. Lambda calculus. Now it makes more sense. Start enjoying WTF moments This is great! ... Long windy road ahead of me
that ending was genius for druink people like me that love coding and just kind listen to things like this in aisutations they really shouldn't be. whilst on web based whatsapp waiting for people. you should do a video on if ADHD associated with being better or worse for people that code, or if thinking about that distracts from the underlying thing that builds towards enjoyment or proficiency in code
I do wan5 more functional stuff in python like a pipe operator. As it stands tho the entire ml economy system is stuck with python 3.8 so I would probably not see it.
@greyshopleskin2315 Well it's up to 3.11 at times. In general because it takes time to move these complicated c packages to the new versions. Especially with this new no Gill option that's gota make this whole thing a mess
could someone write a patch to fix a specific copy of python to be safe? each time a new version comes out they would have to reverse engineer and patch it just like doing a [k] patch for software piracy. probably why it is being differed is because the creators of python are lazy and dont feel like adding a safety check.
I thought that Java was really bad when it came to Null Pointer Exceptions, then here comes C# with its Null Reference Exception, and unlike Java (unless you have the debugger open) good f--ing luck finding out where it came from.
Few people mentioned how C# has good null handling but I see many are not clear what it does. If you have nullable enabled in your project you will get compile warnings if you use a nullable without checking it first. But you can also configure project to report those warnings as errors. Using the ! operator is like using unwrap in Rust.
It is also the only implementation of such a feature that doesn't use generics. There is a generic Nullable in C# but it is only used for value types. Nullable support for reference types is just baked into the language.
It also has the null chaining the video describes as well, using the ?. syntax, plus a ?? operator, which is analogous to java's Optional.orElse() function, which is nice to have
I can feel that C# has everything that other langs are missing
An *adequate* language must have: trycatch(Rust?), null safety(python?), event system(java?), async with threads(js and python?), native generic types(java?), LINQ(unique to c#), Unsafe, dynamic, a normal debugger instead console.log
Do you want to connect to a db? SqlConnection
Do you want to distinguish american and european dates? DateTime.Parse and CultureInfo
Do you want JavaScript alike dynamic programming (useful for json)? dynamic and ExpandoObject
Meanwhile it's open source and licensed under a MIT licence, available for linux, macos and mobile, definitely faster and safer than interpreted garbage and can be compared only with java and c++
@@Gameplayer55055If you like C#, that's fine. Dismissing everything that isn't exactly like it is pretty silly though. Many of the things you listed have alternatives in those languages, but work in a slightly different way. They are often intentionally different as a feature, not an omission.
@@jocketf3083
Some languages have really fatal flaws:
1. java has no unsigned values and structs - pain in the ass to make a socket server
2. lack of try catch in rust/golang and you call them mainstream after that. thats a step back down to ANSI C
3. js being single threaded
4. inconsistency in build systems (c++ and java SUCK)
I've realised, it's acceptable to call bugs features in a dev community. But come on, sometimes it's too much (I like c++, c# and js for yearly innovations that add MUCH)
@@Gameplayer55055What exactly do you want from "try/catch" that Rust doesn't have? It's basically just checked errors. You definitely can have try-like semantics with the try operator (spelled as a question mark "?" postfix operator), and for the catch-site you use a match statement. With rust, you can't really unintentionally ignore an error. With something like go, however, you totally can. Go has no adequate error handling, but for Rust i think ADTs have the job handled and no explicit support beyond some syntax sugar is really necessary. Even so, if you think stack unwinding with unchecked errors is an essential feature of C#, you kinda can do this with `catch_unwind` and `panic_any`
You focused a lot on how Optionals work, but you totally missed the most important part: In a null-safe language, null is not a valid value for most types.
This key property is the basis for actually being able to implement null safety at compile time.
Optionals, where null (Nothing, Empty, ...) is allowed, are just needed in the (rare) case that we want something to be nullable.
In a language that is not null-safe, say Java for example, you could assign null to an Optional.
This of course defeats the whole purpose of using Optional, and your static-analysis tool of choice will probably scream at you, but there is nothing in the language that will prevent you from doing that.
What's wrong with an Optional value that is null? I could think of some cases in a database record where a distinction would be made between "None" and a value of "null".
@@musicalintuition In Rust, you can just wrap them again (in some cases, it will be still small, as it can use niches). Kotlin has sadly not that feature, as you cannot have a nullable of a nullable, so you will still need a wrapper type.
@@musicalintuition The whole idea of null-safe languages is that
a) values of most types cannot be null (Nothing, Empty, ...)
b) for those types where null is allowed (Optional, T?, Maybe, ...), the compiler forces you to handle the case of null so that no null-pointer exception or similar can happen at runtime.
Even in a language that is not null-safe like Java, most programmers will implicitly assume when they see "Optional" that a) the optional itself cannot be null and that b) if the optional is not empty, then it contains a non-null value. (In Java, b) is enforced via a runtime check, while trying to violate a) at least gives you a warning in most IDEs.)
Using an Optional that can be null might make sense from an implementation standpoint, but it will screw with that intuition and easily lead to bugs.
If you need to distinguish various types of failures, it makes sense to either define your own type for that or use a type like Either that supports it out of the box.
@@cleavesolais Thanks for your answer.
Nothing will save you from stupid.
IMO Kotlin does null safety best, as it recognizes that this should be a language feature and mandatory. It also has the best syntax for it because of it.
How would you compare Kotlin's null safety with Dart's null safety?
@@isometric_shahil I mentioned Kotlin since it was referenced in the video, but null safety in Dart 2 and up is really similar to how Kotlin does it. However there are still unsound null safety libraries since Dart 1 was not null safe. But basically with modern code I'd say it's really similar, since it's also a language feature with very similar syntax.
Best would be Rust, not just when it comes to null safety but all sum types that are enum derived
no, swift does it better
It's actually English that does it better than any of these.
Do note that the way that memory allocation is done, just reading one byte off of the memory you're "allowed" to read will likely not cause a segv but it will be permitted because memory is allocated in size of pages, so protection features only extend to pages. But most software/operating systems know this so they add lots of guard pages in between to catch as many out of bound reads as possible to make debugging a lot easier.
Thanks, I could have sworn that I've read slightly outside of C++ arrays before, but without having a seg fault
I completely disagree with the premise of this entire video. Null dereferences are not an issue at all, and in fact I pray to the computer gods that when a crash dump rolls in that the error is a simple null dereference. They are typically extremely easy to debug with a simple stack trace. The worst bugs are the ones who corrupt memory catastrophically, or the bugs where things _appear_ to run correctly to everyone, but are actually extremely buggy scenarios such as a memory leak, where the program can fail in production environments at random points in the future for random customers. As far as I can tell, there are no good solutions to memory leaks and the like in modern languages.
0:43 "Writing to a memory address that belongs to another process" That's not really a thing. The addresses in your process are virtual, meaning the kernel maps them to an actual address of your hardware. A address can really only be mapped or unmapped (or protected, but let's ignore that for now) for a given process. Thus, the same address can be used in multiple processes to map to distinct locations in your memory, or even the same ones to save memory for identical segments (i.e. when using fork, or linking the same dynamic libraries). Your CPU contains a little chip called the MMU that translates these addresses on-the-fly as instructions execute. It's an incredibly cool concept.
Safe navigation has been in in PHP as well for several years now. It's worth mentioming, given that it's far more widely used than Ruby, for instance.
Yeah, PHP having the null safe operator is amazing for keeping code neat. Gone are the days of mindless null checks. Combined with the null coalescing operator, it (basically a null safe ternary operator), coding has become much cleaner. Null safe operator is especially handy in Blade templates in Laravel.
A good reason to use Typescript rather than just pure Javascript. The Typescript compiler is nice as a sanity checker and with editor support it will highlight your nulls as you type.
There is no good reason to use TS instead of JS.
@@etherweb6796VSCode is written in Typescript
@@etherweb6796Thanks for telling us that you're unemployable but that wasn't the point we were discussing
@@NatoBoram Obvious troll is obvious. Here's a troll snack: TS type guarantees are zero once transpiled and the excess syntax doesn't make code any clearer.
@@etherweb6796 gl with your 'career'
I love demonstrating the Rust Option type. Because really it is just the enum { None, Some(T), } under the hood, just with a load of interface definitions and what amounts to documentation thrown on top
Clang does support NULL safety in C. Instead of `whatever *` you can write `whatever *_Nullable`, which allows this pointer to become NULL and `whatever *_Nonnull` which forbids that pointer to become NULL, whereas `whatever *` in fact means `whatever *_Null_unspecified`, which means it is unknown if that pointer can ever become NULL or not. Also you can write `_Pragma("clang assume_nonnull begin")` at the beginning of a section/file and then all pointers are considered `_Nonnulll` by default, unless otherwise specified; just don't forget to balance that with `_Pragma("clang assume_nonnull end")` at the end of the section/file. When you use these in your C code, the compiler will warn you when you pass a NULL value or a potential NULL value to a function that expects a non-NULL value. It will also warn you when you try to assign NULL (or maybe NULL) to a non-NULL pointer variable. And it will warn you when you try to use a Nullable value without checking it for NULL first if the operation would fail in case the pointer was NULL. And as always with clang, you can tell it to make these warnings errors instead, so your code won't even compile if it violates any of these rules. This feature exists since at least clang 9 (released 2019), current version is clang 17. I never write C code without that. I usually use macros for that, so I can define them to be nothing and the code will also compile on other compilers without that feature, e.g. GCC.
Dart has good null safety
Nice vid thanks. While typing is optional in Python, it has had ‘typing.Optional’ for a while and there was ‘Union[SomeType, None]’ before that.
Was going to point this out. If you use typing (Python's static type checker) null (or None) safety is pretty good.
Starting Python 3.10, you can also write it as "SomeType | None".
Another engineer here who uses both Go and Kotlin (with a general preference for Go).
Yeah, optionals at the language level would be awesome in Go.
It's annoying because when you generate the Go server/client code from a protobuf specification, it comes with "getX" functions that have rudimentary nil safety built in (ie. x.getY().getZ() will return nil if either x or x.y is nil). Sure, you can write that code yourself, but it's useful that protoc creates the boilerplate for you.
But it't just not as good as the real thing, like in Kotlin.
C# has the most matured null handling with plenty of operators, extension static methods.
in C# we have ? and ! and the compiler shows you whenever you create something null.
Maybe I'll get to see it in my career one day...
there is also the null coalescing operator ?? and its assignment version ??=
If only libraries could actually take advantage of it
But it doesn't catch errors until runtime. Even the really obvious ones where u look at your code and go 'duh, I'm an idiot' 😅
@@agedvagabond when Null Reference Types is activated, the compiler checks nullability for you.
I like it when people mention Ruby, it's my favorite programming language. Most of my actual work is in C#, but I use Ruby in class at my college, because I can code along with the teacher's C# and explanations in real time.
Anyway, I've never interacted with this feature or had a need for it. Someday I probably will though.
Totally agree with you on Go... Easy to learn, love the absence of colored (async) functions, but the missing null safety makes my code crash a bit too often for my taste.
I don't follow go's development closely so idk if they have any plans to add to this, but I see people complain about this on reddit frequently. I wonder if people are vocal enough about it that the go team might address it somehow.
Go did recently add generics, which the video claims is important to this feature, so maybe it will come soon.
And the lack of map, fold, reduce, filter and find.
Fully agree with you. This video started as a complaint about it, but I decided to spin it as a more general video about null safety.
I'm going to do another video on looking at Null safety within Go and implementing it from scratch.
@@ttuurrttlleIt won't happen
@@ttuurrttlle I'm an avid Go dev and I would kill for ?(return err) or !(panic).
Something like:
file, err := os.ReadFile()
if err != nil {
..return err
}
To:
file, ? := os.ReadFile()
Would make me incredibly happy.
PHP also added null safety in recent years
To the second requirement (3:35) - this is obviously a lie. Option is among the algebraic types widely used in functional programming paradigm. However, it in no way specifies its exact implementation, and especially does not require the language to be compiled.
You can create algebraic types like Option in a dynamic language like JavaScript as well, and they will work exactly as we expect, because Option is a wrapper for a value or no value, not a magic feature offered by language compilers....
Curious you didnt mention PHP, it had a strict type system, support null safety, but dont support generic
What about Dart languages Null Safety ?
In C++, optional is more a way to not have to use the freestore in order to get a nullable type. In a sense, std::optional is less safe than a pointer, because a null pointer dereference SIGSEGVS immediately on most platforms, while using `operator*` on an unchecked std::optional will read invalid data. The problem lies in the concept of nullability it self.
@@Zex-4729 An optional is a container that stores zero or one elements. One way to implement it would be to use std::unique_ptr + some additional stuff to make fulfill the requirements of full value semantics. In case the optional has no value, the underlying pointer will be nullptr. An unchecked access `operator*` will cause a SIGSEGV and the program crashes.
Using freestore is slow. Therefore, an std::optional stores its data inline, and uses a "valid" flag. If unset, the optional may contain uninitialized data. Reading it will not cause the process to crash, since the data is stored in a mapped page. Most likely the current stack frame. Uninitialized data may be more dangerous since there could be a crash later. Other consequences includes data leak, or unintentional control flow, that ultimately may lead to a root shell. Notice that this weakness assumes that you use `operator*` and not one of `value()` or `value_or`. The former will throw an exception if the value is not set, while value_or will return a default value. See also en.cppreference.com/w/cpp/utility/optional.
The comparison assumes that you use an OS with memory protection, and also that it never maps low memory addresses. Linux will by default reserve the first 64 KiB, and will therefore always crash at nullptr address. An interesting side note is that if gcc can figure out that a pointer is always null at the time of dereference, it will generate an ud2 when targeting x86. Probably because it is faster to crash that way than trigger a page table walk.
This doesn't make a lot of sense.
First, using pointers is not the same as using the freestore. One can always take the address of an automatic variable or pass in null for a simple "optional" type without using the freestore at all.
Second, yes, optional is a value type and so has all the same issues of value types, including reading random junk if you didn't initialize it properly. But the stdlib implementers usually provide a way to enable runtime checks so that the program errors on unchecked access. This can even be enabled for optimized builds should the safety be deemed valuable, making them even safer than raw pointers.
But yeah, there is a good point in there. It is something to be aware of and C++ tend to trust the programmer to not do the wrong thing. If you do something unsafe in a release build, you wont have any protection by default and weird things can happen. C++ doesn't do as much handholding as more recent languages.
@@oracleoftroy The worst language with regards to null must be JavaScript. It has two of them in the language. We are lucky that it is run in a safe environment, but native JS would be less safe than C, without ++. The problem with null-ability is that you must check for value validity everywhere if there is no way to have a syntax for fallback, or there is a no fallback. Such checks are dedious and makes the code ugly. And what happens if you fail to do so depends on the language and the runtime environment. You'd better of to never use nullable arguments, but instead, rely on the caller to provide an actual value, and so on, until you get to the point where you must do the check (Missing field in JSON, failed computation...).
@@Zex-4729 std::optional is stack based, so, contrary to dereferencing null pointer (and ending with SEGV), after calling *optional, memory to read bytes from is available. Only state of that memory is undefined, which ends wit undefined behavior.
The same question came from the reddit golang community. even though i really love golang, I really wish golang have the null safety feature as like kotlin at compile time.
PHP also has null-safe operators (optional chaining as described in this video) since 8.0
The good news is that web hosting providers should be offering PHP v8.0 by default sometime around 2050.
@@DemPilafian I've yet to see one where you can't set it yourself to the newest, and where you can't set new customer account defaults as a reseller
Personally like dart and rust's variant of null safety. Rust has the better errror handling in general because exceptions are nonsense, but dart in theory eill eventually have a fully null safe ecosystem.
If you could go over Dart null safety, that would be neet. I like it a lot, it implements everything i love in null safety
Weird to not mention Dart
There are languages that use typestate that can equally prevent null dereferences. You're only allowed to dereference a pointer if the compiler can prove you've already tested it for not being null. By using an "optional" type, you push this from typestate into the type system. The advantage of typestate is it allows you to do the same sort of thing for array indexes, evaluating unassigned variables, and so on.
Good video, thanks for having stressed the importance of null safety. I am using rather heavily C# on some projects, and Dart on some others. The fact that they are both type-safe and null-safe helps me a lot in writing code that runs correctly right from the start, or after very little debugging. On the contrary, javascript is a real nightmare to me.
i dont like him.
Speaking of null-safety in C# and Dart, there is a relevant difference between the two when it comes to dictionaries/maps. In C#, accessing a Dictionary with a non-existing key causes a run-time exception. No warning at edit/compile time. In Dart, on the contrary, accessing a Map with a non-existing key gives null. The return value is therefore considered nullable, and the null-safety mechanisms point out the potential error at development time. I like this.
I take issue with the JS part "question-mark operator", the optional chaining operator is specifically `?.` - both characters combined. It is not simply the inclusion of a prefixing question-mark as assuaded to in this video. You cannot optionally chain into bracket-notation by writing `obj?["doSomething"]()` for example, instead you need to fully write out the operator such as `obj?.["doSomething"]()`, another failure point would have been on the function call itself `obj?.doSomething?()` as opposed to `obj?.doSomething?.()` where the latter is valid syntax and would work as expected.
I feel this is an important distinction to make because we have ternaries which also use the question-mark character, and for anyone who only learned of this operator from this video trying to plug it into their code and wondering why it's not working.
You just taught me that optional chaining into bracket notation and functions is actually possible, thanks.
0:47 is that really the most common? I highly doubt it honestly. Accessing memory past its lifetime is what I would think is the most common (such as use after free or dangling pointer).
No, those produce garbage data, but at least they're valid memory access as far as the OS and CPU are concerned, since freeing (small amounts of) memory rarely leads to memory pages getting relinquished. The most common invalid address is in fact zero, followed by small positive and sometimes negative integers that result from trying to access a member from a pointer to a structure.
Another great feature of these languages is pattern matching and switch expressions
C# also has null chaining. They call it null propogation though. Also a little more boilerplate reduction with null coalescense. Turns
val = foo?.bar?.baz; return val == null ? false : val;
into
return foo?.bar?.baz ?? false;
It also has the only non-generic take on optionals I know of. Types don't accept null, unless you flag them as nullable. Nullable types, given the above operators, are a lot like auto-unboxing optionals that need no generics. Plus the compiler knows when one can and can't be null quite well, and will tell you when it needs handling. If it gets it wrong anyways, you can force unwrap with a ! operator
I have no idea how you managed to not mention C# in that video.
C# doesn't really pick up null errors at compile time, I seem to find a way to find a way to cause a null value error every time I run 😂. I have been loving rust coming from c#, I didn't realise how easy low level language actually are.
@@agedvagabond You don't seem to have NRT enabled in your csproj
@bity-bite I only taught myself and used c# for a couple of years so never knew that was a thing to be honest. I started using rust now and it's really useful to just be shown exactly where the error is when I compile. I really like c# but there is something really clean about rust code that I am enjoying.
@@agedvagabond 👍
you can use a linter in a JIT language like python too, just saying, but I also love compilers
You are correct!
Can you let me know where you found the claim that the most common runtime error in C/C++ is segfault? I’m wondering how that’s measured. In professional code segfaults are hardly ever a problem to be honest.
Great video! However how isn't Dart mentioned in a null safety video? They did a full rework of the language a few years ago and now is fully null safe with a lot of the features you explained. They have a large number of null aware operators.
> "Although not strictly necessary, I haven't found an [optional type] implementation that doesn't use them [generics]."
Just thought I'd mention that, technically speaking, Zig's implementation uses the language's incredibly versatile `comptime` feature instead of using generics. In practice, this is largely the same as using generics, but it's cool anyway.
That's very cool. I need to spend some more time with Zig! I'll have to do a video on it!
To clarify, Zig doesn't have generics as a language feature. Either comptime or some form of type erasure must be used when creating "generic" data types or functions.
JS also has undefined, and NaN also isn't doing it any favours.
And "empty item", but NaN exists in every language.
Yeah but the null safety problem was solved years before Haskell, with object oriented systems. Nil can be a real object with real behaviour, that can be extended either at compile time or runtime like any other object. In such systems, the notion of an optional does not exist and is not useful. Once the object tries to interpret a message it can either succeed or fail (usually by redirecting to another message), and there is NEVER absence of value. Chaining operators are ok, as sugar coating for using Nil specifically to emulate absence of value which has a lot of use cases.
Obviously dynamically typed or untyped systems use this concept (Smalltalk, Ruby, Lisp), but there is also the notable example of the Crystal language, in which values are defined using a union of types. Type unions make inferencing very easy to implement and also include the idea of optionals as a subset out of the box, without needing to deal with generics, a feature that is also excessive in true OOP. Even in an apocalypse scenario where no union types or optionals exist, one can still use the Null Object Pattern to create Null Objects for safeguarding execution from ambiguous datasets.
Optionals are a mid solution for a problem that mid languages create.
There's no way you just called haskell mid, at least not in that respect. We use Maybe when it suits the model that we're programming
@@evandrofilipe1526 I was calling C++ and Java mid, and I meant that `Maybe` was a mid solution in comparison for example to union types. Haskell is indeed a very impressive language which I like. I’m just not sure about optionals in a language where everything could be a valid type, or a valid function.
@@oblivious99 Can you elaborate on how maybe is a mid option? edit: and why union types are better?
@@oblivious99 what value would union types add to Haskell that Sum types don't already solve?
@evandrofilipe1526 @perigord6281 First of all I would like to say that my experience in Haskell is limited and there might be some reason why `Maybe` has a specialised importance in a type system where you can have sum types that I don't grasp. As far I understand in languages like Haskell or Idris, `Maybe` should be a subset of sum types, where all potentially absent values are a sum of the desired type (Just desired type) and `Nothing`. But in that case you could simply use sum types to define the value of `Maybe` which I guess is sort of used under the hood (?). Not only that but you could have more powerful optionals, by combining multiple types. I did not suggest that union types are better/worse than sum types which might more or less provide the solution I was talking about in the comment anyway. I am most likely misunderstanding something or am completely wrong, but in any case,
I was referring instead to how in languages which only contain singular ex-nihilo building blocks like pure object oriented languages one could use Nothing or Nil on Void as actual values of the system with user-programmable behaviour. Defining absence of value then only becomes a natural extension of the system, and handling of this absence is decided by the programmer. Smalltalk-based dynamic languages for example use `nil` as a real object. It is possible to add new messages to `nil` so that it understands just as much as any other object. There is never absence of value in the conventional sense in ex-nihilo systems by definition (unless you are breaking the fourth wall of the virtual machine). There are even automations for handling unknown messages that redirect back to the mother object and dispatch `do not understand` style messages which throw. In crystal lang, you can protect yourself from the billion dollar mistake by having union types of multiple types not just `type` and `Nil`.
But the important distinction is that instead of disallowing all interaction for this Nil, those languages allow you to modify it, such that under user-defined rules and circumstances, Nil could respond properly. I was talking about this behaviour potentially being superior to simply disabling all functionality on `Nothing`, god forbid there is any imperfection to haskell the language of the gods..
I've done my own null-pointer safety by creating a function that takes a pointer and an action (lambda or function). If that pointer wasn't null, then it is passed as the parameter to the passed action and the action is performed
I didn't know JS had this "?." thing, it makes it so much easier to verify properties are met
3:06 This implementation is not only similar, it is identical. All od those macros are just metadata for the compiler to know what versions of rust they are implemented since, except `#[derive]` which implements some additional traits. Other than the lack of trait impls, there is absolutely nothing "naive" about your implementation, and if used with a non-nullable pointer-like type, then it's guaranteed to be one pointer wide. not just the std Option, but also your Optional. The compiler treats them exactly the aame.
meanwhile Javascript: has 1000 ways to represent null or falsy values
Nobody will ever read that but Java's Optional is NOT a way to handle null type. It's only meant to more conveniently handle nullable value in functional programming like code.
The correct way to handle null type in Java is through Type Annotations
Swift has a handful of neat things with optionals, you have optionals that you can force unwrap whichll throw errors (!), the chainable optional operator (?). And also stuff like coalescing that sets the variable to some default if the variable is nil (??). You can also declare a variable in an optional unwrap in an if-statement conditional so that the block won't execute if the optional is nil as a method of safe unwrapping
The defer keyword is really awesome as well
Dart is like this too, it's very nice imho
Why do you even need optional in C++?
Most variables are stack allocated, or encapsulate all the heap allocations (vector, unordered_map), references are not nullable and smart pointers do the rest, right?
"Give me a copy of the biggest value from given vector". What are your proposals to handle empty vector case?
@@jakubl8271 you can just check wheter the vector is or isnt empty before running such function, but I understand what you mean
@@jakubl8271 (my reply disappeared, so I hope Im not repeating myself) I would check if the vector is empty before running any functions on it, but I do understand what you mean.
- *Me not understanding a thing because he's using rust and c++*
- *Me who never tried the optional type before* : wth dude's yappin' on about?!
- *Him finally mentioning javascript* - me: Yo I fully understand what ur saying bro i swear trust me bro i know what ur on about I swear
- *Him mentioning optional chaining* - me: I- I'm- I'm hommme 😭
your themeing looks awesome man an you please make a video or tell me what theme, colorscheme, desktop environment you use .
does lua do this too?
if you referance an unnasigned variable, it returns a nil value.
and you can do ternaries ( newvar = array[some_index] or "default value" ) with the said nil
(sorry if i misunderstood, im reletively new to programing ^^;)
Why do languages have the syntax "foo?.bar()", "foo?bar()" seems so much nicer to me
in general Lisp and Scheme nil|empty list/false respectively is not as much of a problem due to not having references or nil as a reference, so its less of a problem as a result. in general it also has more language support such as nil being falsity also its less likely to cause a problem. in clojure there's `some->` which can be used to wrap a null returning function on the offchance it gets sprung during a chain
Also in Clojure, most of the core functions handle nil the same as if you passed in an empty collection. Reading a value from a map, but the key is missing or the map is nil? The result is nil. Basically safe navigation as the default. You nearly never get a NullPointerException, even though nils are all over the place. And since nil is such a normal value, you'd always remember to write a test for how your function will handle nil in a graceful manner.
@@EskoLuontola like seeing `when-let` being a common enough macro in generic lisp projects; aswell as i think `car-safe`, `cdr-safe` (which are the main places for nil-related errors), shows that nil-punning is not as atrocious compared to C/C++/C#/Java and that.
Dart too has null-safety. It is actually the first language to add sound null-safety. And it was before Swift and Kotlin.
In python, use:
def func(self, *args, **kwargs) -> None:
self._name = None
for arg in args:
if whateverCondition(arg) and self._name is None:
self._name = arg
if self._name is None:
raise TypeError('Couldn't parse, lmao')
I love these features, they are also implemented in C#. Chaining them on a non-nullable object (such as primitive types like 'int') kinda makes them unreadable because you have to specify a default value for the non-nullable type with '??' operator. Which makes the code more cryptic and less readable for new comers. But its a welcome addition nonetheless
I'm surprised you didn't mention C#, which has a very nice null checking system
U can still have runtime null reference errors very easily compared to rust though, and it will compile with obvious errors that you see immediately at runtime.
What's so nice about it?
@@maelstrom57 stable interface and API, optional chaining, ability to have nullable value types. You can check for nulls using pattern matching ex: if (a is null)
@@maelstrom57 That it exists at all. C# made the terrible mistake of references being nullable by default. They finally got around to trying to fix that.
@@agedvagabondall null references will have yellow warning squiggles before compiling the code
Something must be wrong with your ide, even vs code has that
Gosh, I love null safety in Flutter
The pipe operator is the best thing ever and In all of the languages and frameworks I use I implemented it in one way or another if it didn't exist already. It makes writing and reading code so easy imo.
how do u implement it in langs where it doesnt exist?
I'm also very curious! I loved it in F# and tried implementing it in other langs without total success, so it would be cool to see other solutions!
@@yash1152, you have to have operators be definable, or have AST macros.
otherwise i don't think its possible without making a list of callbacks and passing it as some sort of mandatory input to the callbacks (i.e. an explicitly passed continuation)
Thats why Elixir is da best
Mentions Java but not C# 😢
C#'s null handling is so good as well...
Forgive me 😭 I had too many C's
@@martinrages It's not as good as Kotlin's, but definitely a huge improvement compared to before it was introduced.
@@dreamsofcode Alright that's okay 😃
no idea how optionals move errors from runtime to compile time? im pretty sure like 90% of my rust panics have been unwrapping a none
Dart and c# are not mentioned lol
Both are great languages with null safety!
I can feel c# was purposely ignored. It has optional types, syntax sugar using? and JavaScript like null coalesce operators .? ?? and ??=
FINALLY something comprehensive on the topic! Thanks!
Could you make a "Neovim for PHP and Laravel" video?
About Generics 3:23 Zig has this feature without generics ^^
Zig is capable of generics it just does not call it generics and does not have specific syntax for it, but with comptime and type arguments you are essentially writing generic code
@@filipvranesevic8722 well, this is both a correct and wrong statement,
I agree it is 90 percent like generics, but comptime is happening all in the compile time which in most other languages (maybe except rust) there is some runtime type converter involved.
@@alirezanet in most of the compiled languages (like Rust, C++ and Go) generics work completely in compile time, for each use of generic function in the code, compiler generates separate function with concrete types and during runtime those functions are called as normal
hmm interesting 👍I didn't know that
As bad as it sounds - can't we just solve it on the level of exceptions? Perhaps any function that raises an exception would return a "null" if "env=prod" or something. You see?
I actually like swift’s language design for the most part, except for a few hiccups that causes some common scenarios to have silly implementations, its limited use case in Apple development (and by extension the stupidity of xcode), and its unfortunate compromise for execution speed.
Watching this as a developer using primarily Go and Python. Feeling a little left out, LOL
My main langs the last 12 years have been [python/js/ts/C#] so it amazes me this is still a problem in 2024.
I like to wrap all my references to null in another reference.
00:50 don't forget dereferencing ((void*)-1), that is returned by failed mmap
this feels like a alternate universe fireship
Rust wins the null safety and error handling really simple and robust
If you're just going to provide a default value every single time a null value is detected, why make the thing nullable? This is easily handled for primitives, but for objects that implement an interface, this requires manually checking for nil, then handling each case accordingly. Usually, there is no default value provided and functions just return an error or panic. In my opinion, this is as it should be.
Shouldn't be returning errors
@@AndreiGeorgescu-j9p Solid insight right there.
You can specify a different default value with each reference to the optional value, instead of one default value determined when the optional value is set.
@@nathangreene3 the insight is that partial functions need to be made total but you're a soydev who thinks random exceptions are a good idea so I don't think elaborating is going to do much for you
@@AndreiGeorgescu-j9p I didn't say to throw exceptions. I should have said I work with Go primarily so errors and exceptions are very different things to me.
MODERN ? dude .... T-T - It was in sensible languages from a long time ago... People just finally adopted it into languages they use primarily ... which are awful anyway, but it's nice they are bringing in features from languages designed by people with brain, and not just made to be as similar to existing and as usable by as dumb developers as possible...
and it's not just Haskell, it's any ML, etc...
C# not getting any light in this video is a crime in itself.
bet he isn't using c# as it has a reputation to be basically Windows exclusive (except mono ofc). Which has changed since dotnet core came out thus maybe not knowing that its available for linux and mac.
Dogwater
maybe he just hate microsoft lol
same reason for why typescript isn't shown here
@@Mempler Unlikely, I'd imagine anyone in the programming content creator sphere would know that it's now platform agnostic. If not, we probably shouldn't be taking advice from them (not saying this is the case). :D
Core released in 2016, plenty of time for devs to realise.
C# is just a clone of java :P,
It's less popular, so no, it's not a crime.
I'm pretty sure the next big thing will be Union Types. If you are using a language that supports it like well (C++' implementation is horrible) like Typescript or F# its brilliant.
I have complicated feelings about optional values… when the language supports it, it is a pleasure to write code that uses it, but I find it that it becomes very easy to make everything optional which makes it more difficult to figure out issues based on something being null because anything could have been null.
Whenever a throw together a script in JavaScript for a once and done situation I always start by saying that I will be disciplined about it and I always end up not doing so and then spent a long time chasing unexpected behavior.
The only reason I have noticed this is because this never happens to me in Go. But I don’t think that I would want to write a one-and-done script in Go.
A language doesn't have to be compiled to have static checks. In JS, you can use JSDocs with tsc to typecheck your code before running it, while still not having a build step.
Wouldn't adding this feature to existing languages give tons of compile errors in existing code? Maybe that's why it wasn't added in python.
Not really, adding optionals doesn't make any previous code compile errors. The bigger issue is adoption. Optionals thrive when the ecosystem is designed with them in mind.
When it isn't, then you have to do both optional checks and null checks.
C# added "nullable reference types" a few years ago, which added support for `?` for reference types. Previously there were no way to specify that something can be null.
This was an optional addition, and only a compile-time thing (but can be checked for at runtime if you really need to, but few things do), but it caused/causes a lot of extra development time to transition - definitely worth it however.
In C we just put on our sun glasses, spit on our customer and yell "Fuck safety".
Is the editor neovim? Setup looks great. What plugins are used?
Dude I absolutely loved this video, but I'd like to ask if you could please mention the name of the editor and theme you're using? It looks really pretty!
Worrying about null safety in a non-type safe language like JS seems like fitting an airbag and then saying this means there is no need to wear a seat belt now.
I am sure there are cases where it's super useful but I thought the point of dynamic languages was speed of development trumps static analysis catching loads of run-time bugs?
You must have read that in an article from the 2000s.
Calling C++ std::optional a null dereference overcome is a joke. Calling nullopt_optional.value() ends with exception. Calling nullopt_optional-> or *nullopt_optional ends with ub.
You forgot to check if pointer != nullptr, you gonna forget checking od optional_instance.has_value() as well.
Crystal has nice null checks and union types and it's pretty fast like Go, concurrency is similar, too. Also with a Ruby like sintax❤
Honestly, I hate null safety, for me it's really annoying and even in C/C++ segmentation faults aren't that hard to debug. Null safety in some languages like C# feels just really annoying to me. Probably a very uncommon opinion but that's just the way I learned to code.
Kotlin, Rust, Swift. You have to mention Dart languange too 💯
i have 0 respect to any language that has no null safety
what terminal font do you use?
JetBrainsMono Nerd Font. It's a mouthful!
*sniff* Haskell *sob* mentioned 🥲🥲🥲
My stages of learning Haskell
This is great!
WTF?
Oh, I get it.
Nope. I don't.
My head hurts.
Oh! I see!
WTF?
Let's start from scratch.
Lambda calculus.
Now it makes more sense.
Start enjoying WTF moments
This is great!
... Long windy road ahead of me
Optionals are just null checks with extra steps
that ending was genius for druink people like me that love coding and just kind listen to things like this in aisutations they really shouldn't be. whilst on web based whatsapp waiting for people.
you should do a video on if ADHD associated with being better or worse for people that code, or if thinking about that distracts from the underlying thing that builds towards enjoyment or proficiency in code
I do wan5 more functional stuff in python like a pipe operator.
As it stands tho the entire ml economy system is stuck with python 3.8 so I would probably not see it.
Why are you guys still in 3.8?
@greyshopleskin2315
Well it's up to 3.11 at times.
In general because it takes time to move these complicated c packages to the new versions.
Especially with this new no Gill option that's gota make this whole thing a mess
"blah blah bla... is actually a monad."
Oh! That explains a lot. I'm pretty sure 99% of the viewers know what a monad is.
could someone write a patch to fix a specific copy of python to be safe?
each time a new version comes out they would have to reverse engineer and patch it just like doing a [k] patch for software piracy.
probably why it is being differed is because the creators of python are lazy and dont feel like adding a safety check.
You can "?" OPTIONALS ?!? i only thought its just for results.
The more yknow!
Same monadic api
I thought that Java was really bad when it came to Null Pointer Exceptions, then here comes C# with its Null Reference Exception, and unlike Java (unless you have the debugger open) good f--ing luck finding out where it came from.
For Python you have the mypy library, which works great with the native type annotations.
And i was just learning go , goroutine is just so cool , i like rust as well but its just too deep for me to currently understand