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.
Related
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
I just started to study F# and accidentally wrote this binding
let List = 1
Now when I try to obtain List methods such as 'filter' I get this error
error FS0039: The field, constructor or member 'filter' is not defined.
Of course using method with full type name like Microsoft.FSharp.Collections.List.filter is still working.
I'm wondering why it is possible to use type name as identifier in F# and how I can set back name List to type List from Microsoft.FSharp.Collections.
When I tried to reassign like this
type List = Microsoft.FSharp.Collections.List<'T>
I get
Error FS0039: The type parameter 'T is not defined.
Thank you!
In F# you can redefine almost everything and shadow existing definitions. This applies to both types (well actually types have a different behavior regarding shadowing, they shadow their values as you open the namespaces) and values but not interchangeably since values and type (and also modules) can somehow coexist at the same time in the scope. The compiler will do his best to find out which one is.
You are not forced to, but it's a common good practice in F# not to use let bindings in uppercase.
Regarding your second question, you are using a type parameter in the right side which doesn't exist in the left side of the assignment, it should be:
type List<'T> = Microsoft.FSharp.Collections.List<'T>
But notice that filter doesn't belong to the type. It's rather defined in the List module.
You should just rename your let binding from List to something sensible - as Gustavo mentioned, your definition is shadowing the core List module from F# and there is no way to use List to refer both to your integer and to the module. Shadowing core functions will make your code pretty confusing. It's also a good idea to use camelCase for let bindings, but that's a matter of taste.
If you insist on shadowing List, then you won't be able to call List.filter using List.filter. If you wanted something shorter, you could define module alias:
module FsList = Microsoft.FSharp.Collections.List
Note that your attempt to do something similar with List<'T> does not do the same thing, because functions such as filter are in a module named List rather than being static members of the type. With this, you can call filter using FsList.filter.
I have a desire to satisfy compiler warning level 5. So I have 32 warnings in one file FS0052 The value has been copied to ensure the original is not mutated by this operation
I've followed the only SO post that seems to be related to this warning, but since my type is being type provider generated by Microsoft I can't just go mark the field mutable to quiet the warning. plus making something mutable that actually shouldn't ever be mutated seems like a hack not a fix.
examples:
Nullable .GetValueOrDefault()
Nullable .ToString()
Guid .toString()
struct method calls of any sort I believe
What is the recommended way to deal with this warning from a proper functional perspective?
Not sure if you're still interested.
It seems to me that the compiler emits the warning when it is unsure whether the method call is going to destroy the state of original instance (this should mostly come from any library outside F#).
Explicit copy the value into a variable is, in my case, often mitigate the warning. For example:
open System
// generate the warning due to "ToString()"
let DirSeparator = Path.DirectorySeparatorChar.ToString()
// no warning
let ExplicitCopy = let x = Path.DirectorySeparatorChar in x.ToString()
let Alternative = sprintf "%c" Path.DirectorySeparatorChar
What is the recommended way to deal with this warning from a proper
functional perspective?
I found many questions about the issue, but this particular question I found nowhere else. After having fixed many such warnings, this is my conclusion.
I believe there is no single solution. (Unless you want to resort to silencing the warning all over the place with a compiler directive or something, and I certainly don't want to do that. I have a feeling you agree with me on that, having bumped up the warning level to 5.)
First option
There is frequently another way to code, without having to resort to intermediate values or other complex expressions.
For example, to silence the warning in this case
myStruct.someField <- myRecord.SomeField.ToString()
simply change it to
myStruct.someField <- string myRecord.SomeField
Second option
If you do not find a function or some way to easily rewrite your way out of it, and it is a particular case that repeats frequently in your source, then there is the possibility to create a function or functions that you can use in these cases in order to silence the warning.
For example, I use this function instead of GetValueOrDefault.
let valueOrDefault (v: 'a Nullable) = if v.HasValue then v.Value else Unchecked.defaultof<'a>
Third option
If you have only a single particular case or barely a handful of similar cases, then using an intermediate value is possibly the easiest way to silence the warning. I tend to comment the reason for the verbosity in these cases.
According to the documentation for Option.Value<'T>:
Get the value of a Some option. A NullReferenceException is raised if
the option is None.
and
This function is for use by compiled F# code and should not be used
directly.
Does the last sentence imply that it's due to interop? What's the use case if so?
Otherwise it seems very strange since the whole point of the Option type is to make the possibility of undefined values explicit and encourage handling of them, which easily can be circumvented:
let none : int option = None
let value = none.Value
I imagine it is there so that
match v with
|Some(t) -> t
works - without that Value property, you wouldn't be able to get the t with any functions which are available to F# code (Note there are some DU properties which are not accesible from F# which are an alternative here). There may also be some very minor speed benifits if you know that the option is Some as you don't check it if you use value directly
In Groovy types are optional so you can use either:
String foo = "foo"
foo.noSuchMethod()
or
def foo = "foo"
foo.noSuchMethod()
I assumed that the first example would generate a compile-time error, whereas the second would only fail at runtime. However, this doesn't appear to be the case. In my experience, a compile-time error is generated in neither case.
Am I right in assuming then that the only benefit of declaring the type of a reference is as a form of documentation, i.e. to communicate intentions to other programmers. For example, if I write a method such as:
def capitalize(String arg) {
return arg.toUpperCase()
}
This communicates the type of arguments that should be passed to the function much more effectively than:
def capitalize(def arg) {
return arg.toUpperCase()
}
Does the Groovy compiler perform any type-checking when types are specified?
Thanks,
Don
[Edit] Newer versions of Groovy do allow for compile-time static type checking. Code that uses this annotation IS faster than regular run-time Groovy, as many of the dynamic checks are skipped.
As Cesar said, type checking is a run-time process, one of the major reasons that Groovy is slower than Java (not that that's really bad).
You can see why this is, right? Given the dynamic nature of Groovy, it's near-impossible to tell if String has been extended somewhere else in your code to contain a method noSuchMethod(). The same goes for member type-checking, as it's entirely possible to remove a member of one type, and add a member of another type with the same name later in code. It's probably not common, but very possible.
The question is, how much type checking do you really need? You're calling the method, you really should know what arguments it takes, or if the method actually exists. Using compile-time checking to save you the time of looking it up isn't a core usefulness of the compiler.
In Groovy, type checking is done dynamically at runtime. The benefits of variables with type is that you can be sure that it contains the value you expect them to have, otherwise you get a runtime exception that you can catch and do whatever you need to do to handle the exception.
Compile time-checking in Groovy is close to impossible for types. Your example of
String foo = "foo"
foo.noSuchMethod()
Would work beautifully given that previously an instruction was executed along the lines of
String.metaClass.noSuchMethod { -> println "Yes there is such a method"}
One reason you might specify a type is to allow an IDE to help you.
def foo
foo.[ctrl-space]
... won't help you very much
List foo
foo.[ctrl-space]
... might (depending on the IDE) give you a choice of List's methods. Of course, a method that's not one of the choices might be a valid one to use, for the reasons given in other answers.
There are other automated software maintenance operations that benefit from clues about types. For example, refactoring.