Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1 ... Dejavu #74

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5c77794
Support haxe 4.1
back2dos Jul 4, 2021
e82515f
Minor tweak.
back2dos Jul 7, 2021
8692116
Add API to check if tracking is needed.
back2dos Jul 18, 2021
cfefb49
Add retain/release
back2dos Jul 18, 2021
50a014c
Merge branch 'master' of https://github.com/haxetink/tink_state
back2dos Jul 18, 2021
aa8dd66
Improve dependency graph. Add dispose callback for TransformObservable.
back2dos Jul 18, 2021
311009f
Fix formatting in vscode.
back2dos Jul 18, 2021
318c2e4
Begin working on granular observability.
back2dos Jul 18, 2021
d749da2
Make #49 work for arrays.
back2dos Jul 18, 2021
d947b53
Minor.
back2dos Jul 18, 2021
a23a3e5
Delete unused stuff.
back2dos Jul 18, 2021
1350c45
Make maps multitype and granularly observable.
back2dos Jul 18, 2021
9fb34f6
Implement granular array tracking without leaking.
back2dos Jul 18, 2021
8745ca2
Move granularity to ObservableArrayView.
back2dos Jul 18, 2021
f1d7b8b
Non-leaky granular observability for maps. Add tests too.
back2dos Jul 18, 2021
bf329d9
Switch to next version of streams/testrunner. Should fix tests.
back2dos Jul 18, 2021
d437d37
Get rid of callback links in invalidation (for #69).
back2dos Jul 20, 2021
876654a
Get rid of subscriptions (for #69).
back2dos Jul 20, 2021
60cd663
Randomly move things around for a change.
back2dos Jul 20, 2021
4eb7e44
And yet more random refactoring.
back2dos Jul 20, 2021
5cd1e6b
Convolution beats correctness. Always.
back2dos Jul 20, 2021
383c32e
Avoid NPE.
back2dos Jul 20, 2021
213ab73
Revert "Get rid of subscriptions (for #69)."
back2dos Jul 20, 2021
07c67f7
Ditch the subscription type param.
back2dos Jul 20, 2021
1e56968
Focus benchmark on updates (rather than setup/teardown).
back2dos Jul 20, 2021
db3f026
One more level of indirection \o/
back2dos Jul 25, 2021
cb06a99
Make tests pass again.
back2dos Jul 25, 2021
d641c49
Tests seem to pass again after implementing #60.
back2dos Aug 3, 2021
1683e37
Haxe 4.1 compat.
back2dos Aug 3, 2021
f5e6bf8
Use final only where for constant locals.
back2dos Aug 3, 2021
deec843
Make tests slightly trickier.
back2dos Aug 3, 2021
810e5af
Don't use direct scheduler in tests when not necessary.
back2dos Aug 3, 2021
3658427
Add a rudimentary test for #60.
back2dos Aug 3, 2021
c733f98
Move pruning to better place.
back2dos Aug 3, 2021
7048c78
Resolves #73.
back2dos Aug 3, 2021
85515e3
Add tracking getter to AutoObservable.
back2dos Aug 7, 2021
15396fe
Generate a little less code.
back2dos Aug 13, 2021
61dc15b
Subscribe before polling.
back2dos Aug 15, 2021
e32baec
Wahoops.
back2dos Aug 15, 2021
244fde5
Don't trigger bindings while computing.
back2dos Aug 15, 2021
9fda3b0
Add test for #76 and make it pass.
back2dos Nov 4, 2021
33fd87f
Allow cancellation from within autorun.
back2dos Apr 10, 2022
9acb3a6
Use new Promise.never()
back2dos May 14, 2022
f8f57d7
Bump tink_core.
back2dos Jun 6, 2023
d8a0ba6
Const observables can't fire, obviously.
back2dos Jun 6, 2023
c693cf3
Remove some deprecated stuff.
back2dos Jun 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .haxerc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "4.2.0",
"version": "4.2.3",
"resolveLibs": "scoped"
}
27 changes: 15 additions & 12 deletions bench/Bench.hx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ class Bench {
});
return todos;
}
measure('create 10000 todos', () -> makeTodos(1000), 100);
var count = 1000;
measure('creating ${count} todos', () -> makeTodos(count), 100);

var todos = makeTodos(1000);
for (mode in ['direct', 'batched', 'atomic'])
measure('toggle 1000 todos [$mode]', () -> {
var todos = makeTodos(count);
for (mode in ['direct', 'batched', 'atomic']) {
var unfinishedTodoCount = Observable.auto(() -> {
var sum = 0;
for (t in todos)
if (!t.done.value) sum++;
sum;
});

var unfinishedTodoCount = Observable.auto(() -> {
var sum = 0;
for (t in todos)
if (!t.done.value) sum++;
sum;
});
var watch = unfinishedTodoCount.bind(_ -> {}, if (mode == 'batched') null else Scheduler.direct);

var watch = unfinishedTodoCount.bind(_ -> {}, if (mode == 'batched') null else Scheduler.direct);
measure('toggling ${todos.length} todos [$mode]', () -> {

function update()
for (t in todos)
Expand All @@ -38,13 +39,15 @@ class Bench {
if (mode == 'batched')
Observable.updateAll();

watch.cancel();

}, switch mode {
case 'atomic': 1000;
case 'batched': 1000;
default: 10;
});

watch.cancel();
}
}

static function measure(name, f:()->Void, ?repeat = 1) {
Expand Down
27 changes: 15 additions & 12 deletions bench/mobx-bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ function measure(name, task, repeat = 1) {
console.log(`${name} took ${(Date.now() - start) / repeat}ms (avg. of ${repeat} runs)`);
}

measure('create 10000 todos', () => createTodos(1000), 100);
let count = 1000;
measure(`creating ${count} todos`, () => createTodos(count), 100);

{
let todos = createTodos(1000);
Expand All @@ -40,24 +41,26 @@ measure('create 10000 todos', () => createTodos(1000), 100);
}

['direct', 'batched', 'atomic'].forEach(mode => {
measure(`create 1000 todos, finish all [${mode}]`, () => {
let unfinishedTodoCount = computed(() => {
return todos.reduce((count, { done }) => done ? count : count + 1, 0);
});
let unfinishedTodoCount = computed(() => {
return todos.reduce((count, { done }) => done ? count : count + 1, 0);
});

let dispose =
(mode == 'batched')
? autorun(() => unfinishedTodoCount.get(), {
scheduler: scheduler()
})
: unfinishedTodoCount.observe(x => {});
let dispose =
(mode == 'batched')
? autorun(() => unfinishedTodoCount.get(), {
scheduler: scheduler()
})
: unfinishedTodoCount.observe(x => {});

measure(`toggling ${todos.length} todos [${mode}]`, () => {

let update = (mode == 'atomic') ? transaction : f => f();
update(() => {
for (let item of todos)
item.done = !item.done;
});
dispose();
}, { atomic: 1000, batched: 1000, direct: 10 }[mode]);

dispose();
});
}
6 changes: 3 additions & 3 deletions haxe_libraries/tink_core.hxml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# @install: lix --silent download "gh://github.com/haxetink/tink_core#e0ed6c33f6f396fb83397a590bee4c3d48ab2e17" into tink_core/2.0.2/github/e0ed6c33f6f396fb83397a590bee4c3d48ab2e17
-cp ${HAXE_LIBCACHE}/tink_core/2.0.2/github/e0ed6c33f6f396fb83397a590bee4c3d48ab2e17/src
-D tink_core=2.0.2
# @install: lix --silent download "haxelib:/tink_core#2.1.1" into tink_core/2.1.1/haxelib
-cp ${HAXE_LIBCACHE}/tink_core/2.1.1/haxelib/src
-D tink_core=2.1.1
8 changes: 3 additions & 5 deletions haxe_libraries/tink_streams.hxml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# @install: lix --silent download "gh://github.com/haxetink/tink_streams#5066a96c4a8b483479b6a8df8893eaf8922d3bea" into tink_streams/0.4.0/github/5066a96c4a8b483479b6a8df8893eaf8922d3bea
# @install: lix --silent download "gh://github.com/haxetink/tink_streams#f4478825ef0a30df1187f02a354ec61176b47b8b" into tink_streams/0.3.3/github/f4478825ef0a30df1187f02a354ec61176b47b8b
-lib tink_core
-cp ${HAXE_LIBCACHE}/tink_streams/0.4.0/github/5066a96c4a8b483479b6a8df8893eaf8922d3bea/src
-D tink_streams=0.4.0
# temp for development, delete this file when pure branch merged
-D pure
-cp ${HAXE_LIBCACHE}/tink_streams/0.3.3/github/f4478825ef0a30df1187f02a354ec61176b47b8b/src
-D tink_streams=0.3.3
7 changes: 3 additions & 4 deletions haxe_libraries/tink_testrunner.hxml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# @install: lix --silent download "gh://github.com/haxetink/tink_testrunner#45f704215ae28c3d864755036dc2ee63f7c44e8a" into tink_testrunner/0.9.0/github/45f704215ae28c3d864755036dc2ee63f7c44e8a
# @install: lix --silent download "gh://github.com/haxetink/tink_testrunner#866de8b991be89b969825b0c0f5565d51f96a6f7" into tink_testrunner/0.8.0/github/866de8b991be89b969825b0c0f5565d51f96a6f7
-lib ansi
-lib tink_macro
-lib tink_streams
-cp ${HAXE_LIBCACHE}/tink_testrunner/0.9.0/github/45f704215ae28c3d864755036dc2ee63f7c44e8a/src
-D tink_testrunner=0.9.0
--macro addGlobalMetadata('ANSI.Attribute', "@:native('ANSIAttribute')", false)
-cp ${HAXE_LIBCACHE}/tink_testrunner/0.8.0/github/866de8b991be89b969825b0c0f5565d51f96a6f7/src
-D tink_testrunner=0.8.0
76 changes: 31 additions & 45 deletions src/tink/state/Observable.hx
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
package tink.state;

private typedef BindingOptions<T> = Deprecated<{
?direct:Bool,
?comparator:T->T->Bool,
}>;

@:forward
abstract Deprecated<T>(T) {
@:deprecated
@:from static function of<X>(v:X):Deprecated<X>
return cast v;
}

/**
Common representation of a piece of observable state. It can be read using the `value` property
and bound to listen for changes using the `bind` method.
Expand Down Expand Up @@ -56,16 +44,7 @@ abstract Observable<T>(ObservableObject<T>) from ObservableObject<T> to Observab
You can customize this behaviour by passing a different `scheduler` and `comparator` instances
to this function.
**/
public function bind(
#if tink_state.legacy_binding_options ?options:BindingOptions<T>, #end
callback:Callback<T>, ?comparator:Comparator<T>, ?scheduler:Scheduler
):CallbackLink {
#if tink_state.legacy_binding_options
if (options != null) {
comparator = options.comparator;
if (options.direct) scheduler = Scheduler.direct;
}
#end
public function bind(callback:Callback<T>, ?comparator:Comparator<T>, ?scheduler:Scheduler):CallbackLink {
if (scheduler == null)
scheduler = Observable.scheduler;
return Binding.create(this, callback, scheduler, comparator);
Expand Down Expand Up @@ -121,18 +100,6 @@ abstract Observable<T>(ObservableObject<T>) from ObservableObject<T> to Observab
public function mapAsync<R>(f:Transform<T, Promise<R>>):Observable<Promised<R>>
return Observable.auto(() -> f.apply(this.getValue()));

@:deprecated('use auto instead')
public function switchSync<R>(cases:Array<{ when: T->Bool, then: Lazy<Observable<R>> } > , dfault:Lazy<Observable<R>>):Observable<R>
return Observable.auto(() -> {
var v = value;
for (c in cases)
if (c.when(v)) {
dfault = c.then;
break;
}
return dfault.get().value;
});

static var scheduler:Scheduler =
#if macro
Scheduler.direct;
Expand Down Expand Up @@ -172,12 +139,28 @@ abstract Observable<T>(ObservableObject<T>) from ObservableObject<T> to Observab

Returned `CallbackLink` object can be used to cancel the binding.
**/
static public function autorun(callback:()->Void, ?scheduler):CallbackLink {
static public function autorun(callback:Callback<CallbackLink>, ?scheduler):CallbackLink {
var i = 0;
return auto(() -> {
callback();
var link:CallbackLink = null,
cancelled = false;

var cancel:CallbackLink = () -> {
cancelled = true;
link.cancel();
}

link = auto(() -> {
if (cancelled) return 0;
callback.invoke(cancel);
i++;
}).bind(ignore, null, scheduler);

return
if (cancelled) {
link.cancel();
null;
}
else link;
}

@:deprecated
Expand All @@ -195,8 +178,8 @@ abstract Observable<T>(ObservableObject<T>) from ObservableObject<T> to Observab
will take place and the type of the observable value will become `tink.state.Promised` or `tink.State.Promised.Predicted`
respectively. The future/promise will be automatically handled to update the value of this Observable.
**/
@:noUsing static public inline function auto<T>(compute, ?comparator #if tink_state.debug , ?toString, ?pos:haxe.PosInfos #end):Observable<T>
return new AutoObservable<T>(compute, comparator #if tink_state.debug , toString, pos #end);
@:noUsing static public inline function auto<Result>(compute, ?comparator #if tink_state.debug , ?toString, ?pos:haxe.PosInfos #end):Observable<Result>
return new AutoObservable<Result>(compute, comparator #if tink_state.debug , toString, pos #end);

/**
Create a constant Observable object from a value. Const observables are lightweight objects
Expand Down Expand Up @@ -263,7 +246,7 @@ private class ConstObservable<T> implements ObservableObject<T> {
return revision;

public function canFire()
return true;
return false;

public function new(value, ?toString:()->String #if tink_state.debug , ?pos:haxe.PosInfos #end) {
this.value = value;
Expand All @@ -276,6 +259,9 @@ private class ConstObservable<T> implements ObservableObject<T> {
#end
}

function retain() {}
function release() {}

public function getValue()
return value;

Expand All @@ -297,18 +283,18 @@ private class ConstObservable<T> implements ObservableObject<T> {
return EmptyIterator.DEPENDENCIES;
#end

public function onInvalidate(i:Invalidatable):CallbackLink
return null;
public function subscribe(i:Observer) {}
public function unsubscribe(i:Observer) {}
}

private class SimpleObservable<T> extends Invalidator implements ObservableObject<T> {
private class SimpleObservable<T> extends Dispatcher implements ObservableObject<T> {

var _poll:Void->Measurement<T>;
var _cache:Measurement<T> = null;
var comparator:Comparator<T>;

public function new(poll, ?comparator #if tink_state.debug , ?toString, ?pos #end) {
super(#if tink_state.debug toString, pos #end);
super(null #if tink_state.debug , toString, pos #end);
this._poll = poll;
this.comparator = comparator;
}
Expand All @@ -321,7 +307,7 @@ private class SimpleObservable<T> extends Invalidator implements ObservableObjec

function reset(_) {
_cache = null;
fire();
fire(this);
}

function poll() {
Expand Down
Loading
Loading