---
title: "Data transformation"
description: "Immutable bindings, shadowing, destructuring, and first-argument pipes for readable data-first code."
image: "https://hem.run/social/hem.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://hem.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Data transformation

Hem uses an immutable, data-first style. You bind a value, transform it, and bind the next stage without mutating the previous value. The emitted program compiles to standard TypeScript.

## Immutable stages and shadowing

`let` emits `const`. Rebinding a name is allowed; Hem keeps the first binding's name and gives subsequent bindings deterministic `$n` suffixes in emitted TypeScript.

```hem
let readPort = (env: Env): number => {
  let port = env.PORT
  let Some(port) = port else return 8080
  let port = Number.parseInt(port, 10)
  if Number.isNaN(port) { 8080 } else { port }
}
```

Each binding can change the type: `port` goes from `?string` to `string` to `number`. Shadowing is lexical: a later binding references the previous binding, and closures retain the binding they captured. For same-type steps, chaining or `|>` is usually clearer.

## Destructuring at the boundary

Object and array destructuring follow TypeScript syntax. Destructure when a function or stage needs specific fields, and keep the rest when the next stage needs the remainder.

```hem
let { id, profile: { displayName } } = user
let [head, ...tail] = events
let summary = `${id}: ${displayName} (${tail.length + 1} events)`
```

Destructuring is also available in arrow parameters and in pattern bindings. The type checker validates annotated TypeScript types, including the `?T` produced by unchecked indexing.

## First-argument pipes

`x |> f` emits `f(x)`. When called with arguments, Hem inserts the piped value into the first argument position: `x |> f(a)` emits `f(x, a)`. The pipe has no runtime helper and does not curry arguments.

```hem
let slug = title |> normalize |> truncate(80) |> encodeURIComponent
```

This corresponds to the direct call:

```ts
const slug = encodeURIComponent(truncate(normalize(title), 80));
```

Libraries with fixed-arity, data-first functions use the same call signatures in both direct and piped code.

## Later arguments and partial application

The pipe operator inserts only into the first argument position. Wrap calls that require later arguments in an explicit arrow function so argument order remains clear.

```hem
let label = (prefix: string, value: number) => `${prefix}${value}`
let withLabel = (value: number) => label("value: ", value)
let render = (value: number) => value |> withLabel
```

Use the same explicit arrow syntax for partial application:

```hem
let withValue = (value: number) => label("value: ", value)
```

Hem does not infer curried functions or automatically pass a piped value to a later parameter.

## Arrows only

Functions always use arrow syntax: there is no `function` keyword and no automatic currying. Generic parameters, defaults, rest parameters, and `async` are supported. The checker can infer a type predicate from a checked implementation; explicitly authored `is` and `asserts` signatures are refused with HEM2029 because the signature does not prove the implementation. Imported predicate and assertion contracts remain usable. See [TypeScript interop](/reference/typescript-interop#predicates-and-assertion-functions).

```hem
let map = <A, B>(items: A[], transform: (item: A) => B): B[] => items.map(transform)
let names = map(users, (user) => user.name)
```

When a function requires named inputs, pass a standard options object. This is explicit in Hem and uses the same object shape in TypeScript.

## Normative source

Specification 0.2, §4 (bindings), §5 (functions), §6 (pipes), and §13 (modules and interop). The tour's §§2, 4, and 9 show the corresponding source and emitted forms.

Source: https://hem.run/reference/data-transformation/index.mdx
