Code Monkey home page Code Monkey logo

async's Introduction

Dart CI pub package package publisher

Contains utility classes in the style of dart:async to work with asynchronous computations.

Package API

Publishing automation

For information about our publishing automation and release process, see https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

async's People

Contributors

anhtuan23 avatar athomas avatar bcko avatar chalin avatar dependabot[bot] avatar devoncarew avatar ds84182 avatar floitschg avatar franklinyow avatar futuristicgoo avatar goddchen avatar himamis avatar jakemac53 avatar jonasfj avatar keertip avatar kevmoo avatar leafpetersen avatar lrhn avatar matanlurey avatar michaelrfairhurst avatar mnordine avatar munificent avatar natebosch avatar nex3 avatar sethladd avatar sgjesse avatar srawlins avatar tastypi avatar tvolkert avatar whesse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

async's Issues

There is a problem with the onCancel call of CancelableOperation.

example like next:

import 'package:async/async.dart';

main(List<String> arguments) async {
  var cancel = CancelableOperation.fromFuture(getNumber(), onCancel: () {
    print("on cancel");
  }).then((f) {
    print("on call method");
    var i = f();
    print("i = $i");
  });
  cancel.cancel();
  print("run end");
}

Future<int Function()> getNumber() async {
  await Future.delayed(Duration(seconds: 1));
  return () {
    print("will return 1");
    return 1;
  };
}

I get log

run end

And onCancel was not called.

I delete cancel.cancel();, and get the log:

run end

on call method

will return 1

i = 1


/// Creates a [CancelableOperation] wrapping [inner].
///
/// When this operation is canceled, [onCancel] will be called and any value
/// or error produced by [inner] will be discarded. If [onCancel] returns a
/// [Future], it will be forwarded to [cancel].
///
/// [onCancel] will be called synchronously when the operation is canceled.
/// It's guaranteed to only be called once.
factory CancelableOperation.fromFuture(Future<T> inner,
{FutureOr onCancel()}) {
var completer = CancelableCompleter<T>(onCancel: onCancel);
completer.complete(inner);
return completer.operation;
}

I understand that onCancel should be called back synchronously when I call the cancel method.

So what went wrong?

Env

dart --version
Dart VM version: 2.3.0 (Fri May 3 10:32:31 2019 +0200) on "macos_x64"

async package version:

image

FR: ThrottleStreamTransformer

There are probably a dozen or so implementations of this internally today, most like:

stream.transform(new ThrottleStreamTransformer(...))

or

stream.transform(throttle())

The primary needs seem to be specifying a duration in which is the minimum amount of time between new events (i.e. for something like mouse scrolling it is every common). We could also make a Throttler class so users that don't use Stream still would get a benefit.

_StreamQueue._cancel() causes Stream StateError

In the process of building an incremental test runner for Flutter, I've come across an interesting bug. I use the AnsiTerminal terminal defined as a global getter inside the flutter_tools package. Being an AnsiTerminal it is bound to stdin.

We use the test running facilities provided in dart-lang/test via package:test_core/src/executable.dart. Their io libary binds to stdin lazily through a StreamQueue(after a transform that splits the lines). We never cause the binding to happen, and things work well during the invocation. But when we nicely close out all of our resources when we go to shutdown the process, it blows up with a StateError do to these two lines in _StreamQueue._cancel()

if (_subscription == null) _subscription = _sourceStream.listen(null);
var future = _subscription.cancel();

I'm trying to understand the rationale behind this, and maybe what strategy I can use to work around it. Removing those lines seems to have no affect on this narrow case, but I'm sure that this has some meaningful semantics in other context.

Potential breaking changes for the NNBD release

Below is a list of potential changes we might wish to make when we do the NNBD migration, either to help having fewer nullable types in our apis or generally take advantage of a breaking release to improve other apis:

  • Result.asValue/Result.asError should throw instead of returning null
  • CancelableOperation.valueOrCancellation should have a required param of type T

Feel free to edit this comment and add your own suggestions.

Ephemeral AsyncCache is not truly ephemeral

Hi,

Here is a repro:

import 'package:async/async.dart';

Future<void> main(List<String> arguments) async {
  final cache = AsyncCache.ephemeral();

  final firstValue = await cache.fetch(_getValue);
  print('First value: $firstValue');
  final secondValue = await cache.fetch(_getValue);
  print('Second value: $secondValue');
}

int _value = 0;
Future<int> _getValue() async {
  await Future<void>.delayed(const Duration(milliseconds: 500));
  return ++_value;
}

This outputs:

First value: 1
Second value: 1

My expectation would be that the cached future would be invalidated immediately after the first fetch is awaited, leading to this output:

First value: 1
Second value: 2

The reason this doesn't happen is because an ephemeral AsyncCache has a duration of zero, but invalidation always uses a Timer, so invalidation won't occur until the event loop gets a chance to execute (after my main).

By making the following adjustments to AsyncCache, I get the desired behavior:

  Future<T> fetch(Future<T> Function() callback) async {
    if (_cachedStreamSplitter != null) {
      throw StateError('Previously used to cache via `fetchStream`');
    }

    var future = _cachedValueFuture;

    if (future == null) {
      future = _cachedValueFuture = callback();
      await future;
      _startStaleTimer();
    }

    return future;
  }

  void _startStaleTimer() {
    if (_duration == Duration.zero) {
      invalidate();
    } else {
      _stale = Timer(_duration, invalidate);
    }
  }

I believe fetchStream would require a tweak similar to that I made to fetch.

Am I right in thinking this is a bug, or is my expectation of behavior incorrect?

stream_test pipe uses wrong class

stream_test 'pipe()' has:

var consumer = new StreamController();
expect(wrapper.pipe(consumer), completes);

However, the variable is named consumer but constructs a StreamController.

Also, pipe takes a StreamConsumer.

FR: AsyncCache<T>, avoid duplicate async when fresh

Proposal

As usual, happy to implement and send a PR

A class, SynchronizedFuture (name debatable) that only calls a function once until completion:

abstract class SynchronizedFuture<T> implements Future<T> {
  factory AsyncSynchronizer(Future<T> call()) {
    // Use an expando and only create one per `call` function. See below
  }

  // Could also have another factory without a cache (similar to AsyncMemoizer use).
}

Example use:

class Example {
  // Actual implementation.
  Future<String> _doExpensiveCall() async {
    print('CALL MADE');
    return 'Hello';
  }

  // Visible API.
  Future<String> expensiveCall() => new SynchronizedFuture<String>(_doExpensiveCall);
}

main() async {
  var example = new Example();
  example.expensiveCall();
  example.expensiveCall();
  await new Future.value();
  // Output: 'CALL MADE', once.
  await example.expensiveCall();
  // Output: 'CALL MADE', again, since this is a _new_ call and none is in process
}

I'd also like to have a similar pattern for Streams, though that one would be a little more difficult since there isn't a true start/stop point - perhaps if at least one event has been returned on the stream it's considered "done" and new calls call the call function again for a new stream.

/cc @nex3

Ship strong-mode clean pkg/async with passing tests

I've updated the strong branch with a few fixes – still have one left

00:00 +9 -1: with invalid types doesn't throw a CastError for catchError()
  type 'String' is not a subtype of type 'int' in type cast where
    String is from dart:core
    int is from dart:core

  dart:core                                   Object._as
  package:async/src/typed/future.dart 18:45   TypeSafeFuture.then.<fn>
  ===== asynchronous gap ===========================
  dart:async                                  _Future.then
  package:async/src/typed/future.dart 18:15   TypeSafeFuture.then
  dart:async                                  _SyncCompleter.complete
  package:async/src/typed/future.dart 15:7    TypeSafeFuture.catchError.<async>
  ===== asynchronous gap ===========================
  dart:async                                  Future.Future.microtask
  package:async/src/typed/future.dart         TypeSafeFuture.catchError
  test/typed_wrapper/future_test.dart 111:24  main.<fn>.<fn>.<fn>

Dart 2 runtime errors in tests

type '(dynamic, dynamic) => dynamic' is not a subtype of type '(_FakeTimer, _FakeTimer) => _FakeTimer' of 'combine'
dart:core Iterable.reduce
package:fake_async/fake_async.dart 236:26 _minOf
package:fake_async/fake_async.dart 166:12 _FakeAsync._getNextTimer
package:fake_async/fake_async.dart 107:20 _FakeAsync.elapse
../../async/test/restartable_timer_test.dart 20:13 main..

type '_StreamHandlerTransformer<dynamic, dynamic>' is not a subtype of type 'StreamTransformer<String, dynamic>' of 'streamTransformer' where...
type '_StreamHandlerTransformer<dynamic, dynamic>' is not a subtype of type 'StreamTransformer<String, dynamic>' of 'streamTransformer' where
_StreamHandlerTransformer is from dart:async
StreamTransformer is from dart:async
String is from dart:core
dart:async Stream.transform
../../dart/async/test/stream_group_test.dart 51:44 main..

type 'List<Stream>' is not a subtype of type 'List<Stream>' where...
type 'List<Stream>' is not a subtype of type 'List<Stream>' where
List is from dart:core
Stream is from dart:async
List is from dart:core
Stream is from dart:async
int is from dart:core
package:async/src/stream_splitter.dart 65:12 StreamSplitter.splitFrom
../../dart/async/test/stream_splitter_test.dart 276:35 main.

and more...

breaking change in v2.4.2

Problem

When creating mock streams using mockito before v2.4.2 it was possible to create mocks like:

import 'package:mockito/mockito.dart';

class MockStream extends Mock implements Stream {}

The following would then work:

final stream = MockStream();
when(stream.listen(
    any,
    onError: captureAnyNamed('onError'),
    onDone: captureAnyNamed('onDone'),
    cancelOnError: captureAnyNamed('cancelOnError'),
  )).thenAnswer((invocation) {
    return Stream.value(0).listen(
      invocation.positionalArguments.first as Function(int),
      onError: invocation.namedArguments[const Symbol('onError')] as Function,
      onDone:invocation.namedArguments[const Symbol('onDone')] as void Function(),
      cancelOnError:invocation.namedArguments[const Symbol('cancelOnError')] as bool,
    );
});

After v2.4.2 the above code throws an exception because _source.isBroadcast returns null.

if (_source.isBroadcast) {

In order to make the above code work we now must additionally stub isBroadcast

final stream = MockStream();
when(stream.isBroadcast).thenReturn(false);
when(stream.listen(...));

Proposal

Add a null check to the existing code for backward compatibility:

// `isBroadcast` can be null for a mock source.
if (_source?.isBroadcast == true) {
  _ensureListening();
  _pause();
}

README doesn't show the awesome that is this library

Hi,

This package is great. But the README makes it look like it has a single thing: ZipStream.

We might consider blanking out the README and just point to the docs, or we could add more "what is this thing and what can it do for me?" in the README.

We're way way way way underselling this library in the README :)

Feature request: timeoutToNull extension method

It would be useful to have a method on a Future<T> that returns a Future<T?> that resolves to null if it doesn't complete within a timeout.

This would be useful for cases like _exitCodeOrNull in test_process, which looked like this prior to null safety:

Future<int> get _exitCodeOrNull async =>
    await exitCode.timeout(Duration.zero, onTimeout: () => null);

but requires this when migrating to null safety, as exitCode is a Future<int> but we need a Future<int?>:

Future<int?> get _exitCodeOrNull => exitCode
    .then<int?>((value) => value)
    .timeout(Duration.zero, onTimeout: () => null);

With an extension method, this could be simplified to:

Future<int?> get _exitCodeOrNull => exitCode.timeoutToNull(Duration.zero);

cc: @nex3

[Help needed] Broadcast Streams Cache Last Result

Hey Dart Async folks!

I was hoping to get your help on an issue I've been having with setting up Streams in my flutter project.

Description:

There are 3 broadcast streams A, B and C at the top level. They are initialized at the start of the app connecting to 3 different Firestore collections for the User.

Now, each of them is transformed in multiple ways. So, A might give rise to A.1 and A.2; similarly, for B and C. In the end, you could imagine a data flow graph where every node is a stream. Widgets mostly tap into the leaves of this graph but could also theoretically tap into the inner node. Now, the problem comes by the fact that broadcast streams don't "cache" the last result. Right now, on rebuild the StreamBuilder widgets end up empty since the stream "now" has no data. In this newly rebuilt StreamBuilder widget, if we change the data on Firebase, the top-level broadcast stream gets some new data that flows down the graph of transformation into the Widget finally showing expected data. If it's rebuilt, it loses that data since the stream is, once again, now, "empty". What I would want is if the Widget listening into a stream using StreamBuilder is rebuilt (that is, it's a new listener), it gets the same data it had before the rebuild. This same data should come from the "latest" (last) data top-level broadcast streams have.

in case the terminology is new:
Collection = a list of documents, equivalent to a table in SQL
Document = the object in JSON form, equivalent to a row in SQL

Ask:

  • Lol, what do you suggest I do?
    I could try to implement my own StreamController that does cache the last result? I'm imagining my problem seems common enough that there's some solution out there that I just can't seem to find.

Thanks for your help!

Implement CancelableOperation.then

It would be useful to be able to transform CancelableOperation in the same way as a Future. The API I envisage is:

CancelableOperation<R> then<R>(CancelableOperation<R> Function(T) onValue,
    {Function onError});

CancelableOperation<R> thenFuture<R>(FutureOr<R> Function(T) onValue,
    {Function onError});

This should behave similarly to Future.then, but with the following extras:

  1. If the original operation is canceled then the returned operation is canceled.
  2. If the operation returned by onValue is canceled then the returned operation is canceled.
  3. If the returned operation is canceled then the original operation is canceled or the onValue operation is canceled if it has been started.

Publish 1.13.3

This is currently a blocker for running tests with dartdevc, which will be published in the 1.24.0-dev.5.0 release of the sdk.

Any reason to hold off on a release or can we get one soon?

DelegatingStreamSubscription.onData relies on fuzzy arrows

The definition of DelegatingStreamSubscription<T>.onData passes a function of type T -> void to the onData callback of a StreamSubscription<dynamic> which does not statically guarantee to respect the contract. This has been supported via the "fuzzy arrow" feature:

dart-lang/sdk#29630

This feature is being removed in Dart 2.0, and so this code needs to be fixed up.

Broken links in README.md

Original report from: dart-lang/pub-dev#4589

It seems to me that #150 changed the library name, which does affect the file names in the generated API docs. The README references the old URLs, either the library name should be reverted, or the links updated.

Drop dependency on fake_async

Pull requests on that package are getting ignored. That package does not compile with DDC.

We either need to fork that package or drop our dependency.

cc @kevmoo

This is a blocker for #48

Remove all the "typed" APIs

Nearly all of these are not useful in Dart 2.

One that is useful is DelegatingStreamSink.typed. I'm pretty sure there is an equivalent alternative, but it isn't very attractive:

// before
var typed = DelegatingStreamSink<T>.typed(untyped);

// after
var typed = StreamController<T>(sync: true)..stream.cast<S>().pipe(untyped);

Patterns and related features

@munificent commented on Mon Aug 26 2019

We're starting to investigate a couple of related language changes around tuples, pattern matching, and destructuring. These features touch on other things like "data classes", so I wanted to kick things off by trying to sketch out my understanding of the space, how I think the various features relate to each other, and the relevant issues people have filed.

Concepts

As I understand it, the core concepts are:

Patterns

Languages with pattern matching revolve around patterns. "Expression" and "statement" are both syntactic categories in the grammar. Patterns form a third category. Most languages that have pattern matching have a variety of different kinds of patterns they support. The basic idea with a pattern is that it:

  • Can be tested against some value to determine if the pattern matches. Some kinds of patterns always match.
  • If (and only if) it does match, the pattern may bind a new variable in some scope.

Languages with patterns use them in a variety of places. They can be how you declare variables and catch exceptions. Some languages use them for defining parameter lists or "overloads" of a function. Every language with patterns has some kind of explicit pattern matching expression or statement...

Pattern matching

Once you have patterns, it makes sense to introduce a control flow structure that uses them. A pattern matching statement takes a value that it matches against. Then it has a series of pairs of patterns and bodies. At runtime, the implementation tests each pattern against the value in turn. The first pattern that matches has its body executed. Any variables the pattern binds are only available in the corresponding body.

If you really want a functional style, an even more powerful form is a pattern matching expression that lets you do pattern matching in the middle of a larger expression and have it evaluate to a value. In order to do that in a sound way, though, you need the next concept...

Exhaustiveness

So you're executing a pattern matching construct, walking through all of the patterns to find a match. What happens if none of the cases match? If the pattern matching construct is an expression, this is a real problem because what value do you evaluate to in that case? Even when you are using pattern matching as a statement, users still likely want to know if it's possible for a value to not match any of the cases.

To help with that, many languages with pattern matching also do exhaustiveness checking. The static checker will examine all of the patterns and determine if it's possible for a value to sneak through and match none of them. If so, the compiler reports a warning or error to let the user know. Dart does a limited form of this now with switch statements on enum types. If you miss one of the enum cases, Dart gives you a warning to let you know. Exhaustiveness checking in pattern matching takes that concept and applies it to much larger, more complex patterns.

Destructuring

A list literal expression takes a number of smaller values, the list element expressions, and composes a new larger value out of them, the list. Patterns go in the other direction. For example, a list pattern contains a series of nested patterns for elements. When the list pattern is matched against a list, it destructures the list by walking the elements and matching them against the corresponding element patterns.

This gives you a really nice terse way to pull pieces of aggregate objects like lists, maps, and even instances of classes. Any time you have a pattern that contains nested subpatterns, you're usually doing some kind of destructuring.

Tuples

Lists are a great way to collect a series of values of the same type, but they work poorly when you want to, say return a number and a string from a function. Since lists only have a single element type, the type of each separate element is lost.

Tuples fix that. A tuple is sort of like a fixed-size list where each element has its own distinct type. The type system can see "into" the tuple. A tuple of an int followed by a String has a different static type than a tuple of two booleans. Syntactically, tuples are usually a comma-separated list of expressions surrounded by parentheses, like (1, "string").

Tuples are useful in a statically-typed language for things like multiple return values because they maintain precise types. Once you have a tuple, you eventually need to pull the elements back out of it. You can provide number-like getters, but that gets tedious. Languages with tuples almost always have some kind of tuple pattern so that you can destructure them.

Algebraic data types, sum types, unions with fields

Pattern matching comes to us from the ML language family. Another key feature of those languages that ties into pattern matching are algebraic data types, also known as sum types, discriminated unions, or tagged unions. To make matters more confusing, these are quite different from both union types and (untagged) unions in C and C++. Algebraic data types are sometimes abbreviated ADT, which is again distinct from abstract data types. Thanks, computer scientists.

Sum types are like superpowered unions. You have a type that contains a fixed, closed set of cases. Unlike Dart enums, each case may also have its own fields containing extra data. For example, you might have a Shape type, with cases for Rect and Circle. Rect would have four coordinates for its corners. Circle would have a center and radius.

In an object-oriented language like Dart, you'd model this using subclasses. You'd have an abstract Shape class and Rect and Circle subclasses. Subclasses and ADTs are sort of duals. Indeed, Scala's case classes are like ADTs but are subclasses under the hood.

These come into discussions of pattern matching because the typical way to define behavior on specific cases in a sum type is by using a pattern match on the different cases. Likewise, the way to extract each case's fields is by using a case pattern to destructure them. Something like:

someShape match {
  case Rect(left, top, right, bottom) => ...
  case Circle(x, y, radius) => ...
}

It would be very strange to add algebraic data types to a language without a nice way to switch on and decompose them like this.

"Data classes" and "value types"

One of the most frequent feature requests for Dart is some kind of data classes or value types. The former is inspired by Kotlin's corresponding feature. The latter means different things to different people, but common attributes seem to be:

  • A lightweight way of defining a named type with some fields without having to explicitly declare a constructor that initializes each field.

  • An automatically provided implementation of hashCode and operator ==() so that the resulting object has "value semantics".

  • Some kind of immutability. The fields can't be modified. Some ask for deep immutability—the values of all fields must themselves be immutable. This usually implies that the data class can't be extended either.

  • Often some easy way to clone or make a new copy of an object with some fields changed.

Data classes are involved with patterns and pattern matching because users also often request that a data class also implicitly provides destructuring support. (Kotlin data classes give you this.) That means some kind of pattern to let you pull apart the fields in an object.

Also, part of the motivation for both algebraic data types and data classes is a nicer notation for defining a new composite type without all of the boilerplate overhead of a full class declaration. In other words, a good ADT feature might subsume data classes or vice versa.

Type patterns

The last concept which has mostly come up internally but exposes a capability the language doesn't otherwise offer is some kind of way to expose the runtime type argument of an object. If you have patterns and pattern matching, a natural solution is a type pattern that matches an object of some generic type and binds variables to the object's type arguments.

Structure

We are very early in our investigation. This is a family of features that are very close to my heart. I wrote a doc in 2011 requesting that we add pattern matching to Dart. At the same time, as you can see, this is a big sprawling feature and we currently have our hands full with extension methods and non-nullable types (both very large features in their own right!).

That being said, here is how I am interested in approaching the space and what I hope we can do:

  1. Define a set of pattern syntax and semantics. I think patterns are the most important core to all of these features. You obviously can't do pattern matching, destructuring, and exhaustiveness checking at all without them. You technically can do tuples, sum types, and data classes, but I think they are significantly hampered without them.

    Also, while it may not be obvious, I believe there are some real tricky design challenges in this area. Patterns are a dense grammatical region where we want to be able to express a wide variety of behaviors in a small amount of syntax. We need to be able to destructure lists, maps, sets, and tuples. Do runtime type tests. Pull fields out of, at least, sum types or data classes. And, of course, test for literals and constant values for simple switch-like cases.

    We are constrained by the expectations users bring from other languages, the desire for patterns to mirror their corresponding expression forms, and (hardest) Dart's existing notation for variable declarations, since those are already a limited form of "pattern" in that they introduce bindings.

    I think getting this right is key.

  2. Define a set of language constructs where patterns can be used. Some new kind of pattern matching statement is the obvious one. But also retrofitting them into variable declarations so that you can destructure in a declaration. Can we re-engineer catch clauses to be pattern based so that you can have more sophisticated catches? Should parameter lists use patterns? Do we want an expression-based pattern match? Some kind of if-let-like statement? This is the fun stuff. Once users have internalized how patterns work, the more places they can use that knowledge the better.

  3. Add tuples. This can happen in parallel with the previous one. Much like adding sets, this is a whole feature in its own right with new syntax, object representation, runtime behavior, and type checking.

    @lrhn has already put some work into defining this.

  4. User-defined destructuring. My favorite language features are ones that add new expressive syntax whose semantics can be programmed by an API designer. The for-in loop syntax is baked into the language, but you get to decide what it means in your implementation of Iterable.

    I want the same thing for destructuring. I think a syntax something like Name(subpattern1, subpattern2) (an identifier followed by a parenthesized list of subpatterns) should desugar to calling some user-defined destructuring operation on the matched value. That operation can define whether the value matches or not and, if it does, what subvalues the subpatterns are matched against. There are obviously many details to work out here, but also prior are in Scala's unapply() method and Kotlin's componentN() methods.

  1. Data classes. The least-defined (in my mind) goal is around improving the user experience for easily defining value-like types. This could mean some new explicit "data class" syntax, or a family of smaller features like allowing implicit constructors to take parameters.

    If we allow these two be subclasses of a superclass, then that gets pretty close to sum types once you include the previously-mentioned ability to pattern match on types and destructure them. Likewise, once you have general user-defined destructuring, then any lightweight data class-like syntax can just be syntactic sugar for a class declaration that includes a declaration of its own destructuring support.

As I said, this is all still very early and tentative, but I wanted to collect what I think are the related issues into one place and then begin sketching out a map through this territory.


@werediver commented on Tue Aug 27 2019

A great digest 👍 In the meanwhile, there is a young library that generates sum-types (-alike classes) for you: https://github.com/werediver/sum_types.dart (is this shameless plug an off-topic?)


@munificent commented on Tue Aug 27 2019

In the meanwhile, there is a young library that generates sum-types (-alike classes) for you: https://github.com/werediver/sum_types.dart

Thanks for mentioning this! This is a good reference for ensuring that whatever we add to the language covers the functionality that users want to see.


@rrousselGit commented on Tue Aug 27 2019

If existing packages are interesting to see, here's a bunch of them:


@wkornewald commented on Fri Sep 13 2019

Since you're collecting information, I'd like to mention my "proposal" where I suggested how you could combine pattern matching, data classes, union types, and typedef into a coherent whole while still making each of those concepts useful individually. The result is easier to use/understand and more flexible than algebraic data types. This is what we want.

So, pattern matching should IMHO focus on these orthogonal concepts of data classes (for destructuring) and union types (for exhaustiveness).

Also, before I found this issue I already commented here on why algebraic data types result in a worse developer experience vs. union types. TL/DR: with algebraic data types, pattern matching forces you to check for cases at runtime (and raise exceptions) although they should be forbidden at compile-time! I suppose that's also relevant to this issue.


@werediver commented on Fri Sep 13 2019

@wkornewald The untagged unions concept sounds nice, but it doesn't sound like a replacement to the algebraic data types. They can totally coexist and serve different needs.

(not clear which topic is more relevant, so put in both #546 and #83)


@wkornewald commented on Fri Sep 13 2019

I'd say it's more relevant to #83, so let's discuss over there.


@modulovalue commented on Fri Sep 13 2019

I haven't seen lenses mentioned anywhere before when data classes are being discussed. I strongly believe that they would greatly benefit from 'boilerplate free' lenses.
This package attempts to solve that by generating data classes and a lens for each property functional_data.
Another implementation for more advanced lenses can be found in the package dartz and an example can be found here.


@wkornewald commented on Fri Sep 13 2019

@modulovalue That's a great suggestion. Lenses would be very helpful for safe and ergonomic state management (e.g. for my flutter_simple_state package or, if you prefer, something similar to immutable.js + Omniscient in TypeScript). Unfortunately, it's seemingly impossible to define a lens that can wrap arbitrary (including 3rd-party) data structures / objects. Well, at least without some compile-time code generation...

Maybe lenses should be a separate issue, though?


@g5becks commented on Sat Sep 21 2019

@modulovalue Whats the benefit of lenses over record copying semantics of F# over lenses.
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/copy-and-update-record-expressions ?


@spkersten commented on Sat Sep 21 2019

I’m not familiar with it F#, but what is shown on the linked page looks
very similar to copyWith method. Lenses are much more powerful. They
allow you to change values deep within a nested data structure. See the
example in the functional_data package.

On Sat, 21 Sep 2019 at 06:08, Gary Becks [email protected] wrote:

@modulovalue https://github.com/modulovalue Whats the benefit of lenses
over record copying semantics of F# over lenses.

https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/copy-and-update-record-expressions
?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/dart-lang/language/issues/546?email_source=notifications&email_token=ABCADABNFAIEQD2WZVXNORTQKWM5TA5CNFSM4IPYOCZ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7IJ4IA#issuecomment-533765664,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABCADABIBLYRWKW5JK7AR4TQKWM5TANCNFSM4IPYOCZQ
.


@modulovalue commented on Sat Sep 21 2019

I'm also not too familiar with F# but as spkersten said, they are much more flexible by being a more general abstraction.

Take a look at the following code snippet:

class Foo {
  final Bar bar;
}

class Bar {
  final Baz baz;
}

class Baz {
  final String content;
}

A Lens of type Lens<Foo, String> removes the fact that a Bar exists. It lets me access the content of Foo by just having an instance of Foo. The consumer doesn't even care that Baz exists, or where content is. Any changes to the API would not break updateFoo.

Foo updateFoo(Lens<Foo, String> lens, Foo foo) {
  return lens.update(foo, "New Content");
}

Lenses also pave the way for many other abstractions like the FocusedLens in spkerstens package. Lenses can also be chained which I believe is not possible with copy and update expressions in F#.

But as I mentioned, creating such lenses is too verbose right now. I'd ideally want something like Foo$.bar.baz.content.

--
@wkornewald said

Unfortunately, it's seemingly impossible to define a lens that can wrap arbitrary (including 3rd-party) > data structures / objects. Well, at least without some compile-time code generation...
Maybe lenses should be a separate issue, though?

I don't see how the dart team could add support for lenses right now as doing that in the OOP context of Dart only makes sense for data classes. There's not a lot I can say other than asking politely to consider the strengths of this abstraction when implementing data classes.


@g5becks commented on Sat Sep 21 2019

@spkersten Very cool package, I didn't even know that existed!

@modulovalue

I understand what lenses are, but I don't understand what the use case is at a language level vs baked in copyWith ? Even the most functional of languages don't have lens support at the language level. Haskell uses the lens library, Scala uses monocle, F# uses aether, Ocaml uses lens.

Edit... Considering the OO nature of Dart, I do see why this would be more important for dart than it would be in a more functional language, I don't tend to use any inheritance when writing functional code and my records are always designed to be small enough to not need lenses and also not to negatively affect performance. That said, I agree with your point about the team not being able to implement such a feature, would be nice though.


@jifalops commented on Mon Oct 07 2019

Can someone comment on the layers that have an effect on the Dart language as described in the grammar to the Dart language I experience as a developer? For instance the analyzer seems to add restrictions, such as not allowing late. I haven't searched the space satisfactorily but my recollection is that the grammar allows things the VS code plugin doesn't.

Another question: Would patterns allow default type parameters like A<B=X, C=Y>? Is this a thing? I want it to be a thing. Then I can have A => A<X,Y>() and A<Z> => A<Z,Y> I'm not sure about a named syntax, maybe A<C:=Z> => A<X,Z>? That looks ugly and maybe positional only would be best. This just came up because I want to have Graph<V, E=V> so that unweighted graphs can be described as Graph<V>, and that means I don't have to create classes solely for hiding the second type parameter.

I looked into other grammars from ANTLR's catalog, and ones with patterns (scala, C#) didn't seem to do anything out of the ordinary in their grammar. Is there a certain way of connecting rules?

Edit Sorry if this is rather open ended and asking for a volume of info, I hope I didn't derail the thread.


@burakemir commented on Tue Oct 08 2019

I very much like the proposal to add a form of (ML style) algebraic data types - or even features that would let one encode algebraic data types in a convenient way.

I'd like to point out some "hooks" in the language spec that would make it easy to add ML style algebraic data types.

  • The current spec (version 2.2) talks about enums (Section 13 Enums) being translated to classes; this is already very close to what a "data class" translation would be like.

The following example uses a syntax that is borrowed from Scala 3 (see http://dotty.epfl.ch/docs/reference/enums/adts.html ...) but swift (https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html) and Kotlin (https://kotlinlang.org/docs/reference/enum-classes.html) take a similar route.

enum Color {
  case Red
  case Green
  case Blue
  case Mix(int mix)
}

// alternatively:
enum Color(int rgb) {
  case Red extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue extends Color(0x0000FF)
  case Mix(int mix) extends Color(mix)
}

An enum either defines each case using the "case" keyword, or none (which would mean, it's a traditional enum as before). We'd extend the enum translation to create one subclass per case, e.g. (only doing the second example)

class Color {
  final int rgb;
  Color(this.rgb);
}

class Red extends Color {
   Red() : super(0xFF0000);
   // appropriate equals and hashcode implementations
}

class Mix extends Color {
  final int mix;
  Mix(this.mix) : super(mix);
  // ...
}

Instead of the verbose inline "extends", one could use the syntax for super constructors and say case Red : super(0xFF0000) right away. However it would be harder to specialize generic arguments, which is useful:

enum Expr<T> {
  case Number(int n) extends Expr<Int>;
  case String(string s) extends Expr<String>;
}
  • Now, if we wanted data classes, it could make sense to introduce the "enum class"
enum class JustData(int foo, string bar) : SomeSuper(foo) {
  // normal class body, but ... no mutable fields
  JustData.extra(string fooArg) : this(_parse(fooArg))
}

Again, the same class translation that is spec'ed for "enum" would be used (which somewhat justifies the use of the num keyword).

class JustData extends SomeSuper {
  final int foo;
  final string bar; 

  JustData(this.foo, this.bar) : super(foo);
  JustData.extra(string fooArg) : this(_parse(fooArg))
  // appropriate equals and hashcode
  // normal class body
}
  • A suitable syntax for patterns would include destructuring of case classes (whether they are in an enum or outside). Exhaustivity checking only applies to things that are defined within an enum (which takes care of "sealed" scenario). A pattern Foo(p1, ..., pN) would always match a Foo whose main constructor was called with e1, ..., eN and each e_i matches the p_i.

  • a pattern matching expression (whether it's switch or match) should definitely be an expression, i.e. produce a value.

  • a pattern matching expression can be translated in a nested tree of type tests and binding statements. We can add various forms of runtime tests to the pattern syntax (including type tests and and type binding, since that is available at runtime in dart). One can do an "optimizing" translation where the result of a type test is used for the rest of the match so never has to be needlessly repeated (it is possible to specify this as a requirement, however, it would be easier to leave it to implementations).

  • Tuples are nothing but standalone enum classes with the appropriate number of components. (e1, ..., eN) gets desugared into TupleN(e1, ..., eN)

enum class Tuple2<T1, T2>(T1 part1, T2 part2);
  • About user-defined destructuring: a long time ago, when unapply was added to Scala, it was done via user-defined functions that would return Option or Option<TupleN<T1, ..., TN>> for N >= 2 ... it would be good if such user-defined destructuring functions would not contain any side-effects, though.
enum Option<X> {
  case Some(X x)
  case None
}

enum Tree {
  case Leaf;
  case Node(Tree left, Tree right);
}

@unapply Option[Tree] isSpecial(Node tree) {
  return  tree.left == tree.right
    ? Some(tree.left)
    : None
}

exp match {
  case Leaf => ...
  case .isSpecial(tree) => ...  // we already know that "tree is Node"
  case Node(left, right) => ...
}
  • since the cases inside enum and also the "enum classes" are just classes, nothing special needs to be done for runtime representation.

I will leave it at that for now - hope this is helpful. Let me close with the statement that enum corresponds closely to the sum type from type theory (or disjoint union from set theory), so in my mind it's not a coincidence that all these languages have extended "enum" to cover case distinction in data modeling.


@munificent commented on Thu Oct 10 2019

Thanks for the comment. I'm not sure if we literally want to build on Dart's existing enum syntax (though I'm not fundamentally opposed either). But, otherwise, we seem to be thinking in the same direction.


@g5becks commented on Fri Nov 01 2019

Thought I’d share something I came across today while browsing through facebooks’ skiplang docs. This might be an interesting approach for adt’s. Of course , using the keyword “children” wouldn’t be an option because of flutter and what not, but maybe something else would work. If nothing else, I think it’s worth a look.


@munificent commented on Fri Nov 01 2019

Very interesting, thanks @g5becks!


@rrousselGit commented on Fri Nov 01 2019

For some reason, extension methods helped quite a bit in that area.

For example, we can write:

extension UnionValue<A> on Union2<A, A> {
  A get value => _value as A;
}

class Union2<A, B> {
  Object _value;
}

Which has interesting properties:

Union2<String, int> example;
// String and int have no shared interface, so value is of type Object
Object value = example.value;
int impossible = example.value; // COMPILE ERROR, not type safe

Union2<int, double> example2;
// int and double have a common interface: num. Therefore value is of type num
num value2 = example2.value; // valid and type safe
int stillImpossible = example2.value; // COMPILE ERROR, value is a num not a int

Union2<String, String> example3;
// The type is always a String, so value is a String too
String value3 = example3.value; // still type safe

See union for a package using that pattern.

The only thing we're lacking for now is:

  • being able to assign Union2<String, int> to Union2<int, String>
  • being able to assign Union2<String, int> to Union3<String, int, whatever>

The first one is not too important, as we can easily make a method that transform Union2<String, int> in Union2<int, String>.

The most important one being the second one (Union2 to Union3+)

Maybe a simplification of the feature could be an "any"-like, so that we can do:

class Union2<A, B> implements Union3<A, B, any> {}

which would allow:

Union2<String, int> union2;
Union3<String, int, double> union3 = union2;

@g5becks commented on Fri Nov 01 2019

@rrousselGit not sure how that fills the need for adt’s. E.G. how can you create a Result type without all the boilerplate it would take wrt the class hierarchy? I think the extension methods help fill the need of pattern matching for the interim, but not with the creation of what you are matching against. Or at least not for the use cases I think they would be used for mostly.

From the looks of it (I could be wrong) a Union<A,A> that has multiple constructors is an And type and not an Or type? Looks more like a Tuple type than a Choice type.


@rrousselGit commented on Thu Nov 07 2019

@g5becks No, this is not a Tuple but an actual union.
See union

TL;DR Union2<String, int> is manipulated with custom methods.
The typical switch case on the different types a union can take is performed this way:

Union2<String, int> myUnion;

myUnion.switchCase(
  (String value) => print("got a string $value"), 
  (int value) => print("got an integer $value"), 
);

It also has an example of a result-like type https://github.com/rrousselGit/union#making-a-reusable-union


@g5becks commented on Fri Nov 01 2019

@rrousselGit i should have read closer! That’s actually pretty cool. I think I’ll definitely be using this.


@rrousselGit commented on Thu Nov 07 2019

Had another go at this recently.
Interestingly, we can write extensions for functions too.

Which means we can use functions instead of classes to implement our temporary unions.

Which also means we can write:

Union2<String, int> union2;

Union3<String, int, double> union3 = union2; // upcast, no problem
Union4<String, int, double, Object> union4 = union2; // still works

while still being unable to write:

Union3<String, int, double> union3;

// downcast, does not work because the value can be a double too.
Union2<String, int> union2 = union3;

It's sad that Dart doesn't support well typedefs though. It'd be almost perfect: https://github.com/rrousselGit/union#custom-unions


@jogboms commented on Thu Nov 07 2019

True, nice work! @rrousselGit

Also patiently waiting on the typedefs of typedefs feature.


@xsahil03x commented on Fri Nov 15 2019

I also faced these issues and handled using the factory constructors mentioned by @rrousselGit in a StackOverflow post and was pretty satisfied with the results. To avoid the boilerplate, I built a code generator with my colleague which generates these classes by annotating Enums.

@superEnum
enum _MoviesResponse {
  @Data(fields: [DataField('movies', Movies)])
  Success,
  @object
  Unauthorized,
  @object
  NoNetwork,
  @Data(fields: [DataField('exception', Exception)])
  UnexpectedException
}

where:-

@Data() marks an enum value to be treated as a Data class.

  • One should supply a list of possible fields inside the annotation.

  • If you don't want to add fields, use @object annotation instead.

  • Fields are supplied in the form of DataField objects.

  • Each DataField must contain the name and the type of the field.

  • If the field type needs to be generic use Generic type and annotate the enum value with @generic annotation.

@object marks an enum value to be treated as an object.

Then it can be used easily with the generated when function

 moviesResponse.when(
    success: (data) => print('Total Movies: ${data.movies.totalPages}'),
    unauthorized: (_) => print('Invalid ApiKey'),
    noNetwork: (_) => print(
      'No Internet, Please check your internet connection',
    ),
    unexpectedException: (error) => print(error.exception),
  );

@werediver commented on Sat Nov 16 2019

@xsahil03x You may like another solution mentioned earlier in this thread: sum_types (also on pub.dev). It has proven to be very useful in practice and (by now) it has noticeably less limitations then the solution outlined in your message. Hope you'll find it suitable for you needs!


@xsahil03x commented on Sat Nov 16 2019

@werediver I tried sum_types but I didn't like the boilerplate we have to write for generating the classes and the boilerplate looks redundant to me. Whereas in super_enum we are using enums to replicate the same and it feels easy and not redundant at all as we are using the enum itself to generate exhaustive when statement.

As I am coming from the native development, annotating enums feel much similar to me than creating bunch of classes as boilerplate.


@werediver commented on Sat Nov 16 2019

@xsahil03x There are reasons behind the design of sum_types (the required boilerplate is a single class with case-constructors, not sure why you mentioned a bunch of classes). The boilerplate buys it compatibility with other code generators (e.g. functional_data) and extensibility (user-defined members). But surely you have your reasons behind the design of super_enum.

A less related feature of sum_types is that it facilitates arbitrary serialization-deserialization of the generated sum-types (as in Nat.fromJson() / Nat.toJson()). That can mostly be implemented with your approach as well (which is similar the approach of sum_types v0.1.x).

Update implementations of Stream.{lastWhere, firstWhere} for Dart 2

The current transitional state of lastWhere and firstWhere from lib/src/typed/stream.dart looks like this:

   Future<T> firstWhere(bool test(T element),  {Object defaultValue(), T orElse()}) =>
       _stream.firstWhere(_validateType(test), defaultValue: defaultValue ?? orElse);

We are deprecating (and will soon remove) the defaultValue argument. So at a minimum this code needs to be updated to:

   Future<T> firstWhere(bool test(T element),  {Object defaultValue(), T orElse()}) =>
       _stream.firstWhere(_validateType(test), orElse: orElse ?? () => defaultValue());

Presumably at some point we will want to deprecate and remove defaultValue from the TypeSafeStream API as well. Presumably we will need a major version bump when we remove it, since it's an API breaking change.

@nex3 would you prefer that I do this:

  • in one step
    • remove defaultValue entirely, bump the major version
  • in two steps
    • remove the delegating use of defaultValue but leave it in the API marked deprecated
    • at some later point (possibly as part of a collected major release) remove defaultValue

FR: DebounceStreamTransformer

There are probably a dozen or so implementations of this internally today, most like:

stream.transform(new DebounceStreamTransformer(...))

or

stream.transform(debounce())

The primary needs seem to be specifying a duration in which no other event on the stream should have been added - I don't see too many other patterns. We could also make a Debouncer class so users that don't use Stream still would get a benefit.

Add a "backoff" Stream, i.e. one that fires less and less

API

Based slightly on https://www.npmjs.com/package/observable-backoff.

/// Returns a stream that emits the value returned by [action].
///
/// If the new value equals the previous, [ease] is used to lengthen the delay until [action] is
/// called again, up to a total of times specified by [retries].
Stream<T> backoff(FutureOr<T> action(), {
  Equality equality,
  double ease(double input),
  int retries,
}) => ...

Not ready to commit to a specific implementation/API yet, just through up here for discussion.

StreamGroup.merge([1st ,2nd]) doesn't merge the two streams. but instead returns the 2nd stream

`Widget makeStream(){
for (int i =0 ; i <queryKeys.length; i++ ){
Stream x = firestoreCollection.where("query", arrayContains:queryKeys[i]).snapshots();
streamList.add(x);
}
var x = StreamGroup.merge(streamList);
return keywordSearch(x);
}
static Widget keywordSearch(Stream streamGroup){

return StreamBuilder(stream: streamGroup, builder: (BuildContext context, AsyncSnapshot snapshot){
if(!snapshot.hasData){
return MyLoadingWidget();
}
final int length = snapshot.data.documents.length;
return ListView.builder
itemCount: length,
itemBuilder: (_,int index){
final DocumentSnapshot doc = snapshot.data.documents[index];
print(doc.data.toString()); //(the print shows that the retrieved json is from only one stream)----
return new Container(child: Text(doc.data.toString()));
},
);
},

);`

async 1.13.0 breaks tour of heroes

I'm trying to run Tour of Heroes with async version 1.13.0 and I'm getting this error when the app tries to load in dartium:

'package:async/src/byte_collector.dart': error: line 42 pos 16: generic functions not supported
T _collectBytes<T>(
           ^: package:async/src/byte_collector.dart

Going back to version 1.12.0 allows the app to run.

Feature request: Flattening StreamQueue

When dealing with unaligned or highly-compressed binary inputs, it's usually convenient to request data byte-by-byte. Of course, doing this directly on IO level is quite limiting, so an intermediate caching layer is desired.

Ad-hoc combination of StreamQueue with a simple wrapper helps with this like:

Stream<List<int>> chunkStream; // input stream from file or network IO

Stream<int> _byteStream(Stream<List<int>> chunkStream) async* {
  await for (final chunk in chunkStream) {
    for (final byte in chunk) {
      yield byte;
    }
  }
}

final byteQueue = StreamQueue<int>(_byteStream(chunkStream));

It would be nice to have this functionality properly implemented internally without extra wrapping overhead:

factory StreamQueue.flatten(Stream<Iterable<T>> source); 

Similar feature is already implemented in package:collection for regular iterables, see CombinedIterableView.

Similar feature may also be implemented as a wrapper for StreamIterator from the core SDK for those who do not need queues.

RestartableTimer#tick not implemented

Hello, today I read the official documentation and found that Timer has an implementation class RestartableTimer, and then try to use it, I imported dart:async, because Timer is inside the dart:async package. But can't find the RestartableTimer class. Why not add the RestartableTimer to the dart:async package?
Also, the get tick of RestartableTimer will throw an exception, I found it based on the comment. But Timer's get tick is normal.

RestartableTimer#tick

@override
// TODO: Dart 2.0 requires this method to be implemented.
// See https://github.com/dart-lang/sdk/issues/31664
// ignore: override_on_non_overriding_getter
int get tick {
  throw UnimplementedError("tick");
}

Publish 2.1.0

Any reason why 2.1.0 isn't published? Would like the type signature fix for CancelableOperation.valueOrCancellation()

0f4c6fb

CancelableOperation should not cause deadlocks

i think the fact that something like that is possible is severely dangerous

API should be changed in some way to prevent this

Future<void> main() async {
  final completer = CancelableCompleter();
  Future.delayed(const Duration(seconds: 2), () {
    completer.operation.cancel();
  });
  Future.delayed(const Duration(seconds: 4), () {
    // makes no effect
    completer.complete('some value');
  });
  print('init');
  final cancel = await completer.operation.valueOrCancellation();
  print(cancel);
  final value = await completer.operation.value;
  // these lines are never reached
  print(value);
  print('end');
}

Compile-time errors with Dart 2

With dart 1f70b7ad378f37828cc9a3768910841a2b3b8dfc, async 2.0.3

third_party/dart-pkg/pub/async/lib/src/typed/stream.dart:56:10: Error: The return type of the method 'TypeSafeStream::firstWhere' is dart.async::Future<dynamic>, which does not match the return type of the overridden method (dart.async::Future<#lib1::TypeSafeStream::T>).
Change to a subtype of dart.async::Future<#lib1::TypeSafeStream::T>.
  Future firstWhere(bool test(T element), {Object defaultValue()}) =>
         ^
third_party/dart/sdk/lib/async/stream.dart: Error: This is the overriden method ('firstWhere').
third_party/dart-pkg/pub/async/lib/src/typed/stream.dart:56:10: Error: The method 'TypeSafeStream::firstWhere' has fewer named arguments than those of overridden method 'Stream::firstWhere'.
  Future firstWhere(bool test(T element), {Object defaultValue()}) =>
         ^
third_party/dart/sdk/lib/async/stream.dart: Error: This is the overriden method ('firstWhere').
third_party/dart-pkg/pub/async/lib/src/typed/stream.dart:59:10: Error: The return type of the method 'TypeSafeStream::lastWhere' is dart.async::Future<dynamic>, which does not match the return type of the overridden method (dart.async::Future<#lib1::TypeSafeStream::T>).
Change to a subtype of dart.async::Future<#lib1::TypeSafeStream::T>.
  Future lastWhere(bool test(T element), {Object defaultValue()}) =>
         ^
third_party/dart/sdk/lib/async/stream.dart: Error: This is the overriden method ('lastWhere').
third_party/dart-pkg/pub/async/lib/src/typed/stream.dart:59:10: Error: The method 'TypeSafeStream::lastWhere' has fewer named arguments than those of overridden method 'Stream::lastWhere'.
  Future lastWhere(bool test(T element), {Object defaultValue()}) =>
         ^
third_party/dart/sdk/lib/async/stream.dart: Error: This is the overriden method ('lastWhere').
third_party/dart-pkg/pub/async/lib/src/typed/stream.dart:62:13: Error: The method 'TypeSafeStream::singleWhere' has fewer named arguments than those of overridden method 'Stream::singleWhere'.
  Future<T> singleWhere(bool test(T element)) async =>
            ^
third_party/dart/sdk/lib/async/stream.dart: Error: This is the overriden method ('singleWhere').

@lrhn

Idea: FutureSwitch: switchMap for Futures

If often want the behavior of switchMap, but it isn't convenient to create a Stream to feed into it. Especially if there are multiple places in the code that produce an "input event". CancellableOperation is a close fit, but it doesn't have a convenient "switch" or mechanism.

Example use case:

final FutureSwitch dataSwitch = FutureSwitch();

// controls a "loading" indicator
bool get isLoading => dataSwitch.isPending;

Future onClickLoad(someInput) async {
  var future = fetchData(someInput);
  var data = await dataSwitch.switchTo(future, onCancel: () => print('cancelled'));
  showData(data);
}

Partial implementation:

class FutureSwitch {
  StreamSubscription _sub;
  void Function() _onCancel;

  Future<T> switchTo<T>(Future<T> future, {void onCancel()}) {
    cancel();
    _onCancel = onCancel;
    var completer = new Completer<T>();
    _sub = future.asStream().listen(completer.complete,
        onError: completer.completeError, onDone: () {
      _sub = null;
      _onCancel = null;
    });
    return completer.future;
  }

  void cancel() {
    if (_sub != null) {
      _sub.cancel();
      _sub = null;
      if (_onCancel != null) {
        _onCancel();
        _onCancel = null;
      }
    }
  }
}

CancelableOperation.isCompleted is True Before Future Completes

CancelableOperation.isCompleted returns true immediately after constructing the operation even if the future it was constructed with hasn't resolved. This was unexpected to me, and I assume this is not the intended behavior. Below is a little unit test that demonstrates the problem. The expectation on the final line of the test fails, though I would expect it to succeed.

import 'dart:async';

import 'package:async/async.dart';
import 'package:test/test.dart';

void main() {
  group('CancelableOperation', () {
    test('isComplete should be false before completed', () {
      var completer = Completer<void>();
      var operation = CancelableOperation.fromFuture(completer.future);
      expect(operation.isCompleted, isFalse);
    });
  });
}

Run tests with DDC

There are some potential Strong mode issues within this package. We should run tests with DDC.

DelegatingStream.asBroadcastStream() calls wrapped stream directly

  • Dart SDK Version (dart --version)
    2.2.1-dev.2.0+google3-v2.2.1.dev.2.0

Consider the following test:

class TestStream<T> extends DelegatingStream<T> {
  bool listenCalled = false;

  TestStream(Stream<T> stream) : super(stream);

  @override
  StreamSubscription<T> listen(void onData(T event),
      {Function onError, void onDone(), bool cancelOnError}) {
    listenCalled = true;
    return super.listen(onData,
        onError: onError, onDone: onDone, cancelOnError: cancelOnError);
  }
}

main() {
  test('asBroadcastStream should subscribe to TestStream', () {
    final stream = TestStream(Stream.fromIterable([1, 2, 3]);
    stream.asBroadcastStream().listen((_) {});
    expect(stream.listenCalled, isTrue);
  });
}

This fails with:

  Expected: true
    Actual: false

The problem is StreamView which DelegatingStream extends from overrides asBroadcastStream to directly use the wrapped stream, not itself. DelegatingStream should not extend StreamView, it should just override listen and isBroadcast.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.