A library adding some anti-Pythonic syntatic sugar to Python
syntaxsugar

This lib adds some anti-Pythonic "syntactic sugar" to Python.
NOTE: This is merely an experimental prototype to show some potential of operator overloading in Python. Only tested under Python 3.6.0. Anything may evolve without announcement in advance.
Inspired by https://github.com/matz/streem.
Also, you can watch the last part of this Matz's talk to understand the intuition behind this project.
Install
pip install syntax_sugar
Use
To test out this lib, you can simply do.
python
from syntax_sugar import *
For serious use, you can explicitly import each component as explained below ... if you dare to use this lib.
pipe
python
from syntax_sugar import pipe, END
from functools import partial
pipe(10) | range | partial(map, lambda x: x**2) | list | print | END
put 10 into the pipe and just let data flow.
output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
remember to call END at the end
NOTE: everything in the middle of the pipe is just normal Python functions
pipe(10) | range | (map, lambda x: x**2) | list | print | END
Tuples are shortcuts for partial functions
from syntax_sugar import each x = pipe(10) | range | each(lambda x: x ** 2) | END
We can also save the result in a variable.
each is an eager evaluated version of the partial function of map, which returns a list instead of a map object. (Equivalent to map in Python 2)
pipe(10) | range | each(str) | ''.join > 'test.txt'
wanna write to a file? Why not!
write "0123456789" to test.txt
We don't need to put END here.
We can connect multiple pipes to create a longer pipe
python
from syntax_sugar import pipe, each, END
from functools import reduce
p1 = pipe(10) | range | each(lambda x: x/2)
head pipe can have input value
p2 = pipe() | (reduce, lambda acc, x: (acc + x)/2) p3 = pipe() | int | range | sum middle pipes can have no input value
p1 | p2 | p3 | END
returns 6
p = p1 | p2 | p3 p()
You can invoke the pipe by calling it as a function
you can also put a different value in the pipe
p(20)
returns 36
pipe with parallelism
By default, pipe works with threads.
You can have a function running in a seperate thread with pipe. Just put it in a [] or more explicitly t[]. Threads and processes are also available.
python
from syntaxsugar import (threadsyntax as t,
process_syntax as p)
pipe(10) | [print] | END # print run in a thread pipe(10) | t[print] | END # print run in a thread pipe(10) | p[print] | END # print run in a process
What makes this syntax good is that you can specify how many threads you want to spawn, by doing [function] * n where n is the number of threads.
python
pipe([1,2,3,4,5]) | [print] * 3 | END # print will run in a ThreadPool of size 3
Here is an example of requesting a list of urls in parallel
python
import requests
(pipe(['google', 'twitter', 'yahoo', 'facebook', 'github'])
| each(lambda name: 'http://' + name + '.com')
| [requests.get] * 3 # !! requests.get runs in a ThreadPool of size 3
| each(lambda resp: (resp.url, resp.headers.get('Server')))
| list
| END)
returns
[('http://www.google.com/', 'gws'),
('https://twitter.com/', 'tsa_a'),
('https://www.yahoo.com/', 'ATS'),
('https://www.facebook.com/', None),
('https://github.com/', 'GitHub.com')]
infix function
python
from syntaxsugar import isa, has, to, step, drop
1 /is_a/ int
equivalent to isinstance(1, int)
1 /as_a/ str
"1"
range(10) /has/ 'iter'
equivalent to hasattr(range(10), "iter")
1 /to/ 10
An iterator similar to range(1, 11).
Python's nasty range() is right-exclusive. This is right-inclusive.
10 /to/ 1
We can go backward.
'0' /to/ '9'
We can also have a range of characters :)
1 /to/ 10 /step/ 2
We can also specify step sizes.
Similar to range(1, 11, 2)
10 /to/ 1 /step/ 2
Go backward.
Similar to range(10, 0, -2)
1 /to/ 10 /drop/ 5
there is a drop functon which drop N items from the head
An iterator similar to [6, 7, 8, 9, 10]
/to/ has some advanced features
- lazy evaluation.
- support infinity.
- support product operation.
- support pipe.
python
from syntax_sugar import INF, take, each
CAUTION: this will infinitely print numbers
for i in 1 /to/ INF:
print(i)
1 /to/ INF /take/ 5 /as_a/ list
there is a take functon which is similar to itertools.islice
return [1, 2, 3, 4, 5]
1 /to/ ... /take/ 5 /as_a/ list
... is equivalent to INF
0 /to/ -INF /step/ 2 /take/ 5 /as_a/ list
also works with negative infinity.
return [0, -2, -4, -6, -8]
(1 /to/ 3) * (4 /to/ 6) /as_a/ list
all combinations of [1..3] * [4..6]
return [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
1 /to/ 10 /take/ 5 | each(lambda x: x **2) | END
These infix functions can also be piped.
[1, 4, 9, 16, 25]
Make your own infix function, so you can append multiple items to a list in one line.
python
from syntax_sugar import infix
@infix def push(lst, x): lst.append(x) return lst
[] /push/ 1 /push/ 2 /push/ 3
returns [1,2,3]
You can also do
python
def push(lst, x):
lst.append(x)
return lst
ipush = push /as_a/ infix
[] /ipush/ 1 /ipush/ 2 /ipush/ 3
returns [1,2,3]
function composition
In math, (f * g) (x) = f(g(x)). This is called function composition.
python
lmap equivalent to list(map(...))
lmap = compose(list, map)
lmap(lambda x: x ** 2, range(10))
Let's say we want to represent f g h in a program, i.e. fn(x) = f(g(h(x)))
python
f = lambda x: x**2 + 1
g = lambda x: 2*x - 1
h = lambda x: -2 x*3 + 3
fn = compose(f, g, h)
fn(5) # 245026
or you can do
f = composable(lambda x: x**2 + 1)
g = composable(lambda x: 2*x - 1)
h = composable(lambda x: -2 x*3 + 3)
fn = f g h
fn(5) # 245026
Sometimes you may prefer the decorator way.
python
make your own composable functions
@composable
def add2(x):
return x + 2
@composable def mul3(x): return x * 3
@composable def pow2(x): return x ** 2 fn = add2 mul3 pow2
equivalent to add2(mul3(pow2(n)))
fn(5) returns 5^2 * 3 + 2 = 77
More receipes: https://github.com/czheo/syntaxsugarpython/tree/master/recipes
