Add fromBind to replace direct StreamTransformer (dart-lang/stream_transform#35)

Fixes dart-lang/stream_transform#34

`new StreamTransformer(onListen)` produces a Stream which can not know
whether it is a Broadcast stream. The `bind` method can more directly
express the intent of these transformers but adding a class is a lot of
boilerplate. Add fromBind to handle these cases.
diff --git a/pkgs/stream_transform/CHANGELOG.md b/pkgs/stream_transform/CHANGELOG.md
index 1abe605..20f2e9b 100644
--- a/pkgs/stream_transform/CHANGELOG.md
+++ b/pkgs/stream_transform/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.0.7
+
+- Bug Fix: Streams produces with `scan` and `switchMap` now correctly report
+  `isBroadcast`.
+
 ## 0.0.6
 
 - Bug Fix: Some transformers did not correctly add data to all listeners on
diff --git a/pkgs/stream_transform/lib/src/bind.dart b/pkgs/stream_transform/lib/src/bind.dart
new file mode 100644
index 0000000..603cc0f
--- /dev/null
+++ b/pkgs/stream_transform/lib/src/bind.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+/// Matches [StreamTransformer.bind].
+typedef Stream<T> Bind<S, T>(Stream<S> values);
+
+/// Creates a [StreamTransformer] which overrides [StreamTransformer.bind] to
+/// [bindFn].
+StreamTransformer<S, T> fromBind<S, T>(Bind<S, T> bindFn) =>
+    new _StreamTransformer(bindFn);
+
+class _StreamTransformer<S, T> implements StreamTransformer<S, T> {
+  final Bind<S, T> _bind;
+
+  _StreamTransformer(this._bind);
+
+  @override
+  Stream<T> bind(Stream<S> values) => _bind(values);
+}
diff --git a/pkgs/stream_transform/lib/src/scan.dart b/pkgs/stream_transform/lib/src/scan.dart
index 518d332..7c26076 100644
--- a/pkgs/stream_transform/lib/src/scan.dart
+++ b/pkgs/stream_transform/lib/src/scan.dart
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 import 'dart:async';
 
+import 'bind.dart';
+
 /// Scan is like fold, but instead of producing a single value it yields
 /// each intermediate accumulation.
 StreamTransformer<S, T> scan<S, T>(
         T initialValue, T combine(T previousValue, S element)) =>
-    new StreamTransformer<S, T>((stream, cancelOnError) {
+    fromBind((stream) {
       T accumulated = initialValue;
-      return stream
-          .map((value) => accumulated = combine(accumulated, value))
-          .listen(null, cancelOnError: cancelOnError);
+      return stream.map((value) => accumulated = combine(accumulated, value));
     });
diff --git a/pkgs/stream_transform/lib/src/switch.dart b/pkgs/stream_transform/lib/src/switch.dart
index b7fc110..193cbaa 100644
--- a/pkgs/stream_transform/lib/src/switch.dart
+++ b/pkgs/stream_transform/lib/src/switch.dart
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 import 'dart:async';
 
+import 'bind.dart';
+
 /// Maps events to a Stream and emits values from the most recently created
 /// Stream.
 ///
@@ -12,10 +14,7 @@
 /// If the source stream is a broadcast stream, the result stream will be as
 /// well, regardless of the types of the streams produced by [map].
 StreamTransformer<S, T> switchMap<S, T>(Stream<T> map(S event)) =>
-    new StreamTransformer((stream, cancelOnError) => stream
-        .map(map)
-        .transform(switchLatest())
-        .listen(null, cancelOnError: cancelOnError));
+    fromBind((stream) => stream.map(map).transform(switchLatest()));
 
 /// Emits values from the most recently emitted Stream.
 ///
diff --git a/pkgs/stream_transform/pubspec.yaml b/pkgs/stream_transform/pubspec.yaml
index 92b2cbe..c5f0a22 100644
--- a/pkgs/stream_transform/pubspec.yaml
+++ b/pkgs/stream_transform/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A collection of utilities to transform and manipulate streams.
 author: Dart Team <misc@dartlang.org>
 homepage: https://www.github.com/dart-lang/stream_transform
-version: 0.0.6
+version: 0.0.7-dev
 
 environment:
   sdk: ">=1.22.0 <2.0.0"
diff --git a/pkgs/stream_transform/test/scan_test.dart b/pkgs/stream_transform/test/scan_test.dart
index 51456f2..717b808 100644
--- a/pkgs/stream_transform/test/scan_test.dart
+++ b/pkgs/stream_transform/test/scan_test.dart
@@ -13,5 +13,13 @@
 
       expect(result, [1, 3, 6, 10]);
     });
+
+    test('can create a broadcast stream', () async {
+      var source = new StreamController.broadcast();
+
+      var transformed = source.stream.transform(scan(null, null));
+
+      expect(transformed.isBroadcast, true);
+    });
   });
 }
diff --git a/pkgs/stream_transform/test/switch_test.dart b/pkgs/stream_transform/test/switch_test.dart
index 91f10c8..ccc9877 100644
--- a/pkgs/stream_transform/test/switch_test.dart
+++ b/pkgs/stream_transform/test/switch_test.dart
@@ -138,5 +138,13 @@
       await new Future(() {});
       expect(values, [1, 2, 3, 4, 5, 6]);
     });
+
+    test('can create a broadcast stream', () async {
+      var outer = new StreamController.broadcast();
+
+      var transformed = outer.stream.transform(switchMap(null));
+
+      expect(transformed.isBroadcast, true);
+    });
   });
 }