I'm checking out some dart code and looking at this:
AppState.fromJson(Map<String, dynamic> json)
: cartItems = (json['cartItems'] as List)
.map((i) => new CartItem.fromJson(i as Map<String, dynamic>))
.toList();
What's the reasoning behind the colon?
Why is this different from a regular assignment?
You can find more info in the dart tour: https://dart.dev/guides/language/language-tour#classes
If the superclass doesn’t have an unnamed, no-argument constructor, then you must manually call one of the constructors in the superclass. Specify the superclass constructor after a colon (:), just before the constructor body (if any).
Besides invoking a superclass constructor, you can also initialize instance variables before the constructor body runs. Separate initializers with commas.
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
During development, you can validate inputs by using assert in the initializer list.
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
You can also use them to initialize the final variables:
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
Related
class X extends Y {
X(int a, int b) : super(a,b);
}
Can someone give me an explanation about the syntax meaning of the colon :?
This feature in Dart is called "initializer list".
It allows you to initialize fields of your class, make assertions and call the super constructor.
This means that it is not the same as the constructor body. As I said, you can only initialize variables and only access static members. You cannot call any (non-static) methods.
The benefit is that you can also initialize final variables, which you cannot do in the constructor body. You also have access to all parameters that are passed to the constructor, which you do not have when initializing the parameters directly in the parentheses.
Additionally, you can use class fields on the left-hand of an assignment with the same name as a parameter on the right-hand side that refers to a parameter. Dart will automatically use the class field on the left-hand side.
Here is an example:
class X {
final int number;
X(number) : number = number ?? 0;
}
The code above assigns the parameter named number to the final field this.number if it is non-null and otherwise it assigns 0. This means that the left-hand number of the assignment actually refers to this.number. Now, you can even make an assertion that will never fail (and is redundant because of that, but I want to explain how everything works together):
class X {
final int number;
X(number): number = number ?? 0, assert(number != null);
}
Learn more.
It's ok to access non static member in initializer list.
class Point {
num x, y;
Point(this.x, this.y);
Point.origin(): this.x = 10, this.y = 10;
}
main() {
Point p = Point.origin();
print(p.x); // 10
}
I am trying List.fold() in Dart 2.1.0:
void main() {
final a = [1,2,3];
print(a.fold(0, (x,y) => x + y));
}
It comes back with an error:
Error: The method '+' isn't defined for the class 'dart.core::Object'.
Try correcting the name to the name of an existing method, or defining a method named '+'.
Seems that it can't infer the type of x to be int, so doesn't know how to apply the + there.
According to the method spec, x should have the same type as the initial value 0, which is obviously an int. Why can't Dart infer that?
I can make it work by explicitly tipping the type:
void main() {
final a = [1,2,3];
print(a.fold<int>(0, (x,y) => x + y));
}
But I am a little disappointed that Dart cannot infer that for me. Its type inference seems stronger in most other cases.
Dart type inference works as expected:
void main() {
final a = [1,2,3];
var sum = a.fold(0, (x,y) => x + y);
print(sum);
}
The reason it fails in your example is because type inference outcome depends on the context where the expression is evaluated:
print(a.fold(0, (x,y) => x + y));
throws an error because print parameter is expected to be an Object: inference uses Object to fill generic T type argument` of fold:
T fold <T>(T initialValue, T combine(T previousValue, T element)) :
To validate this assumption:
void main() {
final a = [1,2,3];
Object obj = a.fold(0, (x,y) => x + y);
}
Throws exactly the same error.
Take away:
Type inference works well, but care must be paid to the generic expression's surrounding context.
Is it this a Dart limitation?
I dont think this behavoir it is a limitation of dart, just an implementation choice.
Probably there are also sound theoretical reasons that I'm not able to speak about but I can works out some reasoning about that.
Considering the generic method:
T fold <T>(T initialValue, T combine(T previousValue, T element))
and its usage:
Object obj = a.fold(0, (x,y) => x + y);
There are two paths for inferring the type of T:
fold returned value is assigned to an Object obj, 0 is an Object since it is an int, then T "resolve to" Object
fold first argument is an int, the returned value is expected to be an Object and int is an Object,
then T "resolve to" int
Dart chooses the path 1: it takes for inferred type the "broadest" (the supertype) that satisfy the generic method.
Should be better if Dart implements path 2 (the inferred type is the "nearest" type)?
In this specific case probably yes, but then there will be cases that does not works with path 2.
For example this snippet would not be happy with path 2:
abstract class Sensor {
String getType();
}
class CADPrototype extends Sensor {
String getType() {
return "Virtual";
}
}
class Accelerometer extends Sensor {
String getType() {
return "Real";
}
}
T foo<T extends Sensor>(T v1, T v2, T bar(T t1, T t2)) {
if (v2 is CADPrototype) {
return v1;
}
return v2;
}
Sensor foo_what_dart_does(Sensor v1, Sensor v2, Sensor bar(Sensor t1, Sensor t2)) {
if (v2 is CADPrototype) {
return v1;
}
return v2;
}
Accelerometer foo_infer_from_argument(Accelerometer v1, Accelerometer v2, Accelerometer bar(Accelerometer t1, Accelerometer t2)) {
if (v2 is CADPrototype) {
return v1;
}
return v2;
}
void main() {
Accelerometer v1 = Accelerometer();
CADPrototype v2 = CADPrototype();
Sensor result;
result = foo(v1, v2, (p1, p2) => p1);
// it works
result = foo_what_dart_does(v1, v2, (p1, p2) => p1);
// Compilation Error: CADPrototype cannot be assigned to type Accelerometer
result = foo_infer_from_argument(v1, v2, (p1, p2) => p1);
}
i`m learning the flutter, but i do not understand those letters meaning.
map<T>(T f(E e)) → Iterable<T>
Returns a new lazy Iterable with elements that are created by
calling f on each element of this Iterable in iteration order. [...]
so,what do they stand for?
T:
f:
E:
e:
→:
Iterable.map<T>:
map<T>(T f(E e)) → Iterable<T>
Returns a new lazy Iterable with elements that are created by calling
f on each element of this Iterable in iteration order. [...]
T is a language Type in this case the Type of the items of the iterable and is also the type that function f must return.
→ tells you the return type of the whole function (map) in this case an Iterable of T
f is the function applied to the Element e that is passed as the parameter to the function so that the function could do some operation with this current value and then return a new value of type T based on the value of the element e.
If you navigate the Iterable map function definition you will see that:
Iterable<T> map <T>(
T f(
E e
)
)
So I wanna sharpen my answer starting with the exact map<T> function of the OP and then swich to a more complex example.
Just to clarify all these let's take a concrete class of the Iterable class, the Set class choosing a Set of type String in such a scenario:
Set<String> mySet = Set();
for (int i=0; i++<5;) {
mySet.add(i.toString());
}
var myNewSet = mySet.map((currentValue) => (return "new" + currentValue));
for (var newValue in myNewSet) {
debugPrint(newValue);
}
Here I've got a Set of String Set<String> and I want another Set of String Set<String> so that the value is the same value of the original map, but sorrounded with a prefix of "new:". And for that we could easily use the map<T> along with the closure it wants as paraemters.
The function passed as closure is
(currentValue) => ("new:" + currentValue)
And if we want we could write it also like that:
(currentValue) {
return "new:" + currentValue;
}
or even pass a function like that:
String modifySetElement(String currentValue) {
return "new:" + currentValue;
}
var myNewSet = mySet.map((value) => ("new:" + value));
var myNewSet = mySet.map((value) {return "new:" + value;});
var myNewSet = mySet.map((value) => modifySetElement("new:" + value));
And this means that the parameter of the function (closure) is the String value of the element E of the Set we're modifying.
We don't even have to specify the type because its inferred by method definition, that's one of the power of generics.
The function (closure) will be applied to all the Set's elements once at a time, but you write it once as a closure.
So summarising:
T is String
E is the element we are dealing with inside of the function
f is our closure
Let's go deeper with a more complex example. We'll now deal with the Dart Map class.
Its map function is define like that:
map<K2, V2>(MapEntry<K2, V2> f(K key, V value)) → Map<K2, V2>
So in this case the previous first and third T is (K2, V2) and the return type of the function f (closure), that takes as element E parameter the pair K and V (that are the key and value of the current MapEntry element of the iteration), is a type of MapEntry<K2, V2> and is the previous second T.
The whole function then return a new Map<K2, V2>
The following is an actual example with Map:
Map<int, String> myMap = Map();
for (int i=0; i++<5;) {
myMap[i] = i.toString();
}
var myNewMap = myMap.map((key, value) => (MapEntry(key, "new:" + value)));
for (var mapNewEntry in myNewMap.entries) {
debugPrint(mapNewEntry.value);
}
In this example I've got a Map<int, String> and I want another Map<int, String> so that (like before) the value is the same value of the original map, but sorrounded with a prefix of "new:".
Again you could write the closure (your f function) also in this way (maybe it highlights better the fact that it's a fanction that create a brand new MapEntry based on the current map entry value).
var myNewMap = myMap.map((key, value) {
String newString = "new:" + value;
return MapEntry(key, newString);
});
All these symbols are called Generics because they are generic placeholder that correspond to a type or another based on the context you are using them.
That's an extract from the above link:
Using generic methods
Initially, Dart’s generic support was limited to classes. A newer syntax, called generic methods, allows
type arguments on methods and functions:
T first<T>(List<T> ts) {
// Do some initial work or error checking, then...
T tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
Here the generic type parameter on first () allows you to use the
type argument T in several places:
In the function’s return type (T). In the type of an argument
(List<T>). In the type of a local variable (T tmp).
Follow this link for Generics name conventions.
I need clarity on how objects are declared and assigned a definition in F#.
What's happening in this code?
let service = {
new IService with
member this.Translate(_) = raise error }
My guess is we're creating an object that will implement some interface on the fly even though there is no actual class that's backing this object. Hence, we're removing the ceremony involved with creating an object by not having to declare a separate class to use it. In this case, we're minimizing the ceremony involved for implementing a mock object that could be used within a unit test.
Is my understanding accurate?
I tried to research my question and found the specification for F# 3.0 (Section - 6.3.8 Object Expressions)
6.3.8 Object Expressions An expression of the following form is an object expression: { new ty0 args-expropt object-members interface
ty1 object-members1 … interface tyn object-membersn } In the case
of the interface declarations, the object-members are optional and are
considered empty if absent. Each set of object-members has the form:
with member-defns endopt Lexical filtering inserts simulated $end
tokens when lightweight syntax is used. Each member of an object
expression members can use the keyword member, override, or default.
The keyword member can be used even when overriding a member or
implementing an interface.
For example:
let obj1 =
{ new System.Collections.Generic.IComparer<int> with
member x.Compare(a,b) = compare (a % 7) (b % 7) }
You can get a pretty good picture of what is happening behind the scenes if you look at the generated IL using a decompiler like ILSpy. For the example involving IComparer, it generates a hidden class, which implements the interface:
internal sealed class obj1#2 : IComparer<int> {
public obj1#2() : this() { }
int IComparer<int>.System-Collections-Generic-IComparer(int x, int y) {
int num = x % 7;
int num2 = y % 7;
if (num < num2) { return -1; }
return (num > num2) ? 1 : 0;
}
}
Inside the body of the method, it then creates a new instance:
IComparer<int> obj1 = new obj1#2();
Having a simple class as follows is considered a static warning, why?
operator 'negate' should return numeric type
class Vector {
final int x,y;
const Vector(this.x, this.y);
Vector operator +(Vector v) { // Overrides + (a + b).
return new Vector(x + v.x, y + v.y);
}
Vector operator -(Vector v) { // Overrides - (a - b).
return new Vector(x - v.x, y - v.y);
}
Vector operator negate() { // Overrides unary negation (-a).
return new Vector(-x,-y);
}
String toString() => '($x,$y)';
}
main() {
final v = new Vector(2,3);
final w = new Vector(2,2);
assert((-v).x == -2 && (-v).y == -3); // -v == (-2,-3)
}
As of 8/21/2012 the Dart Specification Section 7.1.2 Operators, considers defining negate with a possible nullary type as bad style and should report to the user a static warning.
Defining a nullary method named negate or a binary method named equals
will have the same effect as dening an operator but is considered bad
style, and will cause a static warning.
[...]
It is a static warning if the return type of the user-declared
operator []= is explicitly declared and not void. It is a static
warning if the return type of the user-declared operator equals is
explicitly declared and is not bool. It is a static warning if the
return type of the user-declared operator negate is explicitly
declared and not a numerical type.
We have an open bug for this: http://code.google.com/p/dart/issues/detail?id=3416