Confused about the benefit of null safety - dart

As far as I know, the benefit of null safety is to prevent the accidental assignment of null to a variable and then later doing something like nullable_variable.foo() which would cause a runtime error.
But even when I am not using null safety, I do get a compilation error when calling a method on an object that have the null value, for example I get a compilation error when doing the following:
class Student
{
void foo()
{
}
}
void main()
{
Student? s1 = null; // not using null safety here
s1.foo(); // this causes a compilation error
}
So what I mean is that I am still getting the benefit of null safety without using null safety, so what is the point of null safety?!

You are using "Null Safety". The Null Safety feature is what allows you to write Student? to begin with, to distinguish between types which allow null and types which do not.
With that knowledge, the compiler can disallow calling methods on a nullable type that are not also allowed on null. And it does.
You get a compile-time error for something that, without Null Safety, you'd have written Student s1 = null;, had that accepted by the compiler, and gotten a runtime error.
The promise of Null Safety is that you get compile-time errors instead of runtime errors. The cost is that you need to check for null (or do unsafe casts to non-null types, but checking is better), and you have to explicitly include null in types where you do want to accept null.

To say in simple terms using your above example
Student s1 = null is valid without null safety which will lead to various other problems , Especially when you are dealing with the huge project.
So when you opt into null safety, types in your code are non-nullable by default, meaning that variables can’t contain null unless you say they can.
With null safety, your runtime null-dereference errors turn into edit-time analysis errors.
Where as in null safety , you can't have somethin like Student s1 = null,
Instead use Student? s1 = null. Meaning you are explicity saying the compiler that you are aware that this value can be null and take care of it in the entire program.
So flutter accepts that and helps you through by giving features like ! ?? ?.
To summarize , the null safety was introduced because,
Developers like statically-typed languages like Dart because they enable the type checker to find mistakes in code at compile time, usually right in the IDE. The sooner you find a bug, the sooner you can fix it. When language designers talk about “fixing null reference errors”, they mean enriching the static type checker so that the language can detect mistakes
And Dart is designed to run on an end-user’s device. If a server application fails, you can often restart it before anyone notices. But when a Flutter app crashes on a user’s phone, they are not happy. When your users aren’t happy, you aren’t happy.
Please follow the below links to dive deep into the null-safety concept.
https://dart.dev/null-safety
https://dart.dev/null-safety/understanding-null-safety

Related

What is the point of null safety in Dart?

Is the point of null safety to prevent the accidental assignment of null to a variable and then later doing something like nullable_variable.foo() which would cause a runtime error?
Yes:
With null safety, your runtime null-dereference errors turn into edit-time analysis errors.
It means that a lingering problem that would otherwise become known only when its already too late will turn into an error that you get while you are developing, so you will know about it and can fix it before it ever reaches a customer.

How to ensure user does not pass null variable to Flutter plugin?

Let's say, in Dart/Flutter you are designing a plugin API have a function like this:
static Future<void> startPlaying(Uint8List bytes) async {
_channel.invokeMethod('playBytes', bytes);
}
Would it be true to say that, thanks to the null-safety feature of Dart, we are not responsible in the event that the 'bytes' parameter is passed in as null by the plugin user (and thus causing a runtime crash) because in order to do so, they would have to explicitly unwrap a nullable variable, thereby promising that it is is not null.
OR... is there some other thing we should do such as throwing an exception?
With sound null safety, there is no way to call that method with null as argument.
With unsound null safety, which is what you get if not every library of your program is opted into null safety, it's technically possible to pass null as an argument (because a non-null safe library could be doing the calling, or could be passing null into other null safe code, which doesn't check because it assumes null safety.)
You can choose to check the value coming in, as an extra precaution.
The language makes that hard for you, because locally the code looks null safe.
Something like
if (bytes as dynamic == null) throw ArgumentError.notNull("bytes");
I personally wouldn't bother with that, unless there is a real risk of something going spectacularly wrong if null manages to get into the code.
It's documented as not allowing a null value, and most well-behaved code will respect that. Eventually, when all programs are soundly null safe, that check would just be dead code.
First:
if you mean the final user that use app on device, you should have a validation for input.
Second:
if you mean someone try your plugin to code the app.
you can add required keyword, the dart analysis give the user the error if don't pass bytes.
static Future<void> startPlaying({required Uint8List bytes}) async {
_channel.invokeMethod('playBytes', bytes);
}

Why is asigning a dynamic to a non-nullable not an error with sound null safety

In dart with sound null safety turned on it is entirely possible to do
dynamic myVar; // myVar assumes default value of null
String someString = myVar; // No warning by IDE.
As expected the above results in a run-time error since myVar is null and someString is non-nullable.
Is this a problem with the linter/IDE?
I did discover that I can enable pedantic linting that causes the IDE to show a warning when I try to implicitly cast dynamic to another type. Turning that on helps but I think the problem is that dynamic can be null without having to be explicitly defined as nullable.
In short we don't have
dynamic? myNullableVar;
My questions is: Is this a bug?
This "issue" bites you most commonly when you do something like
Map<String, dynamic> myData = jsonDecode(receivedResponseBody);
var name = myData['name'];
In the above example I expect the IDE to show a warning that I am trying to assign a potentially null value to a non-nullable variable. The IDE should require the user to add a null check.
If this is not a bug (in the IDE or in the linter), then why not?
Long story short: The implicit cast from dynamic to another type masks the issue with null assignments, and I expect the IDE to provide a warning.
EDIT: after note from # jamesdlin below
Specifically I am OK with the following where the Left-hand side of the assignment allows null values.
dynamic someVar;
String? myString = someVar;
Side note, I was hoping that the new dart typeDef feature would allow my to build something similar to Union type hints in Python. That would allow me to then get something like
typeDev JsonValueType = { int, String, float, bool };
And the result from jsonDecode would then be
Map<String, JsonValueType?>
Which explicitly includes Null and therefore the IDE would warn the user to add a null check.
My opinion: As long as you can assign any nullable type on the right of an assignment operator to a non-nullable type on the left, you don't have true sound null safety.
It's not a bug.
You can do:
dynamic x = "not an int";
int y = x;
and not get any compiler warnings. That's just how dynamic works, it turns off compiler ("static") type-checks and relies on run-time ("dynamic") checks instead. That's where the name comes from.
There is no reason to allow dynamic? because dynamic already allows the null value (and any other value). The dynamic type is inherently nullable, adding ? to it makes no difference, just as Null? is meaningless.
The Dart type system is statically null sound (mostly, the usual caveats around generics apply), but dynamic is a way to turn that type system off on request. Don't use dynamic unless you want that effect.

Kotlin- Extension functions and platform types?

I want to add two extension functions to ResultSet that gets a value as a LocalDate.
fun ResultSet.getLocalDate(colName: String) = getDate(colName)?.toLocalDate()
fun ResultSet.getLocalDate(colIndex: Int) = getDate(colIndex)?.toLocalDate()
The problem is getDate() returns a Date!, and obviously I could get a null error without the ?. call before toLocalDate(). But then anyone using this extension must use the result as a LocalDate? rather than a LocalDate!.
Is there any way I can maintain the platform type for consistency's sake? And let the user of the extension function decide if it is allowed to be nullable or not? Or am I looking at this wrongly as an inconvenience rather than a feature?
Look at it from a different angle: if you could make your functions return a value of platform type LocalDate!, Java unsafe nullability would spread to the functions usages in your Kotlin code: they would return null at any time, possibly unexpected to the caller using the return value as non-null.
Kotlin, in turn, is null-safe and it won't allow passing null silently to somewhere where it will cause an NPE. Instead, every value is either passed around as nullable or passes non-null check or assertion.
Platform types are non-denotable in the language, this is just a way of dealing with unsafe Java nullability (simply treating all Java values as nullable wouldn't work). They provide you a way to state that you believe that this call to Java code won't return null: when you treat T! as T, an assertion is generated to check it. Otherwise you work with platform type T! as with nullable T?.
Null safety is one of the key points in Kotlin language design, and it makes you decide for each value in your Kotlin code whether it is nullable or not.
You have two options for API design:
Return a non-null value, checking the nullability inside your function
Return nullable value and thus warn the caller about possible null
However, if a function has semantics allowing the caller to assume that it won't return null in some conditions, you can make a wrapper function that makes the assertion. This is feasible if coupled with additional logic or fallback, otherwise it will hardly be more concise than the assertion (!!) at call site.

F# Options... do they really prevent Null Reference Exceptions

I am reading the book "Professional F# 2.0" The author shows the following code
let a string : option = None
if a.IsNone then
System.Console.WriteLine("a is none")
else
System.Console.WriteLine("a is some");;
then says
"this makes the use of Option vastly superior to the use of null and goes a long way towards removing a significant source of exceptions thrown at runtime"
ok. so I write
System.Console.WriteLine(a.GetType());;
And I get
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Object.GetType()
at .$FSI_0008.main#()
Stopped due to error
And I am like 'un!!!"
How is really doing a
if a.isSome then
do bla bla
any different from
if a != null then
do bla bla
So I don't see how is the programmer being saved from NullPointers
PS: NullPointerException has caused me lot of grief in the past.
The F# compiler does not prevent you from NullReferenceException completely. When you work with types defined in .NET, then you can still get null value, because there is no way F# could prevent that.
However, when you work with types declared in F#, then the compiler does not allow creating null values of that type, and so it avoids NullReferenceException in that case. For example, the following code does not compile:
type Person(name:string) =
member x.Name = name
// 'Person' is a type declared in F#, so the argument cannot be 'null' in safe code
let printName (person:Person) =
printfn "%s" person.Name
// Compiler error here - 'null' is not a valid value of 'Pereson' type
printName null
When you use option<Person> as an argument, then you have to explicitly check for None and Some cases. This is best done using match, which checks that you're not missing any of the cases. For example:
let printName (person:option<Person>) =
match person with
// You get a warning here, saying that you're not handling the 'None' case!
| Some person -> printfn "%s" person.Name
The warning tells you that you should add case handling None. You can still compile the code, but you will not get NullReferenceException when working with F# types if you do not ignore warnings.
See also this great, related StackOverflow post.
To add to the answer by Tomas, a major benefit of Option type lies in the higher order functions that it supports, that give you more brevity and safety. You might find my blog post on the topic useful.

Resources