zug icon indicating copy to clipboard operation
zug copied to clipboard

How would one implement flatMap or bind with zug?

Open bradphelan opened this issue 3 years ago • 7 comments

For example I'm looking for an equivalent to

std::vector<int> src = {1,2,3,4,5,6};

std::vector<int> result = 
    src 
    | flatMap([](int i){ return i%2 == 0 ? std::vector<int>{i,i+1} : std::vector<int>{i} ; })
    | toVector();

result should be

{1,2,3,3,4,5,5,6,7}

ie: flatMap (also known as bind) takes each element of the source and passes it to the function which is expected to return a sequence and all sequences are concatenated together.

Though reading through your doc maybe the way to do this is with map and then cat like.

std::vector<int> src = {1,2,3,4,5,6};
    | map([](int i){ return i%2 == 0 ? std::vector<int>{i,i+1} : std::vector<int>{i} ; })
    | cat();

I notice that you have a mapcat but it doesn't do the same as above. Maybe it should be renamed catmap because it is equivalent to cat | map rather than map | cat

I have my own two libraries that do the same as transducers with one for space mapping and one for temporal mapping. But I'm intrigued by the design of transducers that allows one implementation to handle both by factoring out the processes. I need to spend some time with it.

A small point. Somehow I prefer the term where rather than filter. I'm always have to think twice with filter whether the predicate means to include all things that return positive or exclude them. I think the problem is that filter is most often used with predicates in English as filter out which has the opposite meaning to where and filter as often used in libraries like transducers.

All dogs where tail length is longer than 20cm

vs

Filter out all dogs with tail length longer than 20cm

However I'm aware than different libraries use filter and where interchangeably so maybe it's just me.

bradphelan avatar May 10 '21 06:05 bradphelan

Hi @bradphelan!

The name mapcat is taken from Clojure https://clojuredocs.org/clojure.core/mapcat

Note that in zug one can't really do vector{...} | map(...) | cat since a vector is not pipeable with a function. | in zug means no more than function composition (transducers are just funny functions). In zug you can instead do:

auto v1 = vector{...};
auto v2 = into_vector(cat | map(...), v1);

Cheers!

arximboldi avatar May 10 '21 13:05 arximboldi

A small point. Somehow I prefer the term where rather than filter. I'm always have to think twice with filter whether the predicate means to include all things that return positive or exclude them. I think the problem is that filter is most often used with predicates in English as filter out which has the opposite meaning to where and filter as often used in libraries like transducers.

I agree conceptually but since transducers are a concept from Clojure, I'm happy to keep the names close to the Clojure's standard library.

arximboldi avatar May 10 '21 13:05 arximboldi

You have the incorrect implementation of mapcat then if you want to mimic the clojure implementation.

https://sinusoid.es/zug/transducer.html#mapcat

The example you give is

''' auto v = std::vector<std::vector>{{1, 2}, {3}, {4, 5, 6}}; auto res = into_vector(mapcat([](int x) { return x * 2; }), v); CHECK(res == (std::vector{2, 4, 6, 8, 10, 12})); '''

Which is not correct. This is what puzzled me in the first place. Mapcat should be the same as flatmap or monadic bind as it is often known. IE: the lambda should return a collection from each item and then the collections are concatenated together.

If you read the clojure docs this is what they say.

''' Returns the result of applying concat to the result of applying map to f and colls. Thus function f should return a collection. Returns a transducer when no collections are provided '""

https://stackoverflow.com/questions/20363205/what-are-the-differences-between-mapcat-in-clojure-and-flatmap-in-scala-in-terms

bradphelan avatar May 10 '21 17:05 bradphelan

You're right! Sorry about that. Reopening the issue so I can tackle this soon.

arximboldi avatar May 10 '21 21:05 arximboldi

And thanks for the report!

arximboldi avatar May 10 '21 21:05 arximboldi

As an aside. I found this while googling.

https://english.stackexchange.com/questions/194901/sieve-vs-filter-are-they-opposites

the main argument seems to be that sieve or filter refers to the act not the result thus to be precise one must say select or reject or where or filter out to describe which side of the partitioned set one is choosing.

bradphelan avatar May 17 '21 07:05 bradphelan

I quite agree with your remark about the misleading semantics behind "filter" (hence error-prone). Reusing a term because it is "standard" is not a good argument IMHO. Else, when do we change the world? ;)

Philippe91 avatar May 17 '21 08:05 Philippe91