Better way to assign a value with nullable field in dart - dart

Is there a better way to do this?
Assignment(
dueAt: json['due_at'] == null ?
null :
DateTime.parse(json['due_at']).toLocal()
)
The attribute "dueAt" in Assignment class can be null and i need to parse the string of json['due_at'] to a DateTime, but json['due_at'] can be null too.
Is not really a problem right now but seems noisy and repetitive.

First and foremost, it looks like you're writing JSON serialization code by hand. Your life will be much easier and less bug-prone if you let a library do this instead. json_serializable is very simple and powerful and 100% worth looking into.
However, this pattern is still common outside of json code.
You could also consider writing an extension method for Object? that behaves like the Kotlin standard library's let function (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html)
You can then use Dart's ?. syntax to handle the rest of the logic:
// extension on T rather than Object? to maintain type information
extension Example<T> on T {
R let<R>(R Function(T) function) => function(this);
}
This just applies a given function to this, which isn't incredibly useful on it's own, but allows the use of ?.:
final DateTime? dueAt = json['due_at']?.let(DateTime.parse);
If json['due_at'] evaluates to null, the ?. operator short-circuits, and dueAt is set to null. Otherwise, it evaluates to DateTime.parse(json['due_at']).
Or, you could just use package:kt_dart which ports much of the Kotlin standard library to Dart

In this particular case you may want to use tryParse instead of parse. If dueAt is of type DateTime? you can simply call:
Assignment( dueAt: DateTime.tryParse(json['due_at'])?.toLocal() );
Be aware though that tryParse will return null for any invalid date string (be it null or an improperly formatted string). This may or may not be desired behavior depending on your intended use.

Related

Why does dart not allow method overloading?

I tried to use method overloading in some dart code and quickly learned that overloading is not offered in dart.
My questions are: why is it not offered, and what is the recommended alternative? Is there a standard naming convention since methods that do the same thing but with different inputs must have different names?
Is it standard to use named parameters and then check that the caller has supplied enough information to complete the calculation?
Say I have a method that returns how much money someone makes in a year, called yearlyIncome.
In Java, I would create a method like this
double yearlyIncome(double hourlyRate, double hoursWorkedPerYear)
And maybe another method like this
double yearlyIncome(double monthlyRate, int monthsWorkedPerYear)
and so on. They're all used to calculate the same thing, but with different inputs. What's the best, standardized way to do this in dart?
Thanks so much in advance.
Function overloading is not supported in Dart at all.
Function overloading requires static types. Dart at its core is a dynamically typed language.
You can either use different names for the methods or optional named or unnamed parameters
// optional unnamed
void foo(int a, [String b]);
foo(5);
foo(5, 'bar');
// optional named
void foo(int a, {String b});
foo(5);
foo(5, b :'bar');
Optional parameters can also have default values. Optional named and unnamed parameters can not be used together (only one or the other for a single function)
In the case of a constructor you can use named constructors as an alternative
Dart did not support overloading originally because it was a much more dynamic language where the declared types did not have any semantic effect. That made it impossible to use static type based overload resolution.
Dart has since changed to be more statically type, and there is nothing fundamentally preventing Dart from adding overloading today, except that it would be a huge work and a huge change to the language. Or so I'd assume, because there isn't any obvious design that isn't either highly complicated or hugely breaking.
What you do instead in Dart is to use optional parameters. A method like:
String toString([int radix]);
effectively have two signatures: String Function() and String Function(int). It can act at both signatures.
There are definite limits to how far you can go with just optional parameters, because they still need to have exactly one type each, but that is the alternative that Dart currently provides. (Or use different names, but that's not overloading, you can do that in languages with overloading too).
Optional parameters is also one of the complications if we wanted to add overloading to the Dart language - would existing functions with optional parameters would count as multiple overloadings? If you declare a class like:
abstract class WithOverloading {
String toString();
String toString(int radix);
}
is that then the same signature as:
abstract class WithoutOverloading {
String toString([int radix]);
}
Probably not because you can tear off the latter and get one function with an optional parameter, and you might not be able to tear off both functions from the former and combine them into one function. Or maybe you can, that's why it's not a trivial design question how to include overloading into the existing Dart language.

Why does an unitialized typed variable in Dart not implement the type's interface?

I started learning Dart and was reading a critique of some of it's design choices here: https://medium.com/#krossovochkin/dart-language-bad-design-choices-6e35987dc693
The last point that is made is about the poor type system and the author cited this code snippet which prints null:
void main() {
String s = null;
if (s is String) {
print("string");
} else if (s is Null) {
print("null");
} else {
print ("none");
}
}
The is keyword was new to me but "The Dart Programming Language" by Gilad pointed out that is checks the interface implemented by an object's class and not the actual class of an object.
However this didn't help me much because I would think that the variable s is an instance of String and therefore implements String, but the evidence is to the contrary.
I get that the class is not required when defining objects/variables in Dart, and thus I started to wonder if putting the class in the definition just serves as sugar and has little functional purpose. But instead the class of an object/variable is completely determined by its value, and since the default value for all variables in Dart is null, then it would make sense that String is not implemented, but Null is. Is this the case? Am I way of base? Maybe someone could help me wrap my head around this.
The reason is that is checks the interface of the current object itself and not the reference to this object. So yes, s can point to a String object but also allowed to point to null which are a instance of Null: https://api.dart.dev/stable/2.7.2/dart-core/Null-class.html
Since Null does not implement the String interface, this will return false (null is String). This is also mentioned in the article.
The problem the article are trying to focus on are more the fact you are allowed to set the String variable to null value but Null does not implement String.
Well, in the future, this problem are going to be fixed with non-nullable types which are in development right now. When this is implemented you can actually define variables where you can be sure the value will never be null.
So I continued my Dart reading and I came to a better understanding, and that is that Dart is truly optionally typed and that means 2 things:
Type are syntactically optional.
Type has no impact on runtime semantics.
Therefore the actual type annotation of a variable in Dart only serves documentation purposes and it cannot be assumed that a type annotation is true. The actual type of a variable is wholly determined by the value stored at this variable, and in this case it is null.
In truth the variable that I defined in my example is not a String variable or an implementer of the String interface. It is just annotated that it may be/should be/most likely is a string.

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.

Why is Dart's Datetime.parse not a factory constructor?

Dart's Datetime class has a number of named constructors, but DateTime.parse() is not one of them. Instead, DateTime.parse() is a static method which returns a DateTime. To me, it makes sense as a constructor (since you are generating a new DateTime object in a manner not too different from the Datetime.utc() constructor).
Theories I've come up with are to mirror the fact that int.parse is not a constructor or to allow easier chaining (you don't need to use the cascade operator with a static method). But maybe there is another reason that I'm not thinking of. Does anyone know why they didn't make it a named constructor?
More explanation for the same change for Uri.parse: http://permalink.gmane.org/gmane.comp.lang.dart.general/17081
"parse" is special. The question is: do you see parsing as an
operation that does something and ends up giving you the result, or do
you see the string as data to construct a new element. If you see it
as the earlier, then "parse" should be a static function. If you see
the string as the data, then it should be a named constructor.
And then, of course, there is consistency.

How does Orika decide when to use converter

I am trying to understand when does Orika use converters to do the mapping versus direct casting.
I have the following mapping:
Class A {
Map<String, Object> props;
}
Class B {
String bStr;
int bInt;
}
My mapping is defined as props['aStr'] => bStr and props['aInt'] => bInt
When I look at the generated code, I see that for the String case, it uses a converter and calls its convert method for the transformation:
destination.setBStr("" + ((ma.glasnost.orika.Converter)usedConverters[0]).convert(
((java.lang.Object) ((java.util.Map) source.getProps().get("aStr"),
(ma.glasnost.orika.metadata.Type) usedTypes[0]))
But, for the integer case it directly casts it like this:
destination.setBInt((java.lang.Integer)(java.lang.Object) ((java.util.Map)
source.getProps().get("aInt")))
The above line of code ends up giving class cast exception.
For fixing this issue, I was thinking along the lines of using a custom converter but if the above line of code doesn't use the converter then that wont work.
Of course, I can always do this is in my custom mapper but just trying to understand how the code is generated for type conversion.
Thanks!!
In Orika there is two stages: config-time and runtime, as optimization Orika resolve all used converter in the config-time and cache them into each generated mapper so it will be accessible directly O(1) but in the config time it will try to find in a list O(n) of registered converters which one "canConvert" between two given types, canConvert is a method in Converter interface .
So this solution offer the best of the two worlds:
A very flexible way to register a converter with arbitrary conditions
An efficient resolution and conversion operation in the runtime.
Orika by default, leverage the existence of .toString in every object to offer implicit coercion to String for every Object. The problem here is that there is no Converter from Object to Integer.
Maybe this can be an issue of error reporting. Ideally Orika should report that an Object have to be converted to Integer and there is no appropriate converter registered.

Resources