Performance of accessing the same getter twice in a statement - dart

Lets say I have the following class:
class MyClass{
List<List<String>> get myProperty{
return some_complex_logic_that_returns_a_list_list_string();
}
}
Now I want to use MyClass.myPropery twice in the same statement, for example:
MyClass myClass = MyClass();
String s = myClass.myProperty.isNotEmpty ? myClass.myProperty[0][0] : '';
But because I happen to know that the internals of the myProperty getter are rather complex, my gut instinct is to instead do:
MyClass myClass = MyClass();
final list = myClass.myProperty;
String s = list.isNotEmpty ? list[0][0] : '';
Of course, someone who has no information about the internals of myPropery would probably not do that. So what I'm wondering is - does assigning myProperty to a local variable actually improve performance, or does the compiler cache/reuse the result of a getter if it's used multiple times in the same statement?

Related

Dart Object instantiating

I'm very new to programming and Dart. I was wondering why you put Response in front of the variable response. Also why do we use Datetime object in front of the variable 'now?' I believe when you want to instantiate, you write Datetime now = Datetime(); But it was written something else for the variable. What does it mean? Thank you very much in advance!
void getTime() async{
Response response = await get(Uri.parse('https://worldtimeapi.org/api/timezone/Europe/London'));
Map data = jsonDecode(response.body);
// get properties from data
String datetime = data['datetime'];
String offset = data['utc_offset'].substring(1,3);
//create DateTime object
DateTime now = DateTime.parse(datetime);
now = now.add(Duration(hours: int.parse(offset)));
print(now);
}
Ok, let's break this down:
Response response = await
get(Uri.parse('https://worldtimeapi.org/api/timezone/Europe/London'));
A variable declaration in Dart looks like this:
<Type> <name> [= <value>];
So in your case Response is the type of the variable, response is the name of the variable and get(Uri.parse('https://worldtimeapi.org/api/timezone/Europe/London')) is the value of the variable (actually get(...) is a future, and the future's response is the value, that's why the await keyword is there, but that's not important.)
Sidenote, you can actually use the var keyword to skip the <Type> part of the declaration:
int myFunction(a, b) => a+b;
int x = myFunction(1,2);
Above, we know myFunction returns an int, and the variable x is equal to the result of myFunction, so it must also be an int, we can use the var keyword to skip writing int then:
int myFunction(a, b) => a+b;
var x = myFunction(1,2);
Of course, here there isn't that big a difference between writing int and var, but when your type is something like List<Map<String, List<int>>> it is quite nice to be able to skip writing that over and over
Now for this line:
DateTime now = DateTime.parse(datetime);
We already know that the first DateTime tells us what type the variable is, and we know that now is the name of the variable, we also know that the variable's value is DateTime.parse(datetime) because it is what goes after the = sign, but we still don't know what DateTime.parse(datetime) means.
Classes can have static and non-static methods, a static method is a method that gets called on a class, while a non-static method gets called on an object. Most method calls ever are non-static, look at this example:
class Car {
void accelerate() {
// some method to increase the speed of the car
}
void decelerate() {
// some method to decrease the speed of the car
}
Car buyCar(int maxCost) {
// some method to buy a new car
}
}
void main() {
Car car = Car();
car.accelerate();
car.decelerate();
Car otherCar = car.buyCar(100000000);
}
Above, the method accelerate and decelerate are instance methods and that makes sense because they probably affect some statistics of the car (like current speed), buyCar is also an instance method, but if you think about it, it shouldn't be, it doesn't affect your current car if you buy a new one and also you shouldn't need to have a car object to buy another one in the first place. So let's make that last method static:
class Car {
void accelerate() {
// some method to increase the speed of the car
}
void decelerate() {
// some method to decrease the speed of the car
}
static Car buyCar(int maxCost) {
// some method to buy a new car
}
}
It is as simple as adding the static keyword, now instead of having to do this:
Car myOldCar = Car();
Car myNewCar = myOldCar.buyCar(100000000);
we can just do this:
Car myNewCar = Car.buyCar(100000000);
looks familiar?
That's right parse is a static method on DateTime class, it takes a string that looks like this: 2012-02-27 13:27:00.123456789z and returns a DateTime object.
So to recap:
Response response = await
get(Uri.parse('https://worldtimeapi.org/api/timezone/Europe/London'));
Response is the type of the variable, response is its name and get(Uri.parse('https://worldtimeapi.org/api/timezone/Europe/London')) is a method that returns a Response object.
DateTime now = DateTime.parse(datetime);
Similarly DateTime is the type, now is the name and DateTime.parse is a static method that parses a string and makes a DateTime object, in this case the string is datetime, which was declared as being equal to data['datetime'].
If you want to understand better how the parse method works, here is the DateTime parse documentation

Initializing members in a Constructor

Let's assume a simple class like this
class MyClass {
String aString;
DateTime aDate;
MyClass({this.aString = '', this.aDate = DateTime.now()});
}
This code does not work because optional parameters must be constant. I do not want aDate be nullable.
I can do this:
class MyClass {
String aString;
DateTime aDate;
MyClass({this.aString = ''}) : this.aDate = DateTime.now();
}
This is accepted by the compiler but I cannot set aDate when constructing an instance of MyClass like so:
var c = MyClass(aString: 'bla bla', aDate: DateTime(2021, 9, 20));
Instead it seams I have to write:
var c = MyClass(aString: 'bla bla');
c.aDate = DateTime(2021, 9, 20);
Is there really no better way to initialize complex objects with optional named parameters in constructors with null safety turned on? This is just a simplified sample. In reality, I have classes with lots of complex objects that I get from a remote server. The method shown above forces me to write hundreds of lines of assignment statements.
You can use the following:
class MyClass {
String aString;
DateTime aDate;
MyClass({this.aString = '', DateTime? aDate}) : aDate = aDate ?? DateTime.now();
}
You were on the right track, you just needed to combine the two methods you provided above. This uses an optional named parameter, which must be nullable, and only assigns it to this.aDate when it's not null using the ?? operator. If it is null, it uses your desired default value of DateTime.now().

How do I initialize non-nullable members in a constructor body?

I've created my class in Dart this way, but I'm getting the Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. I would like to know if there's a way to do it in a 'Python' style where this kind of class creation is possible, thank you in advance.
class Lexer {
String _text;
int _pos;
String _current_char;
Lexer(String text) {
this._text = text;
this._pos = -1;
this._current_char = '';
this.advance();
}
void advance() {
this._pos++;
this._current_char = this._pos < this._text.length ? this._text[this._pos] : '';
}
}
class Lexer {
String _text;
int _pos;
String _current_char;
This declares several members with type String. Since they are declared as String and not as String?, these members are non-nullable; they are not allowed to ever be null. (This is part of the new null-safety feature from Dart 2.12.)
Dart initializes objects in two phases. When the constructor's body runs, Dart expects all member variables to already be initialized. Because your members are non-nullable and haven't been initialized to non-null values yet, this is an error. The error message explains what you can do:
Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Use initializer expressions. This means using an initializer list:
Lexer(String text)
: _text = text,
_pos = -1,
_current_char = '' {
advance();
}
Note that if you're initializing members with a construction parameter of the same name, you can use shorthand:
Lexer(this._text)
: _pos = -1,
_current_char = '' {
advance();
}
Adding field initializers. This means initializing members inline in the class declaration.
class Lexer {
String _text = '';
int _pos = -1,
String _current_char = '';
Marking your members as late. This means that you promise that the variables will be initialized before anything attempts to use them.
class Lexer {
late String _text;
late int _pos,
late String _current_char;
Making your members nullable, which allows them to be implicitly null by default:
class Lexer {
String? _text;
int? _pos,
String? _current_char;
However, that will require that all accesses explicitly check that the members aren't null before using them.
You also might want to read: Dart assigning to variable right away or in constructor?

Writing to mutable property for a struct record is not allowed in F#. Why?

When I have the following code:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah";
The compiler complains that "A value must be mutable in order to mutate the contents". However when I remove the Struct attribute it works fine. Why is that so ?
This protects you from a gotcha that used to plague the C# world a few years back: structs are passed by value.
Note that the red squiggly (if you're in IDE) appears not under FirstName, but under john. The compiler complains not about changing the value of john.FirstName, but about changing the value of john itself.
With non-structs, there is an important distinction between the reference and the referenced object:
Both the reference and the object itself can be mutable. So that you can either mutate the reference (i.e. make it point to a different object), or you can mutate the object (i.e. change the contents of its fields).
With structs, however, this distinction does not exist, because there is no reference:
This means that when you mutate john.FirstName, you also mutate john itself. They are one and the same.
Therefore, in order to perform this mutation, you need to declare john itself as mutable too:
[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}
let mutable john = { FirstName = "John"; LastName = "Connor"}
john.FirstName <- "Sarah" // <-- works fine now
For further illustration, try this in C#:
struct Person
{
public string FirstName;
public string LastName;
}
class SomeClass
{
public Person Person { get; } = new Person { FirstName = "John", LastName = "Smith" };
}
class Program
{
static void Main( string[] args )
{
var c = new SomeClass();
c.Person.FirstName = "Jack";
}
}
The IDE will helpfully underline c.Person and tell you that you "Cannot modify the return value of 'SomeClass.Person' because it is not a variable".
Why is that? Every time you write c.Person, that is translated into calling the property getter, which is just like another method that returns you a Person. But because Person is passed by value, that returned Person is going to be a different Person every time. The getter cannot return you references to the same object, because there can be no references to a struct. And therefore, any changes you make to this returned value will not be reflected in the original Person that lives inside SomeClass.
Before this helpful compiler error existed, a lot of people would do this:
c.Person.FirstName = "Jack"; // Why the F doesn't it change? Must be compiler bug!
I clearly remember answering this question almost daily. Those were the days! :-)

F# - public myClass test;

I'm fairly need to this whole F# thing and Functional programming.. I've been looking in all possible documents and found nothing, so therefore I ask you for help for my simple problem. How do I declare:
public myClass test;
in F#?
If you want the declaration to go in the module, do
let test = myClass()
If you want the declaration to go in the class, do
val test: myClass
You'll have to initialize test in the constructor, or to provide a DefaultValue attribute:
[<DefaultValue>]
val mutable test: myClass
Then it will be default-initialized to null.
The final alternative is to have the private field (then you can initialize it inside the class declaration) and provide an accessor:
let test = myClass()
member x.Test = test
or
let mutable test = myClass()
member x.Test with get () = test and set value = test <- value

Resources