none
Interactive Extensions v1.1 Experimental available now!

    General discussion

  • We're happy to announce that Ix is now back as a separate download:Interactive Extensions v1.1.10621 Experimental Release. If you're using NuGet, search for packages with the Ix_Experimental prefix.

    We've focused on restoring the functionality most people care about, eliminated a number of operators that we weren't that fond of (e.g. the *Enumerable aggregates), restored parity with Rx where appropriate, and added support for IQueryable<T> through the QueryableEx type in System.Interactive.Providers. In addition, there are more and better XML doc comments.

    Let us know what you think and let's continue to innovate together!


    using (Microsoft.Sql.Cloud.DataProgrammability.Rx) { Signature.Emit("Bart De Smet"); }
    Wednesday, June 29, 2011 6:11 PM
    Owner

All replies

  • Oh, there it is... I've thought NuGet was delayed. :)

    Well, from the first rapid view, it has everything I need, which is great.

    BTW, discovered the new Expand method which should be awesome for flattening trees. Very good.

    Wednesday, June 29, 2011 6:59 PM
  • Beautiful, Great work!
    Anything is perfect.

    I may think it to be a problem to some extent.

    Scan

    Behavior of Scan is different from Rx in Ix.

     

    // 3, 6
    Enumerable.Range(1, 3).Scan((x, y) => x + y).ForEach(Console.WriteLine);
    // 1, 3, 6
    Observable.Range(1, 3).Scan((x, y) => x + y).ForEach(Console.WriteLine);
    

    When I put seed, it is the same behavior, however thinks Rx to match Ix with Scan of F# together.

     

    // 1, 3, 6
    Enumerable.Range(1, 3).Scan(0, (x, y) => x + y).ForEach(Console.WriteLine);
    // 1, 3, 6
    Observable.Range(1, 3).Scan(0, (x, y) => x + y).ForEach(Console.WriteLine);
    // F#: [0; 1; 3; 6]
    [1..3] |> List.scan (fun x y -> x + y) 0
    

    I understand the circumstances that included Scan0 before.
    However, you should include seed if there is not it.

    Empty and Repeat

    When Repeat empty sequence, result is freeze.
    Is this behavior by design?

     

    // 1, 1, 1
    EnumerableEx.Return(1)
     .Repeat()
     .Take(3)
     .ForEach(Console.WriteLine);
    
    // freeze, infinity loop!
    Enumerable.Empty<int>()
     .Repeat()
     .Take(3)
     .ForEach(Console.WriteLine);
    


    Rx - Observable takes similar behavior.
    I do not know what is a right design.

     

    Distinct

    Distinct's keySelector overload is very nice.
    However, I want it for a other set methods - Except, Intersect and Union.

    //

    Anyway, it is splendid work.
    Really thank you!


    Yoshifumi Kawai / neuecc
    • Edited by neueccMVP Wednesday, June 29, 2011 9:12 PM re ornamentation of the sentence
    Wednesday, June 29, 2011 9:09 PM
  • Thanks for the feedback. The observed behavior is intentional:

    • Scan: On par with Rx, you can get Scan0 behavior by composing the StartWith operator in the query.
    • Empty/Repeat: Behavior of repeat is to re-iterate the sequence, which doesn't happen to produce any results, so Take(3) can't make forward progress.
    • Distinct: Good feedback on the other uses of key selector functions. We'll think about this.

    using (Microsoft.Sql.Cloud.DataProgrammability.Rx) { Signature.Emit("Bart De Smet"); }
    Wednesday, June 29, 2011 9:24 PM
    Owner
  • Hello,

    My understanding of Publish was something like this:

    Publish(f) = MemoizeAll().Let(f)

    So I just had a bug creep up in test file in LINQPad with the new Rx and went to compare the old Publish with the new one.

    Surprise, the old publish was implemented the way I expected but not the new one.

    It would break some code I have in production. I could fix it using the new .Memoize().Let() but Let is also gone. Since I can easily implement Let myself I'm not so bothered by that but I'd rather use .Publish for performance reason. (It turns out the algorithm was originally implemented with Let and got a serious performance boost thanks to Publish).

    Is the change in the behavior intentional? If not I can try to provide a sample example that illustrate the difference with v1.0.2856.

    Thanks!

    David

    Thursday, June 30, 2011 8:06 PM
  • Publish is now mirrored after the IObservable behavior for the same operator, rooted in the deep duality once more :-). First, a sample to set the scene:

    static void Main()
    {
      var rndE = RandomE().Take(100);
      var resE = rndE.Publish(xs => xs.Zip(xs, (x, y) => x == y)).All(b => b);
      Console.WriteLine(resE);
    
      var rndO = RandomO().Take(100);
      var resO = rndO.Publish(xs => xs.Zip(xs, (x, y) => x == y)).All(b => b);
      Console.WriteLine(resO.Single());
    }
    
    static Random s_rand = new Random();
    
    static IEnumerable<int> RandomE()
    {
      while (true)
        yield return s_rand.Next();
    }
    
    static IObservable<int> RandomO()
    {
      return RandomE().ToObservable();
    }
    

    Both print True, as expected, which won't be the case if you omit the Publish operator but use a Let-alike operator instead (which we should bring back, as it's part of the experimental Rx release as well, thanks for noticing).

    The sample above doesn't quite show one essential part of the behavior though: what data the consumer of a published sequence gets to see relative to the subscription/enumeration timing. To show this, here's another example:

    static void Main()
    {
      var rndE = Enumerable.Range(0, 10).Do(x => Console.WriteLine("Got " + x));
      var resE = rndE.Publish(xs => xs.Skip(5).Take(1).SelectMany(xs));
      resE.ForEach(Console.WriteLine);
    
      Console.WriteLine();
    
      var rndO = Observable.Range(0, 10).Do(x => Console.WriteLine("Got " + x));
      var resO = rndO.Publish(xs => xs.Skip(5).Take(1).SelectMany(xs));
      resO.ForEach(Console.WriteLine);
    }
    

    Upon receiving the fifth element (which you could do in Rx using the ElementAt operator as well, but not in LINQ to Objects - in a compositional manner with SelectMany - because it's a blocking call), SelectMany will subscribe to the published sequence "xs", causing it to "tune in" from that point of the enumeration forward. In other words, the result sequence is 6, 7, 8, 9 in both cases.

    What's important to notice here is that the GetEnumerator and Subscribe calls play the same role: from that call on, the consumer takes a "lease" to observe the sequence's elements from that point forward (or until the consumer disposes the subscription or enumerator). In the case of Rx, it simply means to notify the observer. In the case of Ix, it influences the buffer policy under the hood, using a ref-counting mechanism for the elements based on the number of active enumerators, all of which keep a cursor into the shared buffer.

    As you can see, both Ix and Rx are in sync here with regards to behavior. To get the behavior you desire, you can use Memoize (which is very close to Rx's Replay, but didn't want to rename it because people are very keen on the Memoize name in Ix it seems) which now has a lambda form to. One new thing with Memoize is that you can give it a reader count to control the element lifetime in the memoized sequence. Have a look at the following sample which prints five times -5:

    var rndE = Enumerable.Range(0, 10).Do(x => Console.WriteLine("Got " + x));
    var resE = rndE.Memoize(xs => xs.Skip(5).Zip(xs, (x, y) => y - x));
    resE.ForEach(Console.WriteLine);
    

    Here, the use of Memoize causes all the elements of the source to be cached for a potentially infinite number of users. It's almost like doing ToList but with some more laziness involved. In other words, the buffer size will grow to 10 elements. However, Zip only involves the use of two readers on the memoized sequence. Therefore, when the two enumerators have consumed an element, it can be evicted from the buffer. You can communicate this intent by specifying the number of readers to Memoize:

    var rndE = Enumerable.Range(0, 10).Do(x => Console.WriteLine("Got " + x));
    var resE = rndE.Memoize(2, xs => xs.Skip(5).Zip(xs, (x, y) => y - x));
    resE.ForEach(Console.WriteLine);
    


    Finally, the Share operator shares consumption of the buffer, in that each enumerator consumes (and drops!) elements from the buffer. For example:

    var rndE = Enumerable.Range(0, 10).Do(x => Console.WriteLine("Got " + x));
    var resE = rndE.Share(xs => xs.Zip(xs, (x, y) => y - x));
    resE.ForEach(Console.WriteLine);
    

    This code will print five times 1, because the two enumerators used by Zip for the xs sequence are nicely interleaved (consume left, consume right, combine, and repeat those steps).

    Hope this helps.


    using (Microsoft.Sql.Cloud.DataProgrammability.Rx) { Signature.Emit("Bart De Smet"); }
    Thursday, June 30, 2011 8:41 PM
    Owner
  • Totally!

    Very satisfied with the naming as well and it totally seems natural to me that Memoize now accepts a lambda.

    Congratulation on the release!

    David


    Thursday, June 30, 2011 11:39 PM
  • Excellent stuff! I see some operators here that I've always felt are sorely lacking from LINQ.

    A few comments on the Generate method - since it is such a useful operation, I think it's important to provide overloads for ease of use, so people won't have to specify all of the parameters if they don't want:

    •  "resultSelector" will be rarely useful, usually people will just want the items they are generating to be yielded back to them directly. 
    • "condition" is not always useful, some people might want to make an infinite enumerable. 

    Therefore I think it should be OK to pass null to these parameters (and maybe infact null should be the default value).

    Friday, July 15, 2011 3:11 PM
  • Has the .Let() extension method been removed? Thanks.
    Tuesday, January 10, 2012 5:51 PM
  • Yes, mostly for parity with Rx. It's a trivial function anyway, and could be generalized:

    public static R Let<T, R>(T value, Func<T, R> f) { return f(value); }


    using (Microsoft.Sql.Cloud.DataProgrammability.Rx) { Signature.Emit("Bart De Smet"); }
    Wednesday, January 11, 2012 11:06 PM
    Owner
  • Is this the latest official version? from June? (or August actually on NuGet) Rx is newer, but I don't see anything else? So, I'm guessing what's on NuGet is the latest.

    • Edited by Dragan K Tuesday, April 10, 2012 4:33 PM
    Tuesday, April 10, 2012 4:30 PM
  • Edit:  Misread the title of the thread. Thought it was about Rx. For Ix, the last release is here (August 2011 build). Post Rx v2.0, we may update Ix with newer goodies.

    Version inventory:

    • If you're using Rx v1.0 Stable, the latest is the December SP1 release.
    • If you're using Rx v1.1 Experimental, the latest is the November update (v1.1.11111)
    • If you're using Rx v2.0 Beta, the latest is the March build (v2.0.20304)

    Decision factors:

    • If you need product support, v1.0 SP1 is the way forward.
    • If you want stable core v1.0 operators, but some experimental ones on top of it, v1.1 is what you want.
    • If you don't need product support and guaranteed stability of core operators, v2.0 is the latest and greatest.

    Over time, we'll phase out the experimental brand in favor of NuGet "prerelease" packages. It's a relic from days we didn't have a good way to redist the goodies that fell off the v1.0 stable ship, without messing up NuGet people (so we invented Rx_Experimental as a prefix; Entity Framework did something similar too). External libraries such as Rxx can build on top of the stable product to provide additional operators (and they can decide whether those are deemed stable or experimental or whatever).


    using (Microsoft.Sql.Cloud.DataProgrammability.Rx) { Signature.Emit("Bart De Smet"); }


    Tuesday, April 10, 2012 10:20 PM
    Owner
  • Edit:  Misread the title of the thread. Thought it was about Rx. For Ix, the last release is here (August 2011 build). Post Rx v2.0, we may update Ix with newer goodies.

    Despite the fact that the IX library is quite unpopular (less than 1k downloads according to NuGet stats) I think it is a very helpful extension that probably most of the developers out there just never heard about. At least for myself I can say that it saved me many hours and was always a pleasure to use. It would be really fantastic if the Rx team released an up-to-date version of IX later this year!

    @Bart: BTW regarding Rx V2.0: Excellent work!


    Monday, April 16, 2012 12:16 PM