Dart What does "StreamTransformer<RS, RT> cast<RS, RT>()" do? - stream
I've implemented the stream transformer. Please note that it is only an exercise (in order to learn Dart). This transformer converts integers into strings. I give the code below, and you can also find it on GitHub.
// Conceptually, a transformer is simply a function from Stream to Stream that
// is encapsulated into a class.
//
// A transformer is made of:
// - A stream controller. The controller provides the "output" stream that will
// receive the transformed values.
// - A "bind()" method. This method is called by the "input" stream "transform"
// method (inputStream.transform(<the stream transformer>).
import 'dart:async';
/// This class defines the implementation of a class that emulates a function
/// that converts a data with a given type (S) into a data with another type (T).
abstract class TypeCaster<S, T> {
T call(S value);
}
/// This class emulates a converter from integers to strings.
class Caster extends TypeCaster<int, String> {
String call(int value) {
return "<${value.toString()}>";
}
}
// StreamTransformer<S, T> is an abstract class. The functions listed below must
// be implemented:
// - Stream<T> bind(Stream<S> stream)
// - StreamTransformer<RS, RT> cast<RS, RT>()
class CasterTransformer<S, T> implements StreamTransformer<S, T> {
StreamController<T> _controller;
bool _cancelOnError;
TypeCaster<S, T> _caster;
// Original (or input) stream.
Stream<S> _stream;
// The stream subscription returned by the call to the function "listen", of
// the original (input) stream (_stream.listen(...)).
StreamSubscription<S> _subscription;
/// Constructor that creates a unicast stream.
/// [caster] An instance of "type caster".
CasterTransformer(TypeCaster<S, T> caster, {
bool sync: false,
bool cancelOnError: true
}) {
_controller = new StreamController<T>(
onListen: _onListen,
onCancel: _onCancel,
onPause: () => _subscription.pause(),
onResume: () => _subscription.resume(),
sync: sync
);
_cancelOnError = cancelOnError;
_caster = caster;
}
/// Constructor that creates a broadcast stream.
/// [caster] An instance of "type caster".
CasterTransformer.broadcast(TypeCaster<S, T> caster, {
bool sync: false,
bool cancelOnError: true
}) {
_cancelOnError = cancelOnError;
_controller = new StreamController<T>.broadcast(
onListen: _onListen,
onCancel: _onCancel,
sync: sync
);
_caster = caster;
}
/// Handler executed whenever a listener subscribes to the controller's stream.
/// Note: when the transformer is applied to the original stream, through call
/// to the method "transform", the method "bind()" is called behind the
/// scenes. The method "bind()" returns the controller stream.
/// When a listener is applied to the controller stream, then this function
/// (that is "_onListen()") will be executed. This function will set the
/// handler ("_onData") that will be executed each time a value appears
/// in the original stream. This handler takes the incoming value, casts
/// it, and inject it to the (controller) output stream.
/// Note: this method is called only once. On the other hand, the method "_onData"
/// is called as many times as there are values to transform.
void _onListen() {
_subscription = _stream.listen(
_onData,
onError: _controller.addError,
onDone: _controller.close,
cancelOnError: _cancelOnError
);
}
/// Handler executed whenever the subscription to the controller's stream is cancelled.
void _onCancel() {
_subscription.cancel();
_subscription = null;
}
/// Handler executed whenever data comes from the original (input) stream.
/// Please note that the transformation takes place here.
/// Note: this method is called as many times as there are values to transform.
void _onData(S data) {
_controller.add(_caster(data));
}
/// This method is called once, when the stream transformer is assigned to the
/// original (input) stream. It returns the stream provided by the controller.
/// Note: here, you can see that the process transforms a value of type
/// S into a value of type T. Thus, it is necessary to provide a function
/// that performs the conversion from type S to type T.
/// Note: the returned stream may accept only one, or more than one, listener.
/// This depends on the method called to instantiate the transformer.
/// * CasterTransformer() => only one listener.
/// * CasterTransformer.broadcast() => one or more listener.
Stream<T> bind(Stream<S> stream) {
_stream = stream;
return _controller.stream;
}
// TODO: what should this method do ? Find the answer.
StreamTransformer<RS, RT> cast<RS, RT>() {
return StreamTransformer<RS, RT>((Stream<RS> stream, bool b) {
// What should we do here ?
});
}
}
main() {
// ---------------------------------------------------------------------------
// TEST: unicast controller.
// ---------------------------------------------------------------------------
// Create a controller that will be used to inject integers into the "input"
// stream.
StreamController<int> controller_unicast = new StreamController<int>();
// Get the stream "to control".
Stream<int> integer_stream_unicast = controller_unicast.stream;
// Apply a transformer on the "input" stream.
// The method "transform" calls the method "bind", which returns the stream that
// receives the transformed values.
Stream<String> string_stream_unicast = integer_stream_unicast.transform(CasterTransformer<int, String>(new Caster()));
string_stream_unicast.listen((data) {
print('String => $data');
});
// Inject integers into the "input" stream.
controller_unicast.add(1);
controller_unicast.add(2);
controller_unicast.add(3);
// ---------------------------------------------------------------------------
// TEST: broadcast controller.
// ---------------------------------------------------------------------------
StreamController<int> controller_broadcast = new StreamController<int>.broadcast();
Stream<int> integer_stream_broadcast = controller_broadcast.stream;
Stream<String> string_stream_broadcast = integer_stream_broadcast.transform(CasterTransformer<int, String>.broadcast(new Caster()));
string_stream_broadcast.listen((data) {
print('Listener 1: String => $data');
});
string_stream_broadcast.listen((data) {
print('Listener 2: String => $data');
});
controller_broadcast.add(1);
controller_broadcast.add(2);
controller_broadcast.add(3);
}
The class CasterTransformer<S, T> extends the abstract class StreamTransformer<S, T>.
Thus, it implements the method StreamTransformer<RS, RT> cast<RS, RT>().
On the documentation, it is said that :
The resulting transformer will check at run-time that all data events of the stream it transforms are actually instances of S, and it will check that all data events produced by this transformer are actually instances of RT.
See: https://api.dartlang.org/stable/2.1.0/dart-async/StreamTransformer/cast.html
First, I think that there is a typo in this documentation : it should say "...it transforms are actually instances of RS" (instead of S).
However, this seems obscure to me.
Why do we need a stream transformer to check values types ? The purpose of a transformer is to transform, isn't it ? If the purpose of a component is to check, so why don't we call it a checker ?
And, also, why would we need to check that the transformer (we implement) produces the required data ? If it doesn't, then we face a bug that should be fixed.
Can someone explain the purpose of the method Cast() ?
The cast method is there to help typing the operation.
If you have a StreamTransformer<num, int>, it transforms numbers to integers (say, by calling .toInt() on them and then adding 42, because that is obviously useful!).
If you want to use that transformer in some place that expects a StreamTransformer<int, num>, then you can't. Since num is not a sub-type of int, the transformer is not assignable to that type.
But you know, because you understand how a stream transformer actually works, that the first type argument is only used for inputs. Something that accepts any num should safely be useable where it's only given ints.
So, to convince the type system that you know what you are doing, you write:
StreamTransformer<int, num> transform = myTranformer.cast<int, num>();
Now, the tranformer takes any integer (RS), checks that it's a num (S), passes it to myTransformer which calls toInt() and adds 42, then the resulting int (T) is passed back and transformer checks that it is a num (RT) and emits that.
Everything works and the type system is happy.
You can use cast to do things that will never work at run-time, because all it does is to add extra run-time checks that convinces the static type system that things will either succeed or throw at those checks.
The easiest way to get an implementation of StreamTransformer.cast is to use th e StreamTransformer.castFrom static method:
StreamTransformer<RS, RT> cast<RS, RT>() => StreamTransformer.castFrom(this);
That will use the system's default cast wrapper on your own transformer.
Related
Downcasting generic of Future in dart
I have a future that has a generic parameter, which is a superclass (A) of another class (B extends A). I know for a fact that the instance of the value of the Future is of the subtype. Why can't I downcast the Future<A> to Future<B> in dart? If I unwrap the Future once and then wrap it again using async/await, it works. Here's an example: class A {} class B extends A{} void main() { Future<A> getFuture() async { return B();} Future<B> getBasA() { return getFuture() as Future<B>;} Future<B> getBasAasync() async { return (await getFuture()) as B;} print(getBasAasync()); // Works print(getBasA()); // Throws at runtime } For the curious and as a motivation for the question, here's a closer-to-world example. I have a stream that emits data packets, which I filter and then get the first like this: Future<T> getResponse<T extends ReceivedPacket>() => getStream<ReceivedPacket>().firstWhere((packet) => packet is T) as Future<T>; //throws Future<T> getResponse<T extends ReceivedPacket>() async { //works return (await getStream<ReceivedPacket>().firstWhere((packet) => packet is T)) as T; } PS: I've tried it out in Typescript (will happily compile and run) and C# (won't compile, but I have very limited C# knowledge). I understand that the answer to this question might be "because this is how the dart type system works". I'm just confused, because I'd have expected it either to fail at compile time like C# or work at runtime, too, like typescript.
You declared getFuture() as returning Future<A> but with the async keyword, so Dart automatically transforms return B(); to (essentially) return Future<A>.value(B());. The returned Future was never a Future<B> and therefore cannot be downcast to it. Creating the Future explicitly would do what you expect: Future<A> getFuture() { return Future<B>.value(B()); } You could argue that Dart when transforms return x; in async functions, it should create a Future<T> where T is the static type of x instead of inferring it from the function's return type. As lrn explained in comments, that can't be done because you might have: class C extends A {} Future<A> getFuture() async { await Future.delayed(const Duration(seconds: 1)); if (Random().nextBool()) { return B(); } else { return C(); } } The caller must get a Future back immediately, but it won't be known whether its value will be a B or C until the Future eventually completes. I'd have expected it either to fail at compile time like C# I too have very limited experience with C#, but I think that C# gives you a compilation error because C# does not consider Generic<SubType> to be a subtype of Generic<SuperType> whereas Dart does.
Capture a generic runtime type of an object in a function
Consider the following minimal example: class Element<T> {} final elements = [ Element<String>(), Element<int>(), ]; Element<T> transformation<T>(Element<T> element) => Element<T>(); void main() { // prints 'Element<String>', 'Element<int>' for (final element in elements) { print(element); } // prints 'Element<Object>', 'Element<Object>', // but I would like to get identical output as above. for (final element in elements) { print(transformation(element)); } } I can't figure out how to create a transformation of each Element<T> with its respective generic runtime type T, not the static Object type of the elements collection. I remember having seen some complicated trick with nested closures in the past, but can't seem to find it anymore. How can I make my function capture the runtime type of T, without having to implement the transformation function in the Element class itself?
That's not possible. The only way to get access to the type variable of an object is if the object gives it to you. If Element had a method like R callWith<R>(R Function<T>(Element<T>) action) => action<E>(this); then you could get access to the E of Element<E> as a type variable. You can't do that from outside the class.
How can you map/"resink" the input side of a StreamController
Say I have a StreamController<int> called myController. I understand that I can map the output of the stream by doing something like Stream<int> intStream = myController.map((i) => i * 2) and then listen to that. But what if I wanted to intercept/map the input of the sink, how would I achieve this? Is there an API for this? For clarity, have a look at this fake non-working example: Sink<bool> boolSink = myController.sink.resink<int>((bool b) => b ? 1 : 0). So a mapping that takes a sink of bools and converts it to a sink of ints. Then I would do boolSink.add(true) and expect intStream to emit 1. My goal is to provide simplified sinks (Sink<void>) to a few components, but without them having to place the particular value into the sink (because they will each only ever be adding a predefined value). And ideally without having to manage multiple StreamControllers myself.
My solution was to have a class which implements the Sink interface. It intercepts calls to the add method, maps the value being added to a new value, and then forwards it on to the "real" sink. I need a new instance of the class for each type of mapping. import 'package:meta/meta.dart'; typedef Mapper<T, R> = R Function(T); /// A [Sink] which takes a value of type [T] and maps it to a destination sink /// which expects a value of type [R]. class SinkMapper<T, R> implements Sink<T> { final Mapper<T, R> _mapper; final Sink<R> _sink; SinkMapper({ #required Mapper<T, R> mapper, #required Sink<R> sink, }) : _mapper = mapper, _sink = sink; #override void add(T data) => _sink.add(_mapper(data)); #override void close() => _sink.close(); }
how to use setMethodCallHandler [duplicate]
I am writing a native plugin that, in some cases, has to call functions in the Flutter portion of the app, written in Dart. How it's achieved, is explained here: https://flutter.io/platform-channels/ Furthermore, an example of invoking a method from the native/platform part towards the Dart/non-native is here: https://github.com/flutter/plugins/tree/master/packages/quick_actions Now, this example is really nice in case the platform only needs to invoke a method, i.e. that call returns nothing/void, but in case it needs to invoke a function, i.e. needs a return value from the non-native/Dart part, I could not have found an example or documentation on the internet. I believe it can be implemented though, because in the native Java part, there is a method: public void invokeMethod(String method, Object arguments, MethodChannel.Result callback) So, there is a callback object that could have a return value from the non-native part - or, I am mistaken here, and there is currently no way of returning a value from the non-native Dart portion of the app?
The signature is void setMethodCallHandler(Future<dynamic> handler(MethodCall call)), so we need to provide a function at the Dart end that returns Future<dynamic>, for example _channel.setMethodCallHandler(myUtilsHandler); Then implement the handler. This one handles two methods foo and bar returning respectively String and double. Future<dynamic> myUtilsHandler(MethodCall methodCall) async { switch (methodCall.method) { case 'foo': return 'some string'; case 'bar': return 123.0; default: throw MissingPluginException('notImplemented'); } } At the Java end the return value is passed to the success method of the Result callback. channel.invokeMethod("foo", arguments, new Result() { #Override public void success(Object o) { // this will be called with o = "some string" } #Override public void error(String s, String s1, Object o) {} #Override public void notImplemented() {} }); In Swift, the return value is an Any? passed to the result closure. (Not implemented is signaled by the any parameter being the const NSObject value FlutterMethodNotImplemented.) channel.invokeMethod("foo", arguments: args, result: {(r:Any?) -> () in // this will be called with r = "some string" (or FlutterMethodNotImplemented) })
Is there a way to pass a primitive parameter by reference in Dart?
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.