skip to content

Function Types & Arity

Reading a signature as a type: how many arguments, what it returns, and which substitutions still satisfy the caller. Interviewers use it to check you can type a callback correctly.

on this pageshow

questions

5

A renderer declares its cell-formatter hook as one value in, one display string out — what does that declaration constrain?

level: juniorimportance: must knowfreq 68%

answer

  1. a signature read as a type
  2. count the arguments first
  3. two positions, in and out
  4. arity is part of the type
  5. one value in, printable text back

basics

~20 s

A function type fixes arity plus what stands in each parameter position and in the result position. A supplied formatter must accept the one argument the renderer will pass and hand back something the renderer can print.

solid answer

~40 s

It constrains three things at once, and together they *are* the function's type. **Arity**: the renderer will call with exactly one argument, so a two-parameter formatter is a different type, not a near miss. **Parameter position**: whatever type the renderer promises to pass, the formatter must be ready for. **Result position**: whatever comes back, the renderer will print as display text without inspecting it. Everything else about a formatter — how it computes the text, whether it was written inline at the registration site or declared elsewhere — is invisible to the slot. Reading a hook is exactly this: count the arguments, name the type in each parameter position, name the type in result position.

code

pseudocode · 13 lines
pseudocode
// the slot the renderer declares once:
//   formatter takes one cell value, returns display text

function formatAmount(value)
    return groupDigits(value) + " USD"
end

function formatAmountWithRow(value, row)
    return groupDigits(value) + " " + row.currencyCode
end

renderer.setFormatter(column, formatAmount)          // fits: one argument in, text out
renderer.setFormatter(column, formatAmountWithRow)   // does not fit: declares two parameters

go deeper

for a junior

Be able to read a hook declaration out loud: how many arguments arrive, what type each one is, and what type goes back. That is the whole of a function type at this level.

for a middle

Explain why two one-argument functions that both return text can still have different types, and why the argument count is checked rather than assumed.

for a senior

When a slot is filled by dozens of teams, the declaration is the only documentation that is enforced. Show how you would review a new formatter against it without reading its body.

for a principal

Decide how much a published hook should promise. A narrow, precise parameter type makes implementations easy and the renderer rigid; a wide one does the reverse, and you own that trade.

A report renderer publishes one **cell-formatter hook** per column: a slot holding a function that turns the value stored in a cell into the text printed there. The renderer declares that slot once — one value in, one display string out — and forty columns fill it. That declaration is the entire contract between the renderer and the teams supplying formatters, and reading it is a skill with exactly three parts. ## A signature is a type, not a label Where functions are ordinary values, those values need types just as numbers and records do, and a function's type is built out of its signature. "Function" on its own is not a usable type, any more than "container" is: it says nothing about what may go in or come out. The usable type is the whole shape: - **arity** — how many arguments the call site passes, on every call; - **the type in each parameter position** — what will arrive in each of those argument slots; - **the type in result position** — what the caller receives back. Two formatters with the same three parts have the same type, however differently they are written and whatever they do inside. Two formatters differing in any one of the three have different types, however similar they look on the page. ## Arity is part of the type, not a calling convention The renderer's call site is fixed: it will pass one argument. A formatter declaring two parameters is therefore a value of a different type, not a near miss the renderer can patch up at registration. This is the part candidates most often treat as a formality, and it is the part a checker reports first — before it has compared a single parameter type. The mirror of that point matters too. Whether a formatter actually **uses** the argument it is handed is a property of its body, and bodies are invisible to the type. A formatter that ignores the cell value and returns a fixed placeholder fits the slot perfectly; a formatter that needs one more piece of information than the slot passes does not fit, however sensible its need is. ## The two positions face in opposite directions | Part of the declaration | Who produces the value | Who consumes it | What it obliges a formatter to do | |---|---|---|---| | Parameter position | the renderer | the formatter | be ready for every value the renderer may pass | | Result position | the formatter | the renderer | hand back only values the renderer can print | That asymmetry is the thing to carry away from a first reading. The parameter position tells you what you must be prepared to cope with; the result position tells you what you have promised to produce. They are not two copies of one rule, and the later question of which near-misses are still acceptable turns entirely on the difference between them. ## What the declaration deliberately leaves open - **How the text is produced** — reading a field, formatting a number, looking something up, or returning a constant. None of it is in the type. - **Where the function came from** — written inline where the column is registered, or declared elsewhere and referred to by name. Same type either way. - **Whether the value is shared** — one formatter may fill several columns' slots, and the type cannot tell you whether it does. - **What it costs** — a function type carries no statement about speed, allocation or logging. A slow formatter and a fast one of the same shape are interchangeable as far as the contract goes. ## Reading an unfamiliar hook, in four steps 1. **Count the parameters the call site passes.** That number is the first half of the type, and a mismatch here ends the question. 2. **Name the type standing in each parameter position.** Ask what the caller promises to put there, not what you hope to receive. 3. **Name the result type**, and ask what the caller does with it — printing it directly is a much stronger constraint than storing it for later inspection. 4. **Check your candidate function against all three** before reading a line of its body. If any of the three disagrees, the body is irrelevant. ## Why an interviewer opens here This is the question that separates a candidate who thinks of a function as a piece of code from one who thinks of it as a value with a type. The second candidate can type a callback correctly on the first attempt, can read an unfamiliar hook out loud, and can say why two similar-looking functions are not interchangeable. The first tends to reach for the body, discover the mismatch at run time, and describe it as a wiring problem rather than a type problem.

  • Two formatters each take one argument and return text, but one expects a timestamp and the other an amount — do they share a type?
    No. The type standing in parameter position is part of the function type, so these are two different types that merely share an arity and a result type. Only the one whose parameter type covers what the renderer actually passes may fill that renderer's slot.
  • What does the slot's declared result type promise the renderer, and what does it leave open?
    It promises that whatever comes back can be printed as display text, so the renderer needs no test, narrowing or conversion before printing. It leaves entirely open how the text was produced — a lookup, a calculation, or a constant — because none of that is part of a function type.

saying these in an interview costs you the question

  • Thinks any function fits as long as it is a function.
  • Treats the argument count as a calling convention, not part of the type.
  • Reads only the result type and ignores what arrives in parameter position.
  • Assumes a formatter may reach for data the slot never passes it.
  • Believes a function written inline has a different type from a declared one of the same shape.
open as a page

Why does a formatter that accepts any value still fit an amount-in, text-out slot, while one returning any value does not?

level: middleimportance: must knowfreq 58%

basics

~10 s

The caller only ever passes an amount and always prints what comes back. So a formatter may accept wider than promised but must return no wider: looser in parameter position, tighter in result position.

open as a page

The renderer calls every formatter with two arguments, the value and its row — what does supplying a one-parameter function violate?

level: middleimportance: should knowfreq 46%

basics

~20 s

Arity is part of a function's type, not a detail of the body. A one-parameter function therefore has a different type from a two-argument slot, and needs an adapter that takes both arguments and forwards one.

open as a page

Forty columns each declare the same one-argument-to-text formatter shape inline — what does giving that shape one name buy you?

level: middleimportance: should knowfreq 42%

basics

~20 s

Naming a recurring function type gives the contract one place to be read and changed, and reviewers a word for it. Where types match by shape it changes no compatibility; where they match by name, the name becomes the type.

open as a page

Forty teams implement your published formatter slot — which edits to its declared type leave every existing implementation valid?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Two edits are safe: narrowing the parameter type the slot promises to pass, and widening the result type it will accept back. Both ask implementers for less. Widening the parameter, narrowing the result or changing arity moves work onto all forty teams.

open as a page