# function

> A function is a top-level unit of work, written like a C# method — a return type, a name, typed parameters, a body. It runs transactionally: the rows it writes are committed together when it finishes.

<!-- id: function-declaration · area: function · stability: stable · html: https://osysharp.com/reference/function/declaration/ -->

## Summary        {#summary}
A function is the unit of work: a return type, a name, typed parameters and a body, written exactly like a C# method
but declared at the top level of a file. It is where you create rows, query them and decide things.

It runs **transactionally**. The rows a function writes are committed together when it returns, so a function that
fails half-way leaves nothing behind. There is no `Save()` and no `Commit()` to remember.

⚠ **That is true of a FUNCTION and not of a component `action`.** An action runs in the page's optimistic overlay,
where a write renders immediately and is not yet persisted — so an action ends with `UnitOfWork.Commit()`, and the
examples on this page do not because they are functions. If you are looking at a body inside a `component`, see
[creating & saving data](https://osysharp.com/reference/ui/data-mutation/); the rule there is the opposite of the one here, and knowing which body you are in is the whole
of it.

## Signature      {#signature}
```osy syntax
<ReturnType> <Name>(<Type> <param>, …) {
  <statements>
}

```

## Description    {#description}

### How do I declare a function?   {#declaring}
`void` when it returns nothing, a type when it returns something. Parameters are typed and camelCase; the function
name is PascalCase:

```osy title="a function that writes, and one that reads" test app=function-declaration
entity Order {
  [Required] string Code;
  decimal Total;
}

void PlaceOrder(string code, decimal total) {
  var o = new Order { Code = code, Total = total };
}

decimal OrderTotal(string code) {
  var o = Order.Single(x => x.Code == code);
  return o.Total;
}
```

### It commits as one unit   {#transactional}
Everything a function writes lands together, or not at all. That is what lets you write the obvious thing:

```osy title="two rows, one outcome" test app=function-declaration
entity AuditLine {
  [Required] string Message;
}

void PlaceAndLog(string code, decimal total) {
  var o = new Order { Code = code, Total = total };
  var a = new AuditLine { Message = "placed " + code };
  // both rows commit together — there is no state where the order exists and the log line does not
}
```

If the function faults — a constraint violation, an invariant, a division by zero — **neither row is written.** You
do not have to unwind anything by hand.

### There is no `async`   {#no-async}
A function that reaches outside the database — an HTTP call, an LLM completion — is written **exactly like any other
function**. There is no `async`, no `Task<T>`, and no colour to keep track of:

```osy title="a function that calls out — no async anywhere" test app=function-declaration-async
app Shop {
  model "model/**/*.osy";
  use Osysharp.Http;
}

string Fetch(string url) {
  var r = Http.Get(url);           // just a call — the engine suspends and resumes around it
  return r.IsSuccess ? r.Body : "";
}
```

Those outward calls are **effects**, and the platform handles them: when a function hits one, the engine suspends it,
performs the effect, and resumes the function where it left off — even if that means surviving a process restart in
between. You do not have to mark the function, and neither does its caller.

This is why C#'s `async` is absent rather than merely optional. `async` exists to colour a function so its *callers*
know to await it, and that colour spreads until it has infected everything it touches. Here the durability is the
engine's job, not the signature's, so there is nothing to spread.

`await` appears in exactly one place in the language — `Workflow.Run()` — where you are genuinely waiting for another
long-running thing to finish, and want to say so. If you are coming from C#, [async / await — why Osy# has neither](https://osysharp.com/reference/function/async-await/) is the page to
read: it is the first habit to unlearn.

### Where may a function be declared?   {#where}
**At the top level of a file — that is what a `function` is.** It is not nested in anything, and there is no
namespace, module or class you have to put it inside first. A file may hold as many as you like, beside its entities
and components. Not inside an entity, though: an entity body holds data, and behaviour sits beside it.

Two things that LOOK like the same question are not, and the difference is what each one commits:

| you write it… | what it is | where it runs | what saves it |
|---|---|---|---|
| at the top level of a file | a **function** | inferred from its body — see [execution side](https://osysharp.com/reference/function/execution-side/) | itself, on return |
| inside a `component` | a **method** of that component | with the component | the page's `UnitOfWork.Commit()` |
| inside a `class` | a **method** of that class | wherever it is called from | its caller |

All three are written identically — a return type, a name, a typed parameter list, a body — so the enclosing
declaration is the only thing that decides which you have. There is no `function` keyword to write and no `method`
keyword either; see [Writing a component — what differs from C#](https://osysharp.com/reference/ui/csharp-differences/) for why.

```osy title="all three, in one file" test app=function-declaration-where
int Doubled(int n) { return n * 2; }                            // a function

class Rates { public decimal WithVat(decimal net) { return net * 1.25m; } }   // a class method

[Page("/counter")] [AllowAnonymous] [Render(CSR)]
component Counter() {
  int n = 1;
  int Quadrupled(int x) { return x * 4; }                       // a component method
  action Bump() { n = Quadrupled(n); }
  render { Text("n=" + n); }
}
```

And the top-level form beside the data it works on:

```osy title="behaviour lives beside the data, not inside it" test app=function-declaration
entity Product {
  [Required] string Name;
  decimal Price;
}

decimal PriceWithVat(Product p, decimal rate) {
  return Math.Round(p.Price * (1 + rate), 2);
}
```

If you want behaviour *attached* to a type — a method with a receiver — that is a [class method](https://osysharp.com/reference/class/methods/).

## See also       {#see-also}
- [var](https://osysharp.com/reference/function/var/) — locals inside the body
- [if / else](https://osysharp.com/reference/function/if/) · [foreach](https://osysharp.com/reference/function/foreach/) · [while](https://osysharp.com/reference/function/while-loop/) — control flow
- [class methods](https://osysharp.com/reference/class/methods/) — behaviour attached to a type
- [async / await — why Osy# has neither](https://osysharp.com/reference/function/async-await/) — why there is no `async`, and where the one `await` lives
- [Running tests](https://osysharp.com/reference/testing/running-tests/) — how you run one
