Skip to content
tezvyn:

How would you implement a type-safe Kotlin DSL for a UI Form?

Source: kotlinlang.orgHardHow cards are made

How would you implement a type-safe Kotlin DSL for a UI Form?

Tests Kotlin DSL scoping with lambda receivers and extensions. Strong answers sketch a Form builder using initTag, explain T.() -> Unit implicit this for child elements, and add DslMarker to block scope leaks. Red flag: describing it as simple method chaining.

What's really being asked

The interviewer wants to see if you understand how Kotlin transforms function literals with receiver into hierarchical type-safe builders. They care about scope control, implicit receiver management, and the difference between a fluent API and a true DSL. Senior candidates should demonstrate why extension functions on the receiver enable discoverability and how DslMarker annotations prevent invalid nesting at compile time.

The full answer

First, define a Form class that acts as the root receiver and expose child factory methods like textField and button as extensions or members on Form. Second, explain that each factory takes a lambda with receiver, for example TextField.() -> Unit, so inside the block the implicit this is the new child instance, allowing direct property assignment. Third, describe a generic initTag helper that instantiates the child, executes its init lambda, and attaches it to the parent list, which keeps the code DRY across all node types. Fourth, mention adding a DslMarker annotation to the base Tag class so the compiler rejects attempts to call a Button method from inside a TextField block, which enforces the tree structure.

The mistakes people make

Saying you would use a standard Builder class with chained setters misses the entire point of receiver scoping. Proposing plain higher-order functions without a receiver type loses the implicit this that makes the syntax declarative. Forgetting DslMarker or claiming scope leaks are acceptable at runtime shows a lack of production DSL experience. Another red flag is suggesting reflection or code generation when the language provides first-class builder support.

What usually comes next

How would you support optional attributes or conditional blocks without breaking the DSL syntax? Can you make the DSL extensible so consumers add custom components without modifying the Form class? How do you handle rendering or diffing the tree after construction? What are the tradeoffs between inline reified helpers and explicit tag classes for performance?

A concrete example

Imagine a Form builder where form is a top-level function returning Form. Inside, textField takes TextField.() -> Unit. The TextField class has var label and var hint. The form function creates Form, calls its lambda, and inside that lambda textField is available because it is a member of Form. The textField helper uses initTag with TextField and init which runs the lambda with TextField as receiver, so the caller writes label equals Email directly. A DslMarker annotation on the base Component class ensures the compiler errors if someone accidentally calls button inside textField.

Interview question

When designing a type-safe Kotlin Form DSL, what does applying a DslMarker annotation to the base Component class primarily enforce?

  • a.It causes the compiler to reject calls to outer receiver methods from inside an inner child lambdaCorrect
  • b.It allows the initTag helper to instantiate child nodes without reifying the generic type
  • c.It generates child factory methods automatically to reduce boilerplate
  • d.It transforms chained setter calls into nested lambda blocks for readability
Why?

DslMarker restricts implicit receiver scope so the compiler blocks invalid nesting such as calling a button method inside a textField block. Option D is tempting but wrong because the annotation does not convert chained setters into lambdas; the hierarchical syntax comes from extension functions with receivers, not DslMarker.

Just read this? Test yourself on what you have been reading.

Read the original → kotlinlang.org

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on kotlin — each one lists the topics its interview covers.

See open roles