Now I'm thinking that IO<IO<T>> can actually model observable collections (not related to Rx, implementing INotifyCollectionChanged interface) with events being 'element is inside collection at specific position'. Then we can get LINQ to observable collections for free.
@bdesmet:About GroupBy and Buffer methods. That's the best what I can come up with:
The elements of IE<T> and IO<T> can be associated with additional values: index and time (for IO<T>). These values arise from specific properties of Bind operator of each monad.
For IE<T> Bind preserves internal ordering of elements within containers. It is possible to write non-preserving version too (and Px has that version AFAIR). Even inside standart LINQ not all operators respect the order (for example, Union and other set-related operators but that's because they should belong to different monad). However basic operations do preserve the order.
For IO<T> as events have global order (they can be ordered by time when an event happened) Bind preserves that global order.
Buffer operators do partitioning based on these additional values and preserve the ordering while GroupBy family does not.
First of all the easy part: not so catastrophic version of Aggregate is Scan.
On lazyness as the main monadic aspect of IEnumerable. The problem is that both IEnumerable and IObservable are quite complex creations. There are at least three (maybe more) aspects in IEnumerable (like returning multiple values, handling exceptions and deferring side-effects) and at least four (or even five) in IObservable. Had we used haskell (not that I know it), instead of IEnumerable we'd written something like List<Error<IO<T>>> (or sometimes Set<Error<IO<T>>>). In Ix there are a lot of operators that leave one monadic aspect or the other. Like Catch leaves Error monad, or SumEnumerable leaves List monad (but not IO monad) - in some sense these are catamorphisms. Of course, if we're talking of IO as a main part of IEnumerable monad then indeed Cata would equal to unsafePerformIO.
What I was trying to say in previous post, given that the only way to deconstuct a monadic value is Cata, you'd had to use it somehow in the implementation of Bind.
By the way I think it can be good exercise to write approximate signatures of Ix and Rx operators in haskell, taking side-effects and exceptions into account.
Erik's paper is my reading list (already quite some time). I yet had to get used to all hylomorphisms and co.
@bdesmet: On difference between GroupBy and Buffer* methods. GroupBy adds new "monadic layer", so it's signature (not taking Keys into account) should be M<T> -> T -> TKey -> M<M<T>>. It looks like specialized version of Ana applied to already existing monadic value as a seed.
Buffer is a different beast as it adds takes M<T> and produces M<List<T>>.
Using Cata is rarely useful in operators itself, and in fact harmful as it breaks laziness. One exception is where you got to see the whole sequence anyway before you can produce any output.
Didn't get that part. If Cata itself is lazy there should not be any problems. Or you mean that lazy invocations of Cata are usefull and direct invocations are harmfull?
Correct me if I'm wrong, but I thought that for every monad there exist Ana-Cata pair and every monadic operation may be written in terms of Ana and Cata. However in general there's no easy way to find Ana and Cata for a given monad.
assume your team has only the budget to use one try...catch block in the entire codebase of your LINQ to Objects implementation of the Standard Query Operators (i.e. IEnumerable<T> based). How'd you spend it (*) such that you can easily implement error-handling operators in the future?
That can be done using operator analogous to Materialize of Ix. It makes transition from implicit exception-handling mode to explicit Error monad.
I think that there are two different things that got mixed:
1) similarity of definitions of co- and contravariance in physics and programming
2) similarity of coordinatization to going to Monad
It should be possible to prove first without considering second. When we do Select on IEnumerable<> we're allowed to change type of element. Coordinatization might be treated similarly to such type change.
If we're talking about second then we probably need to take into account not only coordinatization of positions but also linear approximation needed to make continuous versions of
f and p.
I think that we should operate not on values but on functions, and there should be different type constructors when we're talking about time and fuel.
Something like this, e.g. for fuel CF<P,C(P)> = (P -> F) -> (C(P) -> F) .
Values of such types should describe how function from position to fuel changes when we switch coordinate system from P to C(P)
And then we should prove that corresponding functor is co-variant.
Or, probably that's overcomplicated. Maybe type constructor CF<P> = P -> F is sufficient. Then to define functor we need mapping CF:: P -> (P -> F) that is for each coordinate system we should have function from position to fuel. And then we need to show
that CF:: (P -> CP) -> ((P->F) -> (CP->F)) is co-variant functor. Hm, type parameter P is in input position. There should be contra-variance then.