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

Add a short demo to main README #42

Closed
Technologicat opened this issue Oct 27, 2019 · 5 comments
Closed

Add a short demo to main README #42

Technologicat opened this issue Oct 27, 2019 · 5 comments
Assignees
Labels
documentation Non-executable English, for humans enhancement New feature or request
Milestone

Comments

@Technologicat
Copy link
Owner

To improve the first impression, provide a runnable demo of an appropriate subset of features right near the start of the main README.

Focus on short and impressive. It does not need to be a complete tour. But it must, quickly, answer the universal first question: why should I bother to read any further?

Things to consider:

  • Showcase preference, i.e. focus the demo on what?
    • Features that currently don't exist in other libraries (continuations)?
    • Features that obviously require a complex implementation?
    • Features that are "done right", esp. in corner cases (e.g. rackety foldl or scanl with multiple inputs, memoize that caches also exceptions)?
    • Features that give a lot of added value and are easy to use?
  • Target audience, i.e. focus the demo for whom?
    • Applied mathematicians and haskellers may like the rule-inferring lazy math sequence constructor s.
    • A general programming audience may feel at home with let or continuations.
  • Complexity balance. The demoed features should be simple enough to explain and use, to facilitate a short code example with a one-liner comment, but complex enough in expected implementation (by a seasoned Python programmer), to communicate that rewriting the same functionality from scratch in five minutes is not realistic.
  • Holistic perspective. How to communicate that unpythonic is essentially a language framework, where (unless specifically otherwise stated) all of the features are designed to work together?

See #8 (mention of executable tagline).

@Technologicat Technologicat added the enhancement New feature or request label Oct 27, 2019
@Technologicat Technologicat added this to the 0.14.2 milestone Oct 27, 2019
@Technologicat Technologicat self-assigned this Oct 27, 2019
@Technologicat Technologicat added the documentation Non-executable English, for humans label Oct 29, 2019
@Technologicat
Copy link
Owner Author

Technologicat commented Oct 30, 2019

At least this snippet should go in:

from unpythonic.syntax import macros, tco

with tco:
    evenp = lambda x: (x == 0) or oddp(x - 1)
    oddp  = lambda x: (x != 0) and evenp(x - 1)
    assert evenp(10000) is True

Automatic TCO works also with lambdas, and analyzes the tail position from expressions. In a logical expression (even when nested) it's always the rightmost item, because short-circuiting depends on values, which are not available at macro expansion time.

@Technologicat
Copy link
Owner Author

@Technologicat
Copy link
Owner Author

Technologicat commented Nov 8, 2019

Unpythonic in 30 seconds - Pure Python

Small, limited-space overview of the overall flavor. Click to expand examples.

Mathematical sequences. Slice syntax for general iterables.
from unpythonic import s, islice

seq = s(1, 2, 4, ...)
assert tuple(islice(seq)[:10]) == (1, 2, 4, 8, 16, 32, 64, 128, 256, 512)
Self-referring lambdas.
from unpythonic import withself

fact = withself(lambda self, n: n * self(n - 1) if n > 1 else 1)
assert fact(5) == 120
Memoizing generators.
from itertools import count, takewhile
from unpythonic import gmemoize, islice

@gmemoize
def primes():  # FP sieve of Eratosthenes
    yield 2
    for n in count(start=3, step=2):
        if not any(n % p == 0 for p in takewhile(lambda x: x*x <= n, primes())):
            yield n

assert tuple(islice(primes())[:10]) == (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
Functional updates.
from itertools import repeat
from unpythonic import fup

t = (1, 2, 3, 4, 5)
s = fup(t)[0::2] << tuple(repeat(10, 3))
assert s == (10, 2, 10, 4, 10)
assert t == (1, 2, 3, 4, 5)
Slicable read-write list view.
from unpythonic import view

lst = list(range(10))
v = view(lst)[::2]  # [0, 2, 4, 6, 8]
v[2:4] = (10, 20)
assert lst == [0, 1, 2, 3, 10, 5, 20, 7, 8, 9]

lst[2] = 42
assert v == [0, 42, 10, 20, 8]
Data flow focused function composition.
from unpythonic import piped, getvalue

double = lambda x: 2 * x
inc    = lambda x: x + 1
x = piped(42) | double | inc | getvalue
assert x == 85

@Technologicat
Copy link
Owner Author

Technologicat commented Nov 8, 2019

Unpythonic in 30 seconds - Macros

Language-level extensions, with MacroPy. Click to expand examples.

Expression-local variables.
from unpythonic.syntax import macros, let

x = let[((a, 1), (b, 2)) in a + b]
Definition-local variables, a.k.a. let-over-lambda for Python.
from unpythonic.syntax import macros, dlet

@dlet((x, 0))
def count():
    return x << x + 1  # << rebinds in the let env
assert count() == 1
assert count() == 2
Imperative code as an expression, with local variables.
from unpythonic.syntax import macros, do, local, delete

x = do[local[a << 21],
       local[b << 2 * a],
       print(b),
       delete[b],  # do[] local variables can be deleted, too
       4 * a]
assert x == 84
Automatic tail call optimization (TCO), with tail-position analysis also for expressions.
from unpythonic.syntax import macros, tco

with tco:
    evenp = lambda x: (x == 0) or oddp(x - 1)
    oddp  = lambda x: (x != 0) and evenp(x - 1)
    assert evenp(10000) is True
Automatic currying, à la Haskell.
from unpythonic.syntax import macros, curry
from unpythonic import foldr, composerc as compose, cons, nil, ll

with curry:
    def add3(a, b, c):
        return a + b + c
    assert add3(1)(2)(3) == 6

    mymap = lambda f: foldr(compose(cons, f), nil)
    double = lambda x: 2 * x
    assert mymap(double, (1, 2, 3)) == ll(2, 4, 6)
Lazy functions, a.k.a. call-by-need.
from unpythonic.syntax import macros, lazify

with lazify:
    def my_if(p, a, b):
        if p:
            return a  # b never evaluated in this code path
        else:
            return b  # a never evaluated in this code path
    assert my_if(True, 23, 1/0) == 23
    assert my_if(False, 1/0, 42) == 42
Continuations (call/cc), also with automatic TCO.
from unpythonic import macros, continuations, call_cc

with continuations:
    # McCarthy's amb() operator
    stack = []
    def amb(lst, cc):
        if not lst:
            return fail()
        first, *rest = tuple(lst)
        if rest:
            remaining_part_of_computation = cc
            stack.append(lambda: amb(rest, cc=remaining_part_of_computation))
        return first
    def fail():
        if stack:
            f = stack.pop()
            return f()

    # Pythagorean triples using amb()
    def pt():
        z = call_cc[amb(range(1, 21))]  # capture continuation, auto-populate cc arg
        y = call_cc[amb(range(1, z+1)))]
        x = call_cc[amb(range(1, y+1))]
        if x*x + y*y != z*z:
            return fail()
        return x, y, z
    t = pt()
    while t:
        print(t)
        t = fail()  # note pt() has already returned when we call this.

@Technologicat
Copy link
Owner Author

Now just include a doc link for each item, and merge this into the README...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Non-executable English, for humans enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant