In Dart's Strong-Mode, can I leave off types from function definitions? - dart

For example, I'd like to just be able to write:
class Dog {
final String name;
Dog(this.name);
bark() => 'Woof woof said $name';
}
But have #Dog.bark's type definition be () => String.
This previously wasn't possible in Dart 1.x, but I'm hoping type inference can save the day and avoid having to type trivial functions where the return type is inferable (the same as it does for closures today?)

The language team doesn't currently have any plans to do inference on member return types based on their bodies. There are definitely cases like this where it would be nice, but there are other cases (like recursive methods) where it doesn't work.
With inference, we have to balance a few opposing forces:
Having smart inference that handles lots of different cases to alleviate as much typing pain as we can.
Having some explicit type annotations so that things like API boundaries are well-defined. If you change a method body and that changes the inferred return type, now you've made a potentially breaking change to your API.
Having a simple boundary between code that is inferred and code that is not so that users can easily reason about which parts of their code are type safe and which need more attention.
The case you bring up is right at the intersection of those. Personally, I lean towards not inferring. I like my class APIs to be pretty explicitly typed anyway, since I find it makes them easier to read and maintain.
Keep in mind that there are similar cases where inference does come into play:
Dart will infer the return type of an anonymous function based on its body. That makes things like lambdas passed to map() do what you want.
It will infer the return type of a method override from the method it is overriding. You don't need to annotate the return type in Beagle.bark() here:
class Dog {
String bark() => "Bark!";
}
class Beagle extends Dog {
final String name;
Dog(this.name);
bark() => 'Woof woof said $name';
}

Related

What does it mean <> in method?

I have this method
#override
Response<BodyType> convertResponse<BodyType, SingleItemType>(
Response response) {
final Response dynamicResponse = super.convertResponse(response);
final BodyType customBody =
_convertToCustomObject<SingleItemType>(dynamicResponse.body);
return dynamicResponse.replace<BodyType>(body: customBody);
}
What does it mean <BodyType> and <BodyType, SingleItemType> in this method?
These are called generics in Dart (in fact, they are called the same in other similar programming languages).
The main idea behind generics is that you could reuse the same code without relying on a specific data/return type. Imagine List in Dart. You could have a list of integers (List<int>), a list of strings (List<String>), a list of your custom objects (List<CustomType>) - the type is not hardcoded and it could be adjusted based on your needs.
Also, you could say that it would be easier just to use dynamic or Object types that would cover most of these cases. However, generics brings you type safety, and the method type itself becomes a parameter.
Here is the official documentation about generics.

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.

Is it possible to require generic type arguments on a Dart class?

A common question, specifically since Dart 2, is if it is possible to require some or all generic type arguments on some or all types - for example List<int> instead of List or MyType<Foo> instead of MyType.
It's not always clear what the intention is though - i.e. is this a matter of style (you/your team likes to see the types), to prevent bugs (omitting type arguments seems to cause more bugs for you/your team), or as a matter of contract (your library expects a type argument).
For example, on dart-misc, a user writes:
Basically, if I have this:
abstract class Mixin<T> {}
I don't have to specify the type:
// Works class Cls extends Object with Mixin<int> {} // ...also works
class Cls extends Object with Mixin {}
Is there some way to make the second one not allowed?
Strictly speaking, yes, and no.
If you want to enforce that type arguments are always used in your own projects (instead of relying on type inference or defaults), you can use optional linter rules such as always_specify_types. Do note this rule violates the official Dart style guide's recommendation of AVOID redundant type arguments on generic invocations in many cases.
If you want to enforce that generic type arguments are always used when the default would be confusing - such as List implicitly meaning List<dynamic>, no such lint exists yet - though we plan on adding this as a mode of the analyzer: https://github.com/dart-lang/sdk/issues/33119.
Both of the above recommendations will help yourself, but if you are creating a library for others to use, you might be asking if you can require a type argument to use your class. For example, from above:
abstract class Mixin<T> {}
abstract class Class extends Object with Mixin {}
The first thing you could do is add a default bounds to T:
// If T is omitted/not inferred, it defaults to num, not dynamic.
abstract class Mixin<T extends num> {}
If you want to allow anything but want to make it difficult to use your class/mixin when T is dynamic you could choose a different default bound, for example Object, or even better I recommend void:
In practice, I use void to mean “anything and I don’t care about the elements”
abstract class Mixin<T extends void> {
T value;
}
class Class extends Mixin {}
void main() {
var c = Class();
// Compile-time error: 'oops' isn't defined for the class 'void'.
c.value.oops();
}
(You could also use Object for this purpose)
If this is a class under your control, you could add an assertion that prevents the class from being used in a way you don't support or expect. For example:
class AlwaysSpecifyType<T> {
AlwaysSpecifyType() {
assert(T != dynamic);
}
}
Finally, you could write a custom lint or tool to disallow certain generic type arguments from being omitted, but that is likely the most amount of work, and if any of the previous approaches work for you, I'd strongly recommend those!

What are the conventions in Dart about class members' encapsulation and type annotations?

I am new to Dart language. So I would like to know more about some conventions that programmers follow while developing in this language.
Should I always encapsulate my class members as I do, for example in Java? Whenever I create property of class, should I make it private and provide getters/setters? Or there are situations when I should leave them public? If so, what are examples of these situations?
In my opinion, type annotations such as String, int, etc. increase readability of code. They serve as a documentation for other developers who are reading/using my code. Programmer should not think value of what type is storing in this variable right now. So again, what are the situations, that require using var keyword when declaring a variable?
Dmitry.
Thank you.
Thanks for checking out Dart!
No need to encapsulate class fields. Dart creates implicit getters and setters for you. If you need to actually compute something for that field, you can then implement the getter or setter manually. Bonus: this doesn't break consumers of your API.
Example:
class Person {
int age;
}
Later, you want to calculate age:
class Person {
DateTime birthdate;
int get age => new DateTime.now().difference(birthdate).inDays ~/ 365;
}
In both cases, you can do this:
print(person.age);
Pretty cool! No change in API, and no defensive getters and setters (just add them when you need them).
You should use type annotations for the "surface area" of your code. For example, use type annotations for method and function signatures. For cases where the variable's type is very obvious, you should consider using var, because it's more terse and readable.
For example:
String doCoolStuff(int bar) {
var clearlyABool = true;
return 'Hello world';
}
Note that the return type and bar parameter are type annotated, but the clearlyABool uses var because we initialize with a bool.
Feel free to use type annotations everywhere, it's programmer choice. Anecdote: the dart2js source code uses type annotations pretty much everywhere.

Why are these contravariant argument types considered safe?

I just learned in my programming languages class that "contravariant argument types would actually be safe, but they have not been found useful and are hence not supported in practical languages." Even though they are not supported, I am confused as to why something like this example we were given would still be, in theory, "safe":
class Animal {
...
public bool compare(Panda) { ... }
}
class Panda extends Animal {
...
public bool compare(Animal) { ... }
}
From what I understand, problems with subtyping come up when something is done that could cause a loss of specificity. So what if I did this? :
Panda p = new Panda();
Animal a = new Animal
...
p.compare(a);
When I look at this, it seems like panda could (and probably does) have some extra fields in it that a plain animal wouldn't know about. Thus, even if all of their animal-specific data members are the same, a panda can have other stuff that differs. How would that make it okay to compare it to a plain animal? Would it just consider the animal-only stuff and ignore the rest?
In your example you don't use any generic types. You have Panda extending Animal, and it's an example of inheritance and leads to polymorphism which is more or less what you describe. Check the links.
To get contravariance, you need to consider some generic type. I'll use .NET type IComparer`1[T] as an example. With C# syntax (which I'll use rather than Java), we indicate that IComparer is contravariant in T by writing in in the definition:
public interface IComparer<in T>
{
...
}
Suppose I have a method which returns an IComparer`1[Animal] (or IComaparer<Animal>), like:
static IComparer<Animal> CreateAnimalComparer()
{
// code that returns something here
}
Now in C#, it's legal to say:
IComparer<Panda> myPandaComparer = CreateAnimalComparer();
Now, this is because of contravariance. Note that the type IComparer<Animal> does not derive from (or "extend") the type IComparer<Panda>. Instead, Panda derives from Animal, and this leads to the IComparer<Xxxx> being assignable to each other (in the opposite order, hence "contravariance" (not "covariance")).
The reason why it's meaningful to declare a Comparer<> contravariant, is if you have a comparer that can compare two arbitrary animals, and return a signed number indicating which is greater, then that same comparer can also take in two pandas and compare those. For pandas are animals.
So the relation
any Panda is an Animal
(from inheritance) leads to the relation
any IComparer<Animal> is an IComparer<Panda>
(by contravariance).
For an example with covariance, the same relation
any Panda is an Animal
leads to
any IEnumerable<Panda> is an IEnumerable<Animal>
by covariance (IEnumerable<out T>).

Resources