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

Deprecate rx-utils #180

Open
raveclassic opened this issue Sep 23, 2019 · 6 comments
Open

Deprecate rx-utils #180

raveclassic opened this issue Sep 23, 2019 · 6 comments

Comments

@raveclassic
Copy link
Contributor

raveclassic commented Sep 23, 2019

Historically Sink and Context were a wrong abstraction and we should consider removing them in future versions of platform.

Sink exposes unsafe access to inner value when you can forget about subscribing to effect. On the other hand we could replace type Sink<A> = { value: A, effect: Observable<unknown>> } with just a type Sink<A> = Observable<A> where effect is bundled into the stream and you don't have direct unsafe access to sink's value, only in subscriber.

Keeping that in mind, type Context<E, A> becomes just a Reader<E, Observable<A>> and can be dropped as well.

Still we have to deliver LiveData and operators and that's why we should think about #171

@igoralekseev
Copy link
Contributor

igoralekseev commented Sep 23, 2019

Can you please give an example of

type Sink<A> = Observable<A> where effect is bundled into the stream

@raveclassic
Copy link
Contributor Author

@igoralekseev Assuming we're using rxjs:

const withEffect = <A>(source: Observable<A>, f: (a: A) => Observable<unknown>): Observable<A> =>
	merge(
		source,
		pipe(
			source,
			switchMap(f),
			ignoreElements(),
		),
	);

declare function save(data: string): Observable<void>;
declare const source: Observable<string>;

const data = withEffect(source, save);

@raveclassic
Copy link
Contributor Author

@igoralekseev Or if we're creating an instance of effectful viewmodel (typical usage):

interface ViewModel {
	a: Observable<string>;
	b: Observable<string>;
	remove(): void;
}
declare function remove(id: string): Observable<void>;
const createViewModel = (id: string): Observable<ViewModel> => {
	const removeHandler = createHandler<void>();
	const vm = {
		a: of('123'),
		b: of('456'),
		remove: removeHandler.handle,
	};
	const removeEffect = pipe(
		removeHandler.value$,
		switchMap(() => remove(id)),
		share(),
	);
	return withEffect(of(vm), () => removeEffect);
};

@raveclassic
Copy link
Contributor Author

raveclassic commented Sep 23, 2019

We could go even shorter with using Proxy for creating handlers:

const withEffect = <A>(source: Observable<A>, effect: Observable<unknown>): Observable<A> =>
	merge(
		source,
		pipe(
			source,
			switchMapTo(effect),
			ignoreElements(),
			share(),
		),
	);
interface Handler<A> extends Observable<A> {
	(a: A): void;
}
interface VoidHandler<A> extends Observable<A> {
	(): void;
}
const functionKeys: PropertyKey[] = Object.getOwnPropertyNames(Object.getPrototypeOf(constVoid));
const createHandler = <A = never>(): A extends void ? VoidHandler<A> : Handler<A> => {
	const s = new Subject<A>();
	const shared = pipe(
		s,
		share(),
	);
	const next = (a: A) => s.next(a);
	return new Proxy(next, {
		get(target, key) {
			return (functionKeys.includes(key) ? next : shared)[key];
		},
	}) as any;
};

interface ViewModel {
	a: Observable<string>;
	b: Observable<string>;
	remove(): void;
}
declare function remoteRemove(id: string): Observable<void>;
const createViewModel = (id: string): Observable<ViewModel> => {
	const remove = createHandler<void>();
	const vm: ViewModel = {
		a: of('123'),
		b: of('456'),
		remove,
	};
	const removeEffect = pipe(
		remove,
		switchMap(() => remoteRemove(id)),
	);
	return withEffect(of(vm), removeEffect);
};

@sutarmin
Copy link
Contributor

Wow, createHandler looks expressive and impressive. But I would say that having both Observable and Function at the same object (can I still call this thing an object?) is not quite FP way of making things though 😅

@raveclassic
Copy link
Contributor Author

Why not, it's just an object that implements both Observable and Function interfaces.
Anyway it's not related to Sink/Context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants