---
Title: Releasing pipe v2.0
Date: 2022-05-23 09:31:00
Summary: Introducing partially parametrized pipes!
---

# Introducing partially parametrized pipes!

[pipe](https://github.com/JulienPalard/Pipe) is a very old project of mine, proudly showing 1.3k stars on github!

I don't maintain it a lot, but today I'm announcing a new release introducing a nice feature: partially parametrized pipes.

If you're familiar with [currying](https://en.wikipedia.org/wiki/Currying)
or
[functools.partial](https://docs.python.org/3/library/functools.html#functools.partial)
you won't get lost.


## Show me!

Before `pipe 2`, the following were already valid:

```python
>>> from random import randint
>>> from pipe import where, sort
>>>
>>> negative = where(lambda x: x < 0)
>>> positive = where(lambda x: x > 0)
>>>
>>> numbers = [randint(-10, 10) for _ in range(10)]
>>>
>>> numbers | positive | sort
[7, 10]
>>> numbers | negative | sort
[-10, -9, -8, -7, -4, -4, -2]
```

It allows to **name things**, naming things make
code more readable so I wanted more of it.

It was probably underrated, I like to do this kind of things:

```python
>>> isort = sort(key=lambda x: x**2)
>>> numbers | isort
[0, -2, -4, -4, -7, 7, -8, -9, 10, -10]
```

But it failed as soon as you try go deeper:

```python
>>> numbers | isort(reverse=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pipe.py", line 100, in __ror__
    return self.function(other)
  File "pipe.py", line 103, in <lambda>
    return Pipe(lambda x: self.function(x, *args, **kwargs))
TypeError: Pipe.__call__.<locals>.<lambda>() got an unexpected keyword argument 'reverse'
```

Call it a bug, that's what's pipe 2 fixes:

```python
>>> from pipe import where, sort
>>> from random import randint
>>> numbers = [randint(-10, 10) for _ in range(10)]
>>>
>>> isort = sort(key=lambda x: x**2)
>>> numbers | isort(reverse=True)
[-10, -9, 8, -6, 6, -4, 4, 4, 2, 1]
>>>
```

Or in the other way around:

```python
>>> rsort = sort(reverse=True)
>>> numbers | rsort
[8, 6, 4, 4, 2, 1, -4, -6, -9, -10]
>>> numbers | rsort(key=lambda x: x**2)
[-10, -9, 8, -6, 6, -4, 4, 4, 2, 1]
```

From here you can build many specialized blocks from a single pipe,
here's a better example:

```python
import re

from pipe import Pipe


@Pipe
def grep(iterable, pattern, flags=0, invert=False):
    """Mimics grep."""
    for line in iterable:
        match = re.match(pattern, line, flags=flags)
        if (match and not invert) or (not match and invert):
            yield line
```

Build the small reusable blocks:

```python
igrep = grep(flags=re.I)
vgrep = grep(invert=True)
vigrep = igrep(invert=True)
https = igrep("https://")
not_https = https(invert=True)
...
```

And they all do what you think they do:

```python
>>> lines = ["https://python.org", "http://detectportal.firefox.com/", "Just no an URL", "HTTPS://afpy.org"]
>>> for url in lines | https:
...     print(url)
...
https://python.org
HTTPS://afpy.org
```

```python
>>> for url in lines | not_https:
...     print(url)
...
http://detectportal.firefox.com/
Just no an URL
```

Remember I said currying? Yes you can abuse the syntax, but please don't:

```python
>>> from pipe import sort
>>> lines | grep()(invert=True)()(flags=re.I)()("https://")() | sort
['Just not an URL', 'http://detectportal.firefox.com/']
```

As I like to say: « It's not because it's possible that you should do it ».
