Can JavaFX deal with cyclic property binding graphs? - binding

Let's consider three JavaFX properties: A B C.
Now let's bind them bidirectionally into a triangle (A-B, B-C, A-C).
Now let's imagine that we modify the value of A.
Does this lead to problems (e.g. infinite recursion)?
Can JavaFX deal with such cyclic binding graphs? If yes, how does it do that ?
Thanks for reading.

Try it...
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class CyclicPropertyTest {
public static void main(String[] args) {
IntegerProperty x = new SimpleIntegerProperty(0);
IntegerProperty y = new SimpleIntegerProperty(1);
IntegerProperty z = new SimpleIntegerProperty(2);
x.addListener((obs, oldValue, newValue) -> System.out.printf("x changed from %d to %d %n", oldValue, newValue));
y.addListener((obs, oldValue, newValue) -> System.out.printf("y changed from %d to %d %n", oldValue, newValue));
z.addListener((obs, oldValue, newValue) -> System.out.printf("z changed from %d to %d %n", oldValue, newValue));
x.bindBidirectional(y);
y.bindBidirectional(z);
z.bindBidirectional(x);
x.set(1);
}
}
The listeners are only notified if the value of the property changes. When x is set to 1, this results in y being set to 1, which results in z being set to 1. These fire the listeners. Since z has changed, this results (at least conceptually) in x being set to 1, but since it's already 1, no listeners are notified and hence the cycle terminates.

Related

Instantiating a subclass in a superclass?

abstract class A {
A(this.x, this.y);
// error: abstract classes cannot be instantiated
//
// another issue: even if you used a base concrete class
// to perform this operation, it would lose type information.
A copy({int? x, int? y}) => A(x ?? this.x, y ?? this.y);
final int x;
final int y;
}
class B extends A {
// Forced to implement copy and similar
// methods on all classes that extend A,
// which is problematic when that number
// is large or changes are necessary.
}
Is there a way to solve this problem or do I have to essentially rewrite the same code for all classes that extend A?
You can, but it requires you to do quite a lot of the work
you are asking to avoid:
class A<T extends A<T>> {
final T Function(int, int) _constructor;
final int x;
final int y;
A._(this._constructor, this.x, this.y);
T copy({int? x, int? y}) => _constructor(x ?? this.x, y ?? this.y);
}
class B extends A<B> {
B(int x, int y) : super._((int x, int y) => B(x, y), x, y);
}
(The code will get shorter when Dart gets constructor tear-offs, then it's just, super._(B, x, y);.)
You cannot, currently, inherit constructors, and you can't create an instance of a type that you don't know yet (because constructors are not inherited, so you don't know if the constructor exists). The only way to abstract over actual behavior (which code to run) is to capture it in a closure and pass it as a function.

How to pass a function as an optional parameter with a default value in a function in dart?

This is what I want to implement:
void fun({
bool Function(int i) predicate = (i) => false,
}) {
// do something with 'predicate(something)'
}
But I am getting the error:
The default value of an optional parameter must be constant.dart(non_constant_default_value).
I was able to get arround this error with the following:
bool falsePredicate(int i) => false;
void fun({
bool Function(int i) predicate = falsePredicate,
}) {
// do something with 'predicate(something)'
}
But now the question becomes, why can't I directly create a default function value as in the first set of code? There seems to be no difference between the first and the second cases. How is the function given in the first approach not constant?
As #Noah has pointed to the git discussion, the dart language has this missing piece of compile-time constant functions, which eventually leads to this problem.
Check this post: https://github.com/dart-lang/language/issues/1048
As the post shows, the issue has been raised in mid-2012 and now it's been 8+ years. So the hopes of this being available in the near feature is very less.
However few alternative solutions till then:
Option 1 (separate method):
class ServiceProvider {
static bool falsePredicate(int i) => false;
void fun({
bool Function(int i) predicate = falsePredicate,
}) {
// do something with 'predicate(something)'
}
}
Option 2 (Null checking while using the predicate)
class ServiceProvider {
void fun({
bool Function(int i)? predicate,
}) {
int val = 55; // for demonstration
predicate?.call(val); // Call only if the predicate is non-null
}
}
Option 3 (Only for class constructors)
class ServiceProvider {
final bool Function(int i) _predicate;
ServiceProvider ({bool Function(int i)? predicate})
: _predicate = predicate ?? ((i) => false);
void fun() {
int val = 55;
_predicate(5); // No null check is needed. The predicate is always non-null
}
}

Setting a Timer to the minimum timestamp seen

I would like to set a Timer in Event time that fires based on the smallest timestamp seen in the elements within my DoFn.
For performance reasons the Timer API does not support a read() operation, which for the vast majority of use cases is not a required feature. In the small set of use cases where it is needed, for example when you need to set a Timer in EventTime based on the smallest timestamp seen in the elements within a DoFn, we can make use of a State object to keep track of the value.
Java (SDK 2.10.0)
// In this pattern, a Timer is set to fire based on the lowest timestamp seen in the DoFn.
public class SetEventTimeTimerBasedOnEarliestElementTime {
private static final Logger LOG = LoggerFactory
.getLogger(SetEventTimeTimerBasedOnEarliestElementTime.class);
public static void main(String[] args) {
// Create pipeline
PipelineOptions options = PipelineOptionsFactory.
fromArgs(args).withValidation().as(PipelineOptions.class);
// We will start our timer at a fixed point
Instant now = Instant.parse("2000-01-01T00:00:00Z");
// ----- Create some dummy data
// Create 3 elements, incrementing by 1 minute
TimestampedValue<KV<String, Integer>> time_1 = TimestampedValue.of(KV.of("Key_A", 1), now);
TimestampedValue<KV<String, Integer>> time_2 = TimestampedValue
.of(KV.of("Key_A", 2), now.plus(Duration.standardMinutes(1)));
TimestampedValue<KV<String, Integer>> time_3 = TimestampedValue
.of(KV.of("Key_A", 3), now.plus(Duration.standardMinutes(2)));
Pipeline p = Pipeline.create(options);
// Apply a fixed window of duration 10 min and Sum the results
p.apply(Create.timestamped(time_3, time_2, time_1)).apply(
Window.<KV<String, Integer>>into(FixedWindows.<Integer>of(Duration.standardMinutes(10))))
.apply(ParDo.of(new StatefulDoFnThatSetTimerBasedOnSmallestTimeStamp()));
p.run();
}
/**
* Set timer to the lowest value that we see in the stateful DoFn
*/
public static class StatefulDoFnThatSetTimerBasedOnSmallestTimeStamp
extends DoFn<KV<String, Integer>, KV<String, Integer>> {
// Due to performance considerations there is no read on a timer object.
// We make use of this Long value to keep track.
#StateId("currentTimerValue") private final StateSpec<ValueState<Long>> currentTimerValue =
StateSpecs.value(BigEndianLongCoder.of());
#TimerId("timer") private final TimerSpec timer = TimerSpecs.timer(TimeDomain.EVENT_TIME);
#ProcessElement public void process(ProcessContext c,
#StateId("currentTimerValue") ValueState<Long> currentTimerValue,
#TimerId("timer") Timer timer) {
Instant timeStampWeWantToSet = c.timestamp();
//*********** Set Timer
// If the timer has never been set then we set it.
// If the timer has been set but is larger than our current value then we set it.
if (currentTimerValue.read() == null || timeStampWeWantToSet.getMillis() < currentTimerValue
.read()) {
timer.set(timeStampWeWantToSet);
currentTimerValue.write(timeStampWeWantToSet.getMillis());
}
}
#OnTimer("timer") public void onMinTimer(OnTimerContext otc,
#StateId("currentTimerValue") ValueState<Long> currentTimerValue,
#TimerId("timer") Timer timer) {
// Reset the currentTimerValue
currentTimerValue.clear();
LOG.info("Timer # {} fired", otc.timestamp());
}
}
}

Send multiple arguments to the compute function in Flutter

I was trying to use the compute function in Flutter.
void _blockPressHandler(int row, int col) async {
// Called when user clicks any block on the sudoku board . row and col are the corresponding row and col values ;
setState(() {
widget.selCol = col;
}
});
bool boardSolvable;
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku , widget.board , widget.size) ;
}
isBoardInSudoku is a static method of class SudokuAlgorithm. Its present in another file. Writing the above code , tells me that
error: The argument type '(List<List<int>>, int) → bool' can't be assigned to the parameter type '(List<List<int>>) → bool'. (argument_type_not_assignable at [just_sudoku] lib/sudoku/SudokuPage.dart:161)
How do i fix this? Can it be done without bringing the SudokuAlgorithm class's methods out of its file ? How to send multiple arguments to the compute function ?
static bool isBoardInSudoku(List<List<int>>board , int size ){ } is my isBoardInSudoku function.
Just put the arguments in a Map and pass that instead.
There is no way to pass more than one argument to compute because it is a convenience function to start isolates which also don't allow anything but a single argument.
Use a map. Here is an example:
Map map = Map();
map['val1'] = val1;
map['val2'] = val2;
Future future1 = compute(longOp, map);
Future<double> longOp(map) async {
var val1 = map['val1'];
var val2 = map['val2'];
...
}
In OOP and in general, it is more elegant to create a class for that with fields you need, that gives you more flexibility and less hassle with hardcoded strings or constants for key names.
For example:
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku , widget.board , widget.size) ;
replace with
class BoardSize{
final int board;
final int size;
BoardSize(this.board, this.size);
}
...
boardSolvable = await compute(SudokuAlgorithm.isBoardInSudoku, BoardSize(widget.board, widget.size)) ;
Use a Tuple
Here is some example code from my app:
#override
Future logChange(
String recordId, AttributeValue newValue, DateTime dateTime) async {
await compute(
logChangeNoCompute, Tuple2<String, AttributeValue>(recordId, newValue));
}
Future<void> logChangeNoCompute(Tuple2<String, AttributeValue> tuple) async {
_recordsById[tuple.item1]!.setAttributeValue(tuple.item2);
await storage.setItem(AssetsFileName, toJson());
}
You can have a function whose only argument is a Map so that you can pass multiple parameters by passing a Map with properties and values. However, the problem that I'm encountering now is that I cannot pass functions. If the value of a Map's property is a function I get an error when I run the compute function.
This example works(keep in mind that I've imported libraries and that's the reason why some functions and classes definitions aren't in this example)
Future<List<int>> getPotentialKeys({
#required int p,
#required int q,
})async{
return await compute(allKeys,{
"p" : p,
"q" : q,
});
}
List<int> allKeys(Map<String,dynamic> parameters){
AdvancedCipherGen key = AdvancedCipherGen();
List<int> possibleE = key.step1(p: parameters["p"], q: parameters["q"]);
return possibleE;
}
This does not work(same thing with a function as the value of a property thows an error)
Future<List<int>> getPotentialKeys({
#required int p,
#required int q,
#required Function(AdvancedCipherGen key) updateKey,
})async{
return await compute(allKeys,{
"p" : p,
"q" : q,
"updateKey" : updateKey,
});
}
List<int> allKeys(Map<String,dynamic> parameters){
AdvancedCipherGen key = AdvancedCipherGen();
List<int> possibleE = key.step1(p: parameters["p"], q: parameters["q"]);
//TODO: Update the key value through callback
parameters["updateKey"](key);
return possibleE;
}
easily use a Class, you can Also Use Map or List But using class is Better and Cleaner
class MyFunctionInput{
final int first;
final int second;
MyFunctionInput({required this.first,required this.second});
}
change your function like this
doSomething(MyFunctionInput input){
}
and use it like below
compute(doSomething,MyFunctionInput(first: 1, second: 4));

How to share expado or weakmap properties (or other options if available) cleanly

Edited as the original post was impossible to understand...sorry guys.
Why did I want to use expando/weakmap?
I had a custom layout/sizer element that resizes children. By using expando, I wanted to control that resizing behavior without creating a strong reference.
e.g.
var l1 = new Layout();
var e1 = new DivElement();
e1.text = 'The size of this element will remain as is';
//setting property prior to appending to avoid resizing
l1.add_resizable_attribute(e1,false);
l1.append(e1);
print(l1.isFrozen(e1));//returns true
I found a better solution, but I want to know how to map an object with data in Dart and share it across multiple objects safely and dynamically manage that.
Why did I want to share a expando property?
My Layout element may contain another layout element and form a tree. Allowing access to the expando property of an object anywhere in the tree seems a rational thing to do.
e.g.
var l1 = new Layout();
var l2 = new Layout();
var e1 = new DivElement();
e1.text = 'The size of this element will remain as is';
l1.add_resizable_attribute(e1,false);
print(l1.isFrozen(e1));//returns true
print(l2.isFrozen(e1));//returns false as l2 is not part of the tree
l1.append(l2)//forming a tree
print(l1.isFrozen(e1));//returns true
print(l2.isFrozen(e1));// returns true on l2 as well
l2.add_resizable_attribute(e1,true);
print(l1.isFrozen(e1));//changed on l2 but returns false on l1 as well
What is the problem?
Cannot merge or split expando instances.
Potential solutions
Give up on expando property/weakmap (works only on a dom element)
new HtmlElement()..dataset['allow_layout_resize_this']='false';
Did not think of this, but the simplest.
Reflection
Obvious but currently needs 'dart:mirror' to make this work with any class instances.
Traversing the tree
Complex and ugly.
class Layout extends HtmlElement{
Expando<bool> _freeze_resizing = new Expando<bool>();
Layout.created():super.created();
void add_resizable_attribute(HtmlElement e, bool isResizable) {
_freeze_resizing[e] = !isResizable;
}
isFrozen(HtmlElement e,
{bool search_down: true,
bool search_up: true
}) {
///resize allowed by default
bool v;
if (search_up) {
Layout t = _get_topmost_layout();
return t.isFrozen(
e, search_down: true,
search_up: false);
} else if (search_down) {
v = _freeze_resizing[e];
v = v == null ? false : v;
if (v) return true;
return isFrozen_in_children(e);
}
return _freeze_resizing[e];
}
bool isFrozen_in_children(e) {
for (var c in children) {
if (c is! Layout) continue;
if (c.isFrozen(
e,
search_up: false,
search_down: true
)) return true;
}
return false;
}
Layout _get_topmost_layout() {
Layout tm;
var e = this;
while (e is Layout) {
tm = e;
e = e.parent;
}
return tm;
}
void _enforce_owner_exclusive_expando(element,[bool search_up=true]){
///remove expando properties from non-owner
if(!children.contains(element)){
_freeze_resizing[element]=null;
}
void enforce_on_children(){
for(var c in children){
if(c is! Layout) continue;
c._enforce_owner_exclusive_expando(element,false);
}
}
if(search_up){
var tm = _get_topmost_layout();
tm._enforce_owner_exclusive_expando(element,false);
}else{
enforce_on_children();
}
}
append(e){
super.append(e);
_enforce_owner_exclusive_expando(element);
}
}
Generally the problem can be circumvented and Dart is still easier than javascript. This is, however, the first ever time I have found Dart frustrating.
How would you solve an issue like this?
Weakmap like structure with keys method
Yay, I've finally solved this!!!!
https://github.com/TastyCatFood/mistletoe
Has keys method
import 'package:mistletoe/mistletoe.dart';
void main(){
var m = new Mistletoe();
var t = new DateTime.now();
m.add( t, 'hi', 'bye');
print(m.keys(t));//prints 'hi'
print(m.value(t,'hi'));//prints bye;
t = null;
//With t garbage collected, m is empty now
}
Support for pseudo dynamic addition of properties
import 'package:mistletoe/mistletoe.dart';
Dynamism d = new Dynamism(expert:true);
void main(){
var o = new Object();
d.on(o).set('greetings',()=>print('hello world'));
d.on(o).invoke('greetings');//prints hello world
o = null;
//With o garbage collected, d is empty now.
}
I requested the feature here: https://github.com/dart-lang/sdk/issues/25781
published: https://pub.dartlang.org/packages/mistletoe

Resources