Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

OCaml Module 12

Effects, concurrency and parallelism (OCaml 5) interview questions

Interview questions on effects, concurrency and parallelism (OCaml 5): warm-ups, then what each harder question tests and how a good answer starts.

19 questions: 5 warm-ups and 14 with model answers · Levels: Fresher to staff

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher to mid

    Both kinds of handler catch something thrown from deep inside a call. What can an effect handler do that an exception handler cannot?

    Both are found the same way: the nearest enclosing handler with a matching case runs, and one without a case lets the exception or the effect go on outward. The difference is what the handler receives. An exception handler gets the exception's value, and the code that raised it has already been abandoned, so the handler can only take over. An effect handler also gets the continuation, the suspended rest of the computation from the perform onwards, and can resume it with continue k v, which makes the perform return v. That makes an effect a question the caller answers, such as a setting or a log line. It also brings duties an exception lacks: a continuation can be resumed only once, or Continuation_already_resumed is raised, and one that will not be continued should be ended with discontinue so its cleanup runs.

    From the lesson Effect handlers

  2. Fresher to mid

    Why would a program turn a push-style iterator into a Yield-based generator?

    Answered in the lesson Generators and schedulers with effects

  3. Fresher to mid

    What is an Eio fiber, and how does Eio.Fiber.both differ from spawning two threads?

    Answered in the lesson Concurrent I/O with Eio and Lwt

  4. Fresher to mid

    What does Domainslib's parallel_for_reduce do, and why must its reducer be associative?

    It runs a body on every number of a range on a task pool's domains and combines the results with a reducer. The library combines the parts in an arbitrary grouping, and its interface asks for an associative reducer to get a deterministic result: ( + ) let pool.ml sum 1 to 1,000,000 to 500000500000 on 18 domains, as one domain does, while ( - ) would give an answer that depends on the split.

    From the lesson Parallel patterns and race detection

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Mid to senior

    A service installs one settings handler around its main function, and its code reads limits with perform (Ask "max_rows"). After request work moves into Domain.spawn, every request fails with Effect.Unhandled (Ask "max_rows"), although the handler is still in place. Why, and how do you fix it?

    What it tests
    Knowing that a deep handler covers only the stack of the computation it runs, that a new domain or thread starts without handlers, and choosing a fix that keeps the effect design.
    First step
    Find where the handler sits relative to Domain.spawn, then reproduce the failure in a few lines: a handler around Domain.join (Domain.spawn f), where f performs Ask. Before changing anything, ask on whose stack that perform runs, and which handlers that stack holds.
    Follow-ups

    From the lesson Effect handlers

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 5 minutes
  2. Mid to senior

    A pricing function reads settings through an Ask effect and writes progress lines through a Log effect. You want unit tests that run it with no files, no globals and no printing, and that check both its result and what it logged. How do you write the test handler, and what should each test assert?

    What it tests
    Using one handler as a test double for several effects, collecting what the code reports, and making missing or unexpected effects fail loudly instead of going unnoticed.
    First step
    Write one function, run_test settings f, that runs f () in a match with an effect case for each effect: Ask answered from the test's own table, Log appended to a list created inside run_test. Return the value and the collected lines together, so every test can assert on both.
    Follow-ups

    From the lesson Effect handlers

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  3. Senior to staff

    A CSV importer meets rows with no price. One caller wants such rows skipped and counted, another wants a default price, a third wants the import stopped at the first bad row, and the importer must not know which. Design this with an effect, and say how stopping should work.

    What it tests
    Designing a resumable error as an effect whose answer carries the caller's decision, and ending a suspended computation with discontinue, compared with exceptions and result types.
    First step
    Give the effect an answer type that can express every decision: Missing_price : int -> float option Effect.t, where Some p means use p and None means skip the row. Each caller installs its own handler around the same import; skipping and the default are both continue, while stopping must end the suspended import.
    Follow-ups

    From the lesson Effect handlers

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 5 minutes
  4. Mid to senior

    Walk through the first six prints of a round-robin scheduler on three tasks A, B and C that each print their name twice and perform Yield between them. The program forks B, then forks C, then runs A inline, each with no other work. What order comes out, and why?

    What it tests
    Reading a run queue of continuations as a FIFO, telling what Fork and Yield each put in the queue and what runs next, and tracing a run on paper.
    First step
    Draw the queue after every effect and write down which task is running. Before tracing, settle two rules: what Fork does with the task that performs it and with the new task, and what Yield does with the current task. Those two rules fix the whole order; then step until every task has returned.
    Follow-ups

    From the lesson Generators and schedulers with effects

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  5. Senior to staff

    You are porting a toplevel-style interpreter from Effect.Deep to Effect.Shallow because its rules change between performs: each step reads a token and chooses the next handler from it. What has to change in each effc branch, and when would you keep the Deep version instead?

    What it tests
    Distinguishing a continuation that keeps its handler from one that does not, and reaching for the right API for a state-machine handler against a scheduler-style handler.
    First step
    Start from what each API hands the branch: a deep continuation still carries its handler, while a shallow one comes back with none. List, branch by branch, what the next perform must find in place, and only then decide where the handler for that next perform is built.
    Follow-ups

    From the lesson Generators and schedulers with effects

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  6. Senior to staff

    A colleague wants their effect scheduler to resume a task after a GTK button-click signal arrives, through caml_callback. Why does OCaml 5.5 not support performing an effect from the signal handler, and what alternative design would you suggest?

    What it tests
    Knowing the official boundary (OCaml 5.2 Changes #12456) between effects and C callbacks, and designing round it rather than fighting it.
    First step
    Trace how the click reaches OCaml: GTK calls a C function, which enters OCaml through caml_callback rather than from inside a fiber of the scheduler. Ask whether a perform made there can reach the scheduler's handler, read what the manual says about the limits of effects, and design from that answer.
    Follow-ups

    From the lesson Generators and schedulers with effects

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  7. Mid to senior

    A service that calls one slow upstream has p99 latency 3 s when the upstream slows down, and p50 under 50 ms when it is healthy. The team writes the call under Eio. How would you keep p99 near the SLO without changing the upstream?

    What it tests
    Reaching for Eio.Time.with_timeout as the first lever, understanding what cancellation does to the slow fiber, and picking a budget that fits the SLO rather than one picked by the slow upstream.
    First step
    Separate what the service controls from what it does not: it cannot make the upstream faster, but it decides how long it waits for it. Work out this hop's share of the latency target, and decide what the caller should get when that share runs out, before choosing the Eio call that enforces it.
    Follow-ups

    From the lesson Concurrent I/O with Eio and Lwt

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 5 minutes
  8. Mid to senior

    A line-based Eio server reads one request a connection with Eio.Buf_read.line, no cap. A client opens a connection and sends 1 MB of bytes with no newline. The process memory climbs. What would you change, and why?

    What it tests
    Spotting that an unbounded line read is an input-validation gap, reaching for Buf_read.of_flow ~max_size:N and knowing where N comes from, and understanding that the handler must catch Buffer_limit_exceeded and reply.
    First step
    Ask what bounds the memory one connection can take while the reader waits for a newline. Find where the buffered reader is created and what limit it was given, decide the longest line the protocol really needs, and plan what the client should receive when a line is longer.
    Follow-ups

    From the lesson Concurrent I/O with Eio and Lwt

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 5 minutes
  9. Senior to staff

    A team is writing a new HTTP service in OCaml. One engineer wants Eio for direct style; another wants Lwt, because the shared logging library and the client for the next service are both cohttp-lwt. What would you propose?

    What it tests
    Weighing OCaml 5 direct style against an existing Lwt ecosystem rather than choosing by novelty, and knowing what a bridge between the two libraries can and cannot do.
    First step
    List the integration points: logging, the HTTP client to the next service, metrics, the database. For each, note which concurrency library it is built on and whether a maintained version for the other one exists. Then decide which library most of the service's code will live in, and where the rest would have to cross over.
    Follow-ups

    From the lesson Concurrent I/O with Eio and Lwt

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 6 minutes
  10. Mid to senior

    Your team wrapped the shared counter in Atomic and the lost updates stopped. A later refactor made it a record { mutable count : int [@atomic]; mutable total : int [@atomic] } where every bump increments count and adds the request's amount into total. Under load the two fields disagree again. Where is the race?

    What it tests
    Reading what an atomic word or atomic field actually makes indivisible, and why a step across several fields needs a mutex that covers the whole body.
    First step
    State exactly what one atomic operation makes indivisible, then write one bump out as the separate steps a second domain could observe, including the moment after count changed and before total did. Decide from that what has to cover both fields as a single step.
    Follow-ups

    From the lesson Parallelism with domains and atomics

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    A background worker runs on a domain, and a bug makes it raise Failure "db connection refused" on start-up. The parent program keeps running and never prints the failure; its logs show only the worker never produced any output. Why, and how do you fix it on OCaml 5.5?

    What it tests
    Knowing that a domain propagates an exception only through Domain.join and that 5.5 keeps the worker's backtrace intact; recognising the pattern of a spawn with no corresponding join.
    First step
    Look at where the worker domain is spawned and whether anything ever joins it. Then ask where an exception that ends a domain's function goes, and which call is the only one that hands it to another domain.
    Follow-ups

    From the lesson Parallelism with domains and atomics

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Mid to senior

    A parallel_for_reduce over 10,000,000 numbers runs slower on 16 domains than a plain for loop on one domain, and the call passes ~chunk_size:1. What is going on, and how do you choose a better chunk size?

    What it tests
    Whether the candidate links the chunk size to the number of tasks the pool makes, knows what goes wrong at both ends, and chooses a size by measuring instead of guessing.
    First step
    Count the tasks the call makes. With chunk_size 1 the loop halves the range down to single numbers, so ten million numbers make about ten million tasks; compare what making, queueing and taking one task costs with one iteration of a cheap body. Then plan how you will time each size you try.
    Follow-ups

    From the lesson Parallel patterns and race detection

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  13. Mid to senior

    A team compiles the tests of its OCaml 5 service in a ThreadSanitizer switch and runs them ten times with no report. A colleague says this proves the service has no data race. Is the colleague right, and what would you do next?

    What it tests
    Knowing that ThreadSanitizer reports only the unordered, conflicting accesses a run performs, the other blind spots the manual names, and how to make a hidden race likely to show.
    First step
    Start from what a ThreadSanitizer report is about: the unordered, conflicting accesses one run performed. Ask what ten clean runs say about an interleaving they never produced, and about shared state no test touched, before deciding what the next runs must do differently.
    Follow-ups

    From the lesson Parallel patterns and race detection

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  14. Senior to staff

    A teammate proposes replacing every Mutex-protected Queue in an OCaml 5 service with Saturn's lock-free queue, because lock-free is faster. What would you check before agreeing?

    What it tests
    Whether the candidate weighs contention, progress when a domain stalls, composability, waiting for items and the measured cost on the real workload, instead of assuming that lock-free means faster.
    First step
    Ask what each queue does, and measure both versions on the real workload before changing anything: a lock-free queue never waits for a lock, which is not the same thing as being faster. Then list what each queue's users rely on: two operations that must happen as one, how consumers wait for an item, and whether the queue needs to be shared at all.
    Follow-ups

    From the lesson Parallel patterns and race detection

    Model answer, Pro.

    In the answer:

    • Model answer
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All OCaml interview questions · The OCaml track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.