Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How would one implement flatMap or bind with zug? #28

Open
bradphelan opened this issue May 10, 2021 · 7 comments
Open

How would one implement flatMap or bind with zug? #28

bradphelan opened this issue May 10, 2021 · 7 comments
Labels
enhancement New feature or request

Comments

@bradphelan
Copy link

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.

@arximboldi
Copy link
Owner

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
Copy link
Owner

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.

@bradphelan
Copy link
Author

bradphelan commented May 10, 2021

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

@arximboldi arximboldi reopened this May 10, 2021
@arximboldi
Copy link
Owner

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

@arximboldi
Copy link
Owner

And thanks for the report!

@bradphelan
Copy link
Author

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.

@Philippe91
Copy link

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? ;)

@arximboldi arximboldi added the enhancement New feature or request label Jan 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants