Metadata-Version: 2.1
Name: diterlib-islptng
Version: 0.1.0
Summary: A functional, array-oriented, tacit programming esolang run via Python interpreter
Home-page: https://github.com/islptng/diterlib
Author: islptng
Author-email: 
License: Unlicense
Classifier: Programming Language :: Python :: 3
Classifier: License :: Public Domain
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE

# dIterLib - write functional, array-oriented, tacit code in Python

dIterLib is not a Python library. It is, instead, an esolang that runs via the Python interpreter.
After `from diterlib import *`, you can use dIterLib in a Python environment,
as a functional, array-oriented, tacit-style programming language.

## Quick Start
This is an interesting introduction to dIterLib. If you just want to look up the functions, go to the appendix.

### The Basics

Every dIterLib expression starts with `o(...)`. It wraps the expression as a dIterLib object.
You can do 2 things with it:
1. **Apply a function** - the basics of dIterLib! The syntax is delightfully simple: `object(function)`

    Here's where the magic begins. dIterLib comes with a built-in range function named `lrange`. Watch this: `o(5)(lrange)` conjures up the list `[0, 1, 2, 3, 4]`. Why? Because behind the scenes, `lrange(5)` does exactly what you'd expect - it generates `[0, 1, 2, 3, 4]`. The `o(5)` wraps the number 5 in dIterLib's special sauce, and then `(lrange)` pipes it through the function. Elegant, isn't it?

    But wait, there's more! dIterLib doesn't stop at custom functions - it gives friendly names to Python's operators too, so you can use them in this functional style. Feast your eyes on this: `o(5)(add, 3)` evaluates to `8`. How? Because `add(5, 3)` is just `5 + 3` in disguise! The comma syntax lets you pass additional arguments, transforming what looks like a simple addition into a sophisticated functional pipeline. It's like Python operators went to finishing school and learned some manners!

2. **Make iterator** - the beating heart of dIterLib! Syntax: `object[rank]`.

    Picture this: you've got your hands on a beautiful list `l = [0, 1, 2, 3, 4]` (courtesy of `o(5)(lrange)`), and now you want to add 1 to every single element. In vanilla Python, you'd be stuck writing `[i+1 for i in l]` or wrestling with `map(lambda x: x+1, l)`. Yawn! Where's the elegance? Where's the *joy*?

    Enter dIterLib! With one elegant stroke: `l[0](add,1)`. That's it. The `[0]` is your magic wand - it conjures an iterator over the 0th dimension, and whatever function follows gets applied to each element like a gentle wave washing over pebbles. The result? `[1, 2, 3, 4, 5]`. Chef's kiss!

    But here's where your eyebrows should start rising. What if the argument itself could be an iterator? Oh yes, my friend. `l[0](add,l[0])` produces `[0, 2, 4, 6, 8]`: element-wise addition, like two dancers moving in perfect synchrony across the floor.

    Now for the grand finale. You want a 5x5 matrix where each cell contains the sum of its row and column indices. In plain Python, you'd descend into the nested list comprehension underworld: `[[i+j for j in l] for i in l]`. It's readable, sure, but where's the *pizzazz*?

    dIterLib says: hold my beverage. `l[0](add,l[1])`. 

    Let that sink in. `[0]` iterates over rows, `[1]` iterates over columns, and because they have *different ranks*, dIterLib performs the most natural thing in the world: broadcasting. Every element of `[0]` meets every element of `[1]` in a beautiful Cartesian dance, producing your 5x5 matrix without a single nested bracket in sight. It's not just code - it's poetry in motion!

**The Stack** - dIterLib's secret memory vault, your personal time capsule for values!

Imagine you're in the middle of an epic calculation, and you stumble upon a precious intermediate result that you'll need again later. In lesser languages, you'd be forced to contort yourself with temporary variables, juggling names like a desperate street performer. But dIterLib scoffs at such pedestrian solutions!

Behold the **stack** - a LIFO wonderland where values go to wait their triumphant return! Here's your arsenal of stack sorcery:

- **`...(push)`** - The gentle archivist! This delightful operator whispers your current value to the stack's eager ears, *while still returning it unchanged* to continue your flowing pipeline. It's like having a photographic memory that doesn't interrupt your train of thought! Use it thusly: `o(5)(lrange)(push)[0](mul,2)` - your list `[0,1,2,3,4]` is safely stashed AND doubled to `[0,2,4,6,8]` in one breathtaking motion!

- **`peek()`** - The curious observer! Sneaks a glance at the stack's summit without disturbing its slumber. "What's up there?" it asks politely, receiving the top value without popping it away. Perfect for when you need to *check* your stash without *spending* it.

- **`pop()`** - The grand retriever! With a theatrical flourish, it extracts the top value from your stack and presents it for your immediate use. The value emerges from its resting place, ready to join the fray once more!

Picture this masterclass: `o(5)(lrange)(push)[0](mul,2)[0](add,pop()[0])` - we stash the original list, double it, then add the original back! The result? `[0,3,6,9,12]` - a beautiful reunion of past and present, made possible by the stack's temporal magic!

The stack isn't just storage - it's *time travel* for your data!

---

Now you know the basics of dIterLib! Time to get more advanced.
### Composing Functions
1. **`reduce`**

   The folder of functions! Takes a binary function and collapses your array into one value. Syntax: `reduce(function)` or `reduce(function, start)` for a custom starting point. Example: `sum = reduce(add)` turns `[1, 2, 3, 4]` into `10`. Simple, effective, *done*!

2. **`prefix`**

   `reduce`'s chatty cousin! Same folding action, but keeps every step. Syntax: `prefix(function)` or `prefix(function, start)`. Example: `o(5)(lrange)(prefix(add))` gives `[0, 1, 3, 6, 10]` - running totals without the sweat!

3. **`fixedpoint`**

   The relentless iterator! Applies your function until input equals output. Syntax: `fixedpoint(function)`. Example: `fixedpoint(lambda x:(x+2/x)/2)(1)` yields sqrt(2) - Newton's method in a one-liner!

4. **`repeat`**

   The loop unroller! Apply a function *n* times. Syntax: `repeat(function)` needs `(base, repetitions, start)` to work. Example: `o(0)(repeat(add),5,3)` ( `repeat(add)(0,5,3)` ) returns `15` (0+3+3+3+3+3). Iteration made elegant!

5. **`train`**

   APL's legendary "trains" - sequences of functions that work together to transform data. `train(l, m, r)` hooks three functions into a pipeline: left and right run first, then middle combines their results.
   Classic example - **arithmetic mean**: `average = train(reduce(add), div, len)`.
   On `[2, 4, 6, 8]`:
   - First left: `reduce(add)` -> `20`
   - Then right: `len` -> `4`
   - Finally middle: `div` -> `20 / 4 = 5.0`

   One expression, zero temps, maximum style!
   * Pro tip: `train` needs three functions, but sometimes you only have two. Enter **`iden`** - the identity-function creator! `iden()` passes the left operand through unchanged, `iden(1)` does the same for the right. It's the duct tape of functional composition - not glamorous, but indispensable when you need it.

### Composing Functions (2)
Tired of writing long expressions with `lambda:`s, or just want to use dIterLib's syntax to create functions? Meet `c`!

**`c`** - the composer of dreams, the architect of abstraction!

Syntax: `c(functions).f`. Think of `c` as your very own dIterLib object - a blank canvas upon which you paint functional masterpieces! Chain operations upon it with the same elegant syntax you know and love. When your symphony is complete, the grand finale `.f` seals the deal, transforming your creation into a callable function ready to spring into action!

But wait - what if your masterpiece requires *multiple* arguments? Fear not, for `arg()` rides to the rescue! It's the placeholder that whispers "put an argument here, please!"

Behold, the arithmetic mean reborn: `c(add,arg())(div,2).f` - a function that takes two numbers and returns their average!

Let's peel back the curtain on this magic:
- `c(add, arg())` says: "First, I'll add something to my first argument... and that 'something' is the *second* argument!"
- `(div, 2)` continues: "Then I'll divide the result by 2!"
- `.f` cries: "Ta-da! You're a real function now!"

So `c(add,arg())(div,2).f(10, 20)` dances through: `(10 + 20) / 2 = 15.0`. Two arguments in, one beautiful average out!

No more `lambda x, y:` drudgery - just pure, compositional poetry!

## Appendix: List of dIterLib's built-in functions
| Function | Description |
|----------|-------------|
| `o(x)` | Wrap `x` as dIterLib object |
| `lrange(n)` | Generate `[0, 1, ..., n-1]` |
| `add` | `+` operator |
| `sub` | `-` operator |
| `mul` | `*` operator |
| `div` | `/` operator |
| `truediv` | `//` operator |
| `mod` | `%` operator |
| `pow` | `**` operator |
| `eq` | `==` operator |
| `ne` | `!=` operator |
| `gt` | `>` operator |
| `ge` | `>=` operator |
| `lt` | `<` operator |
| `le` | `<=` operator |
| `push` | Push value to internal stack |
| `peek` | Peek at top of stack |
| `pop` | Pop top of stack |
| `slice(a,x,y)` | Slice array |
| `take(a,x)` | Take first `x` elements of array |
| `drop(a,x)` | Drop first `x` elements of array |
| `reverse(a)` | Reverse array |
| `reduce(f, start?)` | Fold array with binary `f` |
| `prefix(f, start?)` | Running fold, keeps intermediates |
| `fixedpoint(f)` | Iterate `f` until convergence |
| `repeat(f)` | Apply `f` `n` times |
| `train(l, m, r)` | APL-style function train |
| `iden()` / `iden(1)` | Identity function (left/right arg) |
| `c` | Function composer |
| `arg()` | Placeholder for composer arguments |
