Why Vishwakarma is not a programming language

It is tempting to call Vishwakarma a programming language, because you write it in files and a compiler reads it. But the label misleads, and the difference is worth being precise about.

A programming language is where you write how: control flow, mutable state, algorithms, the step-by-step procedure a machine executes. Vishwakarma has none of that. There are no loops, no variables you assign to and reassign, no arithmetic to compute a result. You describe what the system is, and the compiler decides how to build it. The examples below are from Chowk, a small marketplace.

Rules are conditions, not procedures

The closest the language comes to logic is a rule expression — a condition attached to a command or a flow step. It evaluates to true or false; it does not run a sequence of statements.

sub-domains/ordering/commands/order.vishwakarma
command MarkOrderPaid {
  aggregate  Order
  operation  update

  guard "status == 'PENDING'"
  set status = expr "'PAID'"

  id uuid required
}

The guard is a precondition: the write is permitted only when the order is still PENDING, which also makes marking it paid twice a harmless no-op. There is no branch to take, no state to mutate by hand, no next instruction. You state the rule; the generated code enforces it.

Multi-step flows are state machines, not scripts

Even a flow that visits several steps is declared, not scripted. Chowk's Checkout opens an order and adds its first line in one transaction. Each state names an operation to invoke and where to go next. It reads like control flow, but goto here is an edge in a state machine, not a jump in a program.

sub-domains/ordering/use_cases/checkout.vishwakarma
use_case Checkout {
  input  CheckoutInput
  output CheckoutOutput
  transactional

  machine {
    initial place_order

    state place_order {
      command PlaceOrder
      input {
        customer_id: "input.customer_id"
        total:       "input.total"
      }
      output_as order
      goto add_line
    }

    state add_line {
      command AddOrderLine
      input {
        order_id:   "result.order.id"
        product_id: "input.product_id"
        quantity:   "input.quantity"
        unit_price: "input.unit_price"
      }
      goto done
    }

    state done {
      terminal
      result "{ 'order_id': result.order.id }"
    }
  }
}

Because the flow is declared rather than scripted, it has an exact shape — the same machine drawn as a diagram:

stateDiagram-v2
  [*] --> place_order
  place_order --> add_line: order opened
  add_line --> done: line added
  done --> [*]

There are no local variables to accumulate, no computation between states. A state reads the saga input with input.* and a prior step's output with result.<name>.*, and hands off with goto. The machine orchestrates declared operations; it does not implement them.

Real computation lives in a declared seam

When a system genuinely needs imperative code — cryptographic randomness, an external SDK, a hand-tuned algorithm — the language does not let you write it inline. You declare a seam: a typed boundary with an input, an output, and a pointer to where the real implementation lives. The hand-written code sits outside the model; Vishwakarma only references it.

sub-domains/ordering/bespoke/order_number_generator.vishwakarma
bespoke OrderNumberGenerator {
  doc "Generate a human-friendly order number (CHOWK- + a base32 random suffix)."

  method Generate {
    input  OrderNumberRequest
    output OrderNumberResponse
  }

  impl {
    package     "bespoke/ordering"
    constructor "NewOrderNumberGenerator"
  }
}

By convention, every seam states why it must be hand-written and not declared. This one's reason is exact:

Bespoke (not declarative) because it needs crypto/rand for the entropy suffix — the framework has no declarative shape for that.

That is the whole boundary in one sentence. The moment a task is genuinely a computation, it leaves the language. A programming language would simply let you write the loop. Vishwakarma refuses, and pushes the computation out into a named seam.

Why the refusal matters

The refusal is not a limitation the language is waiting to outgrow. It is the mechanism that keeps the model describable and generatable. If you could drop into arbitrary procedures anywhere, the description would stop being a complete statement of intent, and the compiler could no longer generate the whole system from it. By keeping computation at arm's length behind an explicit seam, everything the language does express stays declarative, checkable, and generated — and the seams stay few, visible, and justified.