npm package: @on-the-ground/quackquack
Description: Message-based duck typing system focused on behavioral roles instead of structural types.
Author: Joohyung Park
Latest version: 0.0.12

![QuackQuack](./assets/quackquack.png)

---

## ❌ Not That Duck (Seriously)

You might have seen another package called [`duck`](https://www.npmjs.com/package/duck).  
It’s great — but **completely different** from what we’re doing here.

Here's a quick comparison:

| Feature         | [`duck`](https://www.npmjs.com/package/duck) | `quackquack` (`@on-the-ground/quack`)            |
| --------------- | -------------------------------------------- | ------------------------------------------------ |
| 📦 Purpose      | Assertion matcher for tests                  | Runtime signature validator                      |
| 🧠 Focus        | _Structural_ object matching                 | _Behavioral_ contract matching                   |
| 🔍 Scope        | Deep introspection of object contents        | Zero introspection – ignores internals           |
| 🧪 Typical Use  | `duck.assertThat(obj, matcher)`              | `expectQuack('(string, number) => boolean')(fn)` |
| ⚠️ Fails If     | Object is missing properties                 | Function signature doesn’t match                 |
| 📘 Mental Model | “Does this object look like this?”           | “Can this function play the Role I expect?”      |

---

## 🎯 Philosophy: Only the Role Matters

`quackquack` doesn’t care how your function is implemented.  
We don’t parse the body.  
We don’t read its variables.  
We don’t care if it mutates state, opens a socket, or whispers to ducks.

We **only** care about one thing:

> **Does it look like it can play the Role?**  
> _(As defined by its signature.)_

That’s it.

You can define Roles declaratively using our DSL:

```ts
const duckFn = expectQuack("(string, number?) => boolean")(fn);
```

Or check full objects at once:

```ts
const duckObj = expectDuck({
  add: "(number, number) => number",
  log: "(string) => void",
})(obj);
```

Under the hood, this creates a **runtime contract** —  
a guarantee that your function (or object) **quacks like the Role it's meant to play**.

---

## ✨ Why Use `quackquack`?

- ✅ Works at **runtime** — no TypeScript required
- ✅ Zero dependence on internal object structure
- ✅ Perfect for **plugin systems**, **dynamic registries**, **adapter validation**
- ✅ Supports `strict` mode: fail if shape doesn't match exactly
- ✅ Bridges the gap between _duck typing_ and _role declaration_

---

## 🧬 Minimalism By Design

> “We’re not here to judge how you built it —  
> we just want to know if it can do the job.”

That’s the `quackquack` way.

No reflection. No object traversal.  
Just plain, declarative, behavioral contracts.

---

## 🚀 Quick Start

This library helps you **declare Roles** via a lightweight DSL, and **enforce them** at runtime or with decorators.

### ✅ 1. Mark a function as quackable

```ts
const greet = quackable("(name: string) => void")((name) => {
  console.log(`Hello, ${name}`);
});
```

---

### ✅ 2. Mark a method with the same decorator

```ts
class Greeter {
  @quackable("(name: string) => void")
  greet(name: string) {
    console.log(`Hi, ${name}`);
  }
}
```

---

### ✅ 3. Enforce quack match with expectQuack

```ts
const greet = expectQuack("(name: string) => void")(greetFromArgs);
```

> If the function wasn't decorated, it'll be wrapped with validation.  
> If already decorated but mismatched, it throws.

---

### ✅ 4. Enforce a whole object with expectDuck

```ts
const role = expectDuck({
  greet: "(name: string) => void",
  add: "(x: number, y: number) => number",
})(obj);
```

> Each method will be verified to match the expected Role signature.

---

## ✍️ Quack DSL: Writing Quack Signatures

The Quack DSL supports two styles for writing function signatures:

```ts
(name: string, age?: number) => void  // JavaScript-style
(string, number?) => void             // Simplified
```

Both are valid and compiled to the same internal representation.

Note: The DSL is inspired by JavaScript, but it doesn't aim to replicate JS syntax exactly.
Its purpose is to express roles, not real function declarations.

---

## 🪶 Ultra-Light: No Overhead, No Interference

Once a function or method is `quackable` (i.e., decorated with `@quackable`),  
`quackquack` will **not wrap, proxy, or alter** it in any way.

When you `expectQuack` a `quackable` function,

It simply compares two **precompiled QuackAsts**:

- ✅ The **expected** signature string
- ✅ The **given** function’s signature

That’s it.  
No call-time parsing. No reflection.  
**Zero overhead. Near-instant validation.**

This makes it ideal for:

- Performance-sensitive plugin systems
- Dynamically loaded modules
- High-frequency contract checks

---

## ☁️ And Yes, We Can Expect Non-Quackables Too

Not every function needs to be `quackable`.  
You can still use `expectQuack` or `expectDuck`  
on plain JavaScript functions or objects.

In that case, `quackquack` will fall back to **runtime introspection**,  
using tools like [`zod`](https://github.com/colinhacks/zod)  
to validate the shape of parameters, arity, and behavior.

So even if you forgot (or chose not) to decorate it —  
you’re still covered.

✅ **Decorated?** Instant match by signature → Zero overhead  
✅ **Undecorated?** Smart fallback with runtime validation  
✅ **You?** Worry-free API contract checking 🫶

---

## 🎭 Role-First Object Orientation

This project is named after Alan Kay — not because it’s directly affiliated with him, but because we deeply respect his philosophy of **message passing between objects**.
Our intention is to carry that spirit forward.

Ironically, however, to truly honor that spirit, we believe we must **break free from the habit of treating everything as an object**.

Objects only gain real value when they **aren’t created arbitrarily**.
An object deserves to exist only when it can **justify its presence** —
specifically, when it can **fulfill a Role**.

In this regard, we depart from Alan Kay's assertion that “everything is an object.”
We say instead: **“Everything begins with a Role.”**
A Role arises first, born from a concrete need.
Then, and only then, does an object emerge — as a **candidate** that may fulfill that Role.

**Messages are not sent to objects, but to Roles.**
An object is simply a responder — an implementer of the behavior required by the Role.

---

## 🧱 Classes Do Not Represent Roles

We often treat classes as mere “bundles of data and methods.”
But starting with data and then slapping on some methods quickly leads to
**unprincipled, trash-bin objects**.

Objects are meant to represent **high cohesion**.
But what defines “right” cohesion has always been ambiguous.
Now, we propose a clear answer: **the right cohesion comes from the right Role**.

A Role is essentially **a collection of related functions**.
For example, the role of a `Transaction` might include `play` and `rollback`.
These functions are grouped not because they share data,
but because they **share responsibility for the same behavior**.

In this light, cohesion should be driven not by data,
but by **behavior and responsibility**.

---

## ⏳ Avoid Bring-Up First Design

If you design objects before you clearly understand the roles they need to fulfill,
you fall into the trap of **Bring-Up First Design** —
an accidental architecture shaped by momentum rather than intention.

The most valuable objects and roles
only become clear **when you wait for genuine needs to emerge**.
The longer you delay their introduction,
the sharper and more robust they become.

---

## 🔍 Class vs Role

| Concept      | Role                                 | Class                                 |
| ------------ | ------------------------------------ | ------------------------------------- |
| Declared by  | The side that **needs** the behavior | The side that **produces** the object |
| Purpose      | Specify **behavioral expectations**  | Describe **internal structure**       |
| Focus        | “Can you handle this message?”       | “What are you made of?”               |
| Prerequisite | Must satisfy external demand         | Reflects internal design              |

While classes describe objects from the inside out,
**Roles describe them from the outside in** — from the perspective of **what the world needs**.

---

## 🧠 Real OOP Is About Behavior, Not Reflection

Real object-oriented programming doesn't ask:

> “What are you made of?”

That's reflection — and it has no place in message-driven design.

Instead, real OOP asks:

> “I need someone who can play this role. Can you handle it?”

Everything should be designed around **behavior and responsibility**,
not around static structure.

```

```