In Dart, I have a class like this that protects _x behind getters/setters which allows me to control changes to _x:
//in a.dart
class A {
int _x;
int get x => _x;
set x(int value) {
bool validation_ok=true;
//do some validation/processing
if (validation_ok) {
_x = value;
//perform side effects that should happen every time _x changes e.g. save to SharedPreferences
print('Validated $value and side effects performed');
}
}
}
//in amain.dart
import 'a.dart';
void main() {
A a = A();
a.x = 5; //if validation successful stores 5 to _x and performs side effects
print(a.x); //prints 'Validated 5 and side effects performed' and then '5'
}
But what can I do if instead of an int, I want to protect a List or an object?
//in b.dart
class B {
List<int> _y;
List<int> get y => _y;
set y(List<int> value) {
bool validation_ok=true;
//do some validation/processing
if (validation_ok) {
_y = value;
//perform side effects that should happen every time _y changes e.g. save to SharedPreferences
print('Validated $value and side effects performed');
}
}
}
//in bmain.dart
import 'b.dart';
void main() {
B b = B();
b.y = [5]; //if validation successful stores [5] to _y and performs side effects
print(b.y); //prints 'Validated [5] and side effects performed' and '[5]'
b.y.add(6); //now _y is [5,6] but no validation was done on 6 and no side effects performed
print(b.y); //prints '[5,6]' only
}
Notice that in bmain.dart the line b.y.add(6) adds to the private list without going through the setter. How can I ensure no such access is permitted and any changes to the private list or object are controlled?
You cannot expose a mutable object and be certain that it's not changed by someone else.
That leaves your options as:
Not exposing the object at all.
Exposing a view of the object which does not allow modification.
The former is not as bad as it sounds. If you give your container class members which accesses the members of the protected object, then you don't have to expose the object itself. (It might even be a good idea, q.v. "the law of Demeter").
The latter would mean wrapping the real object in a view adapter which throws if you want to make changes. For a List, you can use an UnmodifiableListView. For other classes, you might need to write one yourself.
This solution is obviously only skin deep. Code with access to the unmodifiable list cannot change which objects are elements of the list, but if those objects are themselves mutable, then they can be changed as well.
Usually protecting the list itself is what you need.
Related
I'm trying to aggregate (per key) a streaming data source in Apache Beam (via Scio) using a stateful DoFn (using #ProcessElement with #StateId ValueState elements). I thought this would be most appropriate for the problem I'm trying to solve. The requirements are:
for a given key, records are aggregated (essentially summed) across all time - I don't care about previously computed aggregates, just the most recent
keys may be evicted from the state (state.clear()) based on certain conditions that I control
Every 5 minutes, regardless if any new keys were seen, all keys that haven't been evicted from the state should be outputted
Given that this is a streaming pipeline and will be running indefinitely, using a combinePerKey over a global window with accumulating fired panes seems like it will continue to increase its memory footprint and the amount of data it needs to run over time, so I'd like to avoid it. Additionally, when testing this out, (maybe as expected) it simply appends the newly computed aggregates to the output along with the historical input, rather than using the latest value for each key.
My thought was that using a StatefulDoFn would simply allow me to output all of the global state up until now(), but it seems this isn't a trivial solution. I've seen hintings at using timers to artificially execute callbacks for this, as well as potentially using a slowly growing side input map (How to solve Duplicate values exception when I create PCollectionView<Map<String,String>>) and somehow flushing this, but this would essentially require iterating over all values in the map rather than joining on it.
I feel like I might be overlooking something simple to get this working. I'm relatively new to many concepts of windowing and timers in Beam, looking for any advice on how to solve this. Thanks!
You are right that Stateful DoFn should help you here. This is a basic sketch of what you can do. Note that this only outputs the sum without the key. It may not be exactly what you want, but it should help you move forward.
class CombiningEmittingFn extends DoFn<KV<Integer, Integer>, Integer> {
#TimerId("emitter")
private final TimerSpec emitterSpec = TimerSpecs.timer(TimeDomain.PROCESSING_TIME);
#StateId("done")
private final StateSpec<ValueState<Boolean>> doneState = StateSpecs.value();
#StateId("agg")
private final StateSpec<CombiningState<Integer, int[], Integer>>
aggSpec = StateSpecs.combining(
Sum.ofIntegers().getAccumulatorCoder(null, VarIntCoder.of()), Sum.ofIntegers());
#ProcessElement
public void processElement(ProcessContext c,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) throws Exception {
if (SOME CONDITION) {
countValueState.clear();
doneState.write(true);
} else {
countValueState.addAccum(c.element().getValue());
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
#OnTimer("emitter")
public void onEmit(
OnTimerContext context,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) {
Boolean isDone = doneState.read();
if (isDone != null && isDone) {
return;
} else {
context.output(aggState.getAccum());
// Set the timer to emit again
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
}
Happy to iterate with you on something that'll work.
#Pablo was indeed correct that a StatefulDoFn and timers are useful in this scenario. Here is the with code I was able to get working.
Stateful Do Fn
// DomainState is a custom case class I'm using
type DoFnT = DoFn[KV[String, DomainState], KV[String, DomainState]]
class StatefulDoFn extends DoFnT {
#StateId("key")
private val keySpec = StateSpecs.value[String]()
#StateId("domainState")
private val domainStateSpec = StateSpecs.value[DomainState]()
#TimerId("loopingTimer")
private val loopingTimer: TimerSpec = TimerSpecs.timer(TimeDomain.EVENT_TIME)
#ProcessElement
def process(
context: DoFnT#ProcessContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value from potentially null values
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
stateKey.write(key)
stateValue.write(value)
if (flushState(value)) {
context.output(KV.of(key, value))
}
} else {
stateValue.clear()
}
}
#OnTimer("loopingTimer")
def onLoopingTimer(
context: DoFnT#OnTimerContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value checking for nulls
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
if (flushState(value)) {
context.output(KV.of(key, value))
}
}
}
}
With pipeline
sc
.pubsubSubscription(...)
.keyBy(...)
.withGlobalWindow()
.applyPerKeyDoFn(new StatefulDoFn())
.withFixedWindows(
duration = Duration.standardMinutes(5),
options = WindowOptions(
accumulationMode = DISCARDING_FIRED_PANES,
trigger = AfterWatermark.pastEndOfWindow(),
allowedLateness = Duration.ZERO,
// Only take the latest per key during a window
timestampCombiner = TimestampCombiner.END_OF_WINDOW
))
.reduceByKey(mostRecentEvent())
.saveAsCustomOutput(TextIO.write()...)
I use Piece class as Map's key.
But when this code ran, error occured Uncaught exception:
C.JSNull_methods.$indexSet is not a function.
class Piece {
int type;
Piece(this.type);
}
void main() {
Map<Piece, int> hand;
hand[Piece(5)] = 5;
if (hand.containsKey(Piece(5))) {
print("contains");
}
print('${hand[Piece(5)]}');
}
In dart-lang, how can I use class as Map's key?
First, the error you got has nothing to do with using types as keys but are before you never initialize the hand variable. So you need to do this:
Map<Piece, int> hand = {};
Now, you will not get the exception but your code will properly not work as expected since hand.containsKey(Piece(5)) will return false and print('${hand[Piece(5)]}') will return null.
This is because the map Map<Piece, int> are not using the Type as key but instead objects of the type Piece. So if we take your code here:
Map<Piece, int> hand = {};
hand[Piece(5)] = 5;
if (hand.containsKey(Piece(5))) {
print("contains");
}
print('${hand[Piece(5)]}');
You are here creating a new object instance of the Piece type each type you are writing "Piece(5)". Since each of this objects will be a separate instance of a Piece then you will not receive the value 5 you have saved because the value 5 has been saved for a different object than you are requesting.
There are multiple solutions for that and I don't know which one are the best for you. But the simple solution in this case is to either only creating one instance of Piece and reuse that:
void main() {
Map<Piece, int> hand = {};
final piece = Piece(5);
hand[piece] = 5;
if (hand.containsKey(piece)) {
print("contains");
}
print('${hand[piece]}');
}
Or make a const constructor for your Piece class so instances with the same arguments are made into the same object. This solution requires that the int type are final since you cannot edit a const constructed object (since it is constant):
class Piece {
final int type;
const Piece(this.type);
}
void main() {
Map<Piece, int> hand = {};
hand[const Piece(5)] = 5;
if (hand.containsKey(const Piece(5))) {
print("contains");
}
print('${hand[const Piece(5)]}');
}
Note that you need to prefix you object instantiation with const like "const Piece(5)" each time you want a instance where you are sure it will returns the same object for the same arguments.
I noticed over and over again that if i did something like:
#property int x = 1;
...code...
set("x", 2);
print(x);
it will show it as 1. The reason was that it doesnt necessarily execute this immediately. So if i ever set a property I have always made it a point to never use it through the rest of the function. I always believed that set was just called at the end of the current execution.
When dealing with functions a similar approach happens.
It will be assigned but outside the scope of the function. So i would try something like observing it and awaiting for a change in state.
#property Function myFunc = null;
#reflectable
_myFunc(_) => true;
attached(){
set("myFunc", _myFunc);
print("is myFunc null: ${myFunc == null}");
}
will return True.
So I then would also try:
#Observe("myFunc")
functionObservation(_)=>print("Function Called");
but this would not fire.
My desired end state is that when i pass myFunc into another polymer element and try to do stuff with it on that class' attached, as such:
#property Function execFunc = null;
attached(){
if(execFunc != null)
execFunc();
}
so when passing it into another component there are issues.
I am not 100% sure if this is a life cycle issue, or a set error but it seems that when i do it in attached, OR define a future:
attached(){
new Future((){
execFunc()
});
}
It will still not seem to be assigned.
you should rewrite your props as setters and getters and use the setter for notification :
int _myProp;
#reflectable
int get myProp => _myProp;
set myProp(v) {
_myProp = v;
notifyPath('myProp');
}
Or use something like autonotify to automatically get properties notified for you.
This way you are guaranteed that this code always stamp 5 in the console:
myProp=5
print("${myProp}");
regardless the possible async polymer setter behavior.
I would like to pass a primitive (int, bool, ...) by reference. I found a discussion about it (paragraph "Passing value types by reference") here: value types in Dart, but I still wonder if there is a way to do it in Dart (except using an object wrapper) ? Any development ?
The Dart language does not support this and I doubt it ever will, but the future will tell.
Primitives will be passed by value, and as already mentioned here, the only way to 'pass primitives by reference' is by wrapping them like:
class PrimitiveWrapper {
var value;
PrimitiveWrapper(this.value);
}
void alter(PrimitiveWrapper data) {
data.value++;
}
main() {
var data = new PrimitiveWrapper(5);
print(data.value); // 5
alter(data);
print(data.value); // 6
}
If you don't want to do that, then you need to find another way around your problem.
One case where I see people needing to pass by reference is that they have some sort of value they want to pass to functions in a class:
class Foo {
void doFoo() {
var i = 0;
...
doBar(i); // We want to alter i in doBar().
...
i++;
}
void doBar(i) {
i++;
}
}
In this case you could just make i a class member instead.
No, wrappers are the only way.
They are passed by reference. It just doesn't matter because the "primitive" types don't have methods to change their internal value.
Correct me if I'm wrong, but maybe you are misunderstanding what "passing by reference" means? I'm assuming you want to do something like param1 = 10 and want this value to still be 10 when you return from your method. But references aren't pointers. When you assign the parameter a new value (with = operator), this change won't be reflected in the calling method. This is still true with non-primitive types (classes).
Example:
class Test {
int val;
Test(this.val);
}
void main() {
Test t = new Test(1);
fn1(t);
print(t.val); // 2
fn2(t);
print(t.val); // still 2, because "t" has been assigned a new instance in fn2()
}
void fn1(Test t) {
print(t.val); // 1
t.val = 2;
}
void fn2(Test t) {
t = new Test(10);
print(t.val); // 10
}
EDIT
I tried to make my answer more clear, based on the comments, but somehow I can't seem to phrase it right without causing more confusion. Basically, when someone coming from Java says "parameters are passed by reference", they mean what a C/C++ developer would mean by saying "parameters are passed as pointers".
As dart is compiled into JavaScript, I tried something that works for JS, and guess what!? It worked for dart!
Basically, what you can do is put your value inside an object, and then any changes made on that field value inside that function will change the value outside that function as well.
Code (You can run this on dartpad.dev)
main() {
var a = {"b": false};
print("Before passing: " + a["b"].toString());
trial(a);
print("After passing: " + a["b"].toString());
}
trial(param) {
param["b"] = true;
}
Output
Before passing: false
After passing: true
One of the way to pass the variables by reference by using the values in List. As arrays or lists are Pass by reference by default.
void main() {
List<String> name=['ali' ,'fana'];
updatename(name);
print(name);
}
updatename(List<String> name){
name[0]='gufran';
}
Try this one, This one of the simplest way to pass by reference.
You can use ValueNotifier
And, you can pass it as ValueListenable to classes or methods that needs to know up-to-date value, but should not edit it:
class Owner {
final theValue = ValueNotifier(true);
final user = User(theValue);
...
}
class User {
final ValueListeneble<bool> theValue;
User(this.theValue);
...
}
It provides more functionality than actually needed, but solves the problem.
If ValueNotifier + ValueListenable do not work for you (you want to make sure the client does not listen to every change of the value, or your package is pure Dart package and thus cannot reference Flutter libraries), use a function:
class Owner {
int _value = 0;
int getValue() => _value;
void increase() => _value++;
}
void main() {
final owner = Owner();
int Function() obtainer = owner.getValue;
print(obtainer());
owner.increase();
print(obtainer());
}
Output will be:
0
1
This approach has memory usage related downside: the obtainer will hold the reference to the owner, and this, even if owner is already not referenced, but obtainer is still reachable, owner will be also reachable
and thus will not be garbage collected.
If you do not want the downside, pass the smaller container than the entire owner:
import 'package:flutter/foundation.dart';
class ListenableAsObtainer<T> implements ValueObtainer<T> {
ListenableAsObtainer(this._listenable);
final ValueListenable<T> _listenable;
#override
T get value => _listenable.value;
}
class FunctionAsObtainer<T> implements ValueObtainer<T> {
FunctionAsObtainer(this._function);
final T Function() _function;
#override
T get value => _function();
}
class ValueAsObtainer<T> implements ValueObtainer<T> {
ValueAsObtainer(this.value);
#override
T value;
}
/// Use this interface when the client needs
/// access to the current value, but does not need the value to be listenable,
/// i.e. [ValueListenable] would be too strong requirement.
abstract class ValueObtainer<T> {
T get value;
}
The usage of FunctionAsObtainer will still result in holding the owner from garbage collection, but two other options will not.
Just to make it clear:
void main() {
var list1 = [0,1,2];
var modifiedList1 = addMutable(list1, 3);
var list2 = [0,1,2];
var modifiedList2 = addImmutable(list2, 3);
print(list1);
print(modifiedList1);
print(list2);
print(modifiedList2);
}
List<int> addMutable(List<int> list, int element){
return list..add(element);
}
List<int> addImmutable(List<int> list, int element){
return [...list, element];
}
Output:
[0, 1, 2, 3]
[0, 1, 2, 3]
[0, 1, 2]
[0, 1, 2, 3]
All variables are passed by value. If a variable contains a primitive (int, bool, etc.), that's it. You got its value. You can do with it whatever you want, it won't affect the source value. If a variable contains an object, what it really contains is a reference to that object.
The reference itself is also passed by value, but the object it references is not passed at all. It just stayed where it was. This means that you can actually make changes to this very object.
Therefore, if you pass a List and if you .add() something to it, you have internally changed it, like it is passed by reference. But if you use the spread operator [...list], you are creating a fresh new copy of it. In most cases that is what you really want to do.
Sounds complicated. Isn't really. Dart is cool.
Have been seeing the term "Expando" used recently with Dart. Sounds interesting. The API did not provide much of a clue to me.
An example or two could be most helpful!
(Not sure if this is related, but I am most anxious for a way to add methods (getters) and/or variables to a class. Hoping this might be a key to solving this problem. (hint: I am using the Nosuchmethod method now and want to be able to return the value of the unfound method.))
Thanks in advance,
_swarmii
Just to clarify the difference between expando and maps: as reported in the groups, expando has weak references.
This means that a key can be garbage collected even if it's still present in the expando (as long as there are no other references to it).
For all other intents and purposes it's a map.
Expandos allow you to associate objects to other objects. One very useful example of this is an HTML DOM element, which cannot itself be sub-classed. Let's make a top-level expando to add some functionality to an element - in this case a Function signature given in the typedef statement:
typedef CustomFunction(int foo, String bar);
Expando<CustomFunction> domFunctionExpando = new Expando<CustomFunction>();
Now to use it:
main(){
// Assumes dart:html is imported
final myElement = new DivElement();
// Use the expando on our DOM element.
domFunctionExpando[myElement] = someFunc;
// Now that we've "attached" the function to our object,
// we can call it like so:
domFunctionExpando[myElement](42, 'expandos are cool');
}
void someFunc(int foo, String bar){
print('Hello. $foo $bar');
}
I played with it a little bit. Here's what I've got.
import 'dart:html';
const String cHidden = 'hidden';
class ExpandoElement {
static final Expando<ExpandoElement> expando =
new Expando<ExpandoElement>("ExpandoElement.expando");
final Element element;
const ExpandoElement._expand(this.element);
static Element expand(Element element) {
if (expando[element] == null)
expando[element] = new ExpandoElement._expand(element);
return element;
}
// bool get hidden => element.hidden; // commented out to test noSuchMethod()
void set hidden(bool hidden) {
if (element.hidden = hidden)
element.classes.add(cHidden);
else
element.classes.remove(cHidden);
}
noSuchMethod(InvocationMirror invocation) => invocation.invokeOn(element);
}
final Expando<ExpandoElement> x = ExpandoElement.expando;
Element xquery(String selector) => ExpandoElement.expand(query(selector));
final Element input = xquery('#input');
void main() {
input.classes.remove(cHidden);
assert(!input.classes.contains(cHidden));
input.hidden = true;
assert(x[input].hidden); // Dart Editor warning here, but it's still true
assert(!input.classes.contains(cHidden)); // no effect
input.hidden = false;
assert(!x[input].hidden); // same warning, but we'll get input.hidden via noSuchMethod()
assert(!input.classes.contains(cHidden));
x[input].hidden = true;
assert(input.hidden); // set by the setter of ExpandoElement.hidden
assert(input.classes.contains(cHidden)); // added by the setter
assert(x[input].hidden);
assert(x[input].classes.contains(cHidden)); // this is input.classes
x[input].hidden = false;
assert(!input.hidden); // set by the setter
assert(!input.classes.contains(cHidden)); // removed by the setter
assert(!x[input].hidden);
assert(!x[input].classes.contains(cHidden));
// confused?
assert(input is Element);
assert(x[input] is! Element); // is not
assert(x[input] is ExpandoElement);
assert(x is Expando<ExpandoElement>);
}