# Define map, filter, ... also as functionals

**URL:** <https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401>\
**Category:** Internals & Design\
**Created:** [September 17, 2022, 12:26pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401 "2022-09-17T12:26:04Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![ludwig-austermann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ludwig-austermann/32/11093_2.png) [@ludwig-austermann](https://discourse.julialang.org/u/ludwig-austermann)\
**Post date:** [September 17, 2022, 12:26pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/1 "2022-09-17T12:26:04Z")

</div>

Hey all,  
I like the pipelining syntax, but I often encounter the problem that one has to write code like this:

```julia
rand(10) |> x -> filter(<=(0.5), x)

```

I see that map, filter, accumulate, findfirst etc. all take a minimum of two arguments. If I define  
`Base.filter(f::Function) = a -> filter(f,a)` the above code can be written as:

```julia
rand(10) |> filter(<=(0.5))

```

My question is if there is a reason not to implement these extensions and if there are better (existing) alternatives.

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [September 17, 2022, 1:25pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/2 "2022-09-17T13:25:08Z")

</div>

Yes, there are quite a few, e.g.

> **[GitHub - jkrumbiegel/Chain.jl: A Julia package for piping a value through a...](https://github.com/jkrumbiegel/Chain.jl)**
>
> A Julia package for piping a value through a series of transformation expressions using a more convenient syntax than Julia's native piping functionality. - GitHub - jkrumbiegel/Chain.jl: A Jul...

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [September 17, 2022, 1:39pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/3 "2022-09-17T13:39:02Z")

</div>

[Transducers.jl](https://github.com/JuliaFolds/Transducers.jl) provide that functionality:

```julia
using Transducers

1:10 |> Filter(iseven) |> Map(sqrt) |> collect

```

Transducers have been introduced by Rich Hickey in Clojure and decouple the desired transformations, i.e., `Map`, `Filter` and so on, from the actual traversal (that’s why an explicit `collect` is needed above). Thereby the same transducer can be used across different data structures\[1\] and do not create intermediate data structures, i.e.,

```julia
1:10 |> Filter(iseven) |> Map(sqrt) |> foldxl(+)

```

does not allocate.

* * *

1. Don’t know if Julia implements them for anything besides `Iterators` though. Clojure has some more options in this respect and supports transducers on channels for instance.

---

<div class="post-metadata">

**Author:** ![ludwig-austermann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ludwig-austermann/32/11093_2.png) [@ludwig-austermann](https://discourse.julialang.org/u/ludwig-austermann)\
**Post date:** [September 17, 2022, 1:54pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/4 "2022-09-17T13:54:55Z")

</div>

But why not add the simple behaviour case to the standard library?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [September 17, 2022, 3:30pm UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/5 "2022-09-17T15:30:56Z")

</div>

Because that already has a meaning, so changing that would be breaking:

```julia
julia> foo() = println("hello")
foo (generic function with 1 method)

julia> map(foo)
hello

```

If you’re wondering how you can reach that, you can splat an empty container:

```julia
julia> map(foo, []...)
hello

```

---

<div class="post-metadata">

**Author:** ![ludwig-austermann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ludwig-austermann/32/11093_2.png) [@ludwig-austermann](https://discourse.julialang.org/u/ludwig-austermann)\
**Post date:** [September 18, 2022, 12:05am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/6 "2022-09-18T00:05:59Z")

</div>

Ah, this is actually kinda obvious and, more importantly, consistent, thanks.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [September 18, 2022, 12:10am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/7 "2022-09-18T00:10:50Z")

</div>

That’s just map though. If that function were instead named `zipwith`, then `map`, `filter`, `accumulate` could all be curried, which would be nicer. No?

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [September 18, 2022, 9:34am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/8 "2022-09-18T09:34:07Z")

</div>

There are many functions both in Base and across the data ecosystem that take a function argument and a data argument. Why add a special currying method to all of them, when this can be solved once and in a clean way using one of the “piping” packages?  
I would recommend _(my)_ [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) ([discourse thread](https://discourse.julialang.org/t/ann-datapipes-jl-0-3-0/60734)) that has the main goal to make general data processing as convenient as possible. To my knowledge, `DataPipes` pipes have the least amount of code overhead compared to alternative packages. A basic example: `@p rand(10) |> filter(<=(0.5)) |> map(_ + 1)`.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [September 18, 2022, 9:58am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/9 "2022-09-18T09:58:15Z")

</div>

> [@jar1](#):
>
> That’s just map though. If that function were instead named `zipwith`, then `map`, `filter`, `accumulate` could all be curried, which would be nicer. No?

I think all of those can already be done via `Base.Fix1(foo, bar)`, which fixes the first argument of `foo` to `bar`.

To be clear, we’d have to go to every function and add such a version manually, since automatic currying does not play nice with multiple dispatch. And adding these manually can run into the problem that e.g. `map` poses which would result in inconsistencies across functions, so either writing it explicitly or having a different name seems preferrable to me.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [September 18, 2022, 11:30am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/10 "2022-09-18T11:30:56Z")

</div>

I’m also a bit puzzled by that overload too, i.e., it seems that `map(f)` is the same as `f()`. Independent of the name, i.e., `zipWith`, I fail to see you this is a consistent extension of `map` … in any case I would expect `map(whatever)` to return something iterable or a vector. Maybe I’m missing something here?

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [September 19, 2022, 12:16am UTC](https://discourse.julialang.org/t/define-map-filter-also-as-functionals/87401/11 "2022-09-19T00:16:27Z")

</div>

The 0-arg map may get fixed (removed) in the future.

> <https://github.com/JuliaLang/julia/issues/35293>
>
> It might be useful to define curried version of iterator transforms such as \`Ite…rators.map(f) = x -\> Iterators.map(f, x)\` (after #34352) and \`Iterators.filter(f) = x -\> Iterators.filter(f, x)\` (ref #33027, https://github.com/JuliaLang/julia/pull/24990#issuecomment-605384271). However, since the eager counter part \`map(f)\` is currently defined as \`f()\`, it may confuse users when the difference between \`map(f)\` and \`Iterators.map(f)\` are more than laziness.
> 
> Can we remove \`map(f)\` at some point? (I think whether or not it is desirable to do this within Julia 1.x is up to core devs.) I think it makes sense to also remove \`foreach(f)\` when removing \`map(f)\` for consistency.
> 
> \`map(f)\` was added by @stevengj in #17318. The reasoning was that it is better to be consistent with \`f.()\`. I agree that considering \`f.()\` as a special case of \`f.(args...)\` makes sense for broadcasting; i.e., the output dimension of broadcasting is \`reduce(max, ndims\_of\_arguments)\` when \`ndims\_of\_arguments\` is not empty. Since \`ndims\` is always a non-negative integer, it is reasonable to use the identity element of \`max\` on non-negative integers which is \`0\`, when there is no argument. However, this argument relies on that the "\`ndims\`" of the output is flexibly determined (i.e., using the largest \`ndims\`) in broadcasting. As this is not the case in \`map\`, I think it makes less sense to define \`map(f)\` as \`f()\`. OTOH, I think curried \`map\` is a useful definition.
> 
> \`foreach(f)\` was added by @JeffBezanson in #13774. It seems there was no discussion for adding \`foreach(f)\`.

The others are discussed in this PR

> <https://github.com/JuliaLang/julia/pull/41173>
