Overview

14 Solving common problems functionally

This chapter invites you to think functionally: approach everyday programming tasks by composing small, reusable functions and embracing abstraction, immutability, and totality. Rather than re-implementing “patterns,” you build up a toolbox of concrete, reusable functions and types that model errors, side effects, and computations explicitly. The payoff is code that composes cleanly, remains testable, and makes failure modes visible instead of implicit or ad hoc.

It first recasts common tasks with safer, declarative utilities. Assertions and input validation are expressed with a Result type, turning partial functions into total ones and replacing exceptions with composable outcomes; generic validators illustrate how to lift pre/postcondition checks into reusable helpers. For unreliable operations, a retry combinator abstracts “try again with delay” logic for both functions and effects, returning a Result instead of throwing. Reading configuration is made robust by loading properties into Result, enriching error messages, and providing typed readers (strings, numbers, lists, enums, and even arbitrary objects) through parsing functions and mapping/flatMapping, all while avoiding exceptions and nulls.

The chapter then demonstrates how to wrap imperative libraries in a thin functional layer through a case study that converts an XML reader to a modular, testable design. The solution decomposes the workflow (read file, parse document, transform elements, apply effect), isolates effects, and propagates failures with Result. It further improves safety and clarity by parameterizing dependencies and behaviors (file path, root element, element-processing function, and output effect), introducing value types to prevent argument mix-ups, returning Result from element-processing to capture data errors, and using sequence to turn a list of Results into a single aggregated Result. With currying and partial application, clients supply only the specifics they need, yielding a reusable, composable, and safer program that interoperates cleanly with nonfunctional code.

 Summary

  • Putting values in the Result context is the functional equivalent of assertions.
  • Property files can be read in a safe manner using the Result context.
  • Functional property reading relieves you of handling conversion errors.
  • Properties can be read as any type, enum, or collection in an abstracted way.
  • Automatic retries can be abstracted into functions
  • Functional wrappers can be built around legacy imperative libraries.

FAQ

How should I validate data functionally instead of using traditional assertions?Use total functions that return Result rather than throwing. Write small predicates (for example, isPositive, isValidName) and compose them with helper functions like assertCondition, assertTrue/assertFalse, assertNotNull, assertPositive. Transform partial functions (for example, inverse) into total ones by returning Result.Success on valid input and Result.Failure with a clear message otherwise.
Why avoid throwing exceptions for input checks and assertions?Exceptions break referential transparency and make functions hard to compose and test. Java/Kotlin assertions can be disabled at runtime. Returning Result encodes failures in the type, enables safe composition (map/flatMap), and lets you attach precise, domain-specific error messages.
How can I automatically retry a function or effect safely?Wrap the function with a retry combinator that never rethrows. Signature: fun <A, B> retry(f: (A) -> B, times: Int, delay: Long = 10): (A) -> Result<B>. Internally, attempt f, catch exceptions, sleep between attempts, and return the first success as Result.Success or a failure after all retries. Works for effects returning Unit as well.
What’s a functional way to read a properties file with helpful errors?Encapsulate loading in PropertyReader that returns Result<Properties>. Wrap getResourceAsStream and handle all outcomes explicitly: null stream (“File X not found in classpath”), IO issues, or unexpected exceptions. Expose methods that return Result for downstream composition rather than throwing.
How do I read string properties without accidental Success(null)?Don’t map directly. Use flatMap + Result.of around getProperty and then mapFailure to enrich the error with the missing key name. Example idea: readAsString(name) = properties.flatMap { Result.of { it.getProperty(name) } }.mapFailure("Property \"name\" not found").
How can I parse numeric/collection properties and surface clear parsing errors?Layer typed readers on top of readAsString: readAsInt, readAsDouble, etc., using try/catch to convert and returning Result.failure with messages like “Invalid value while parsing property 'id' to Int: 'three'”. For lists, use a generic readAsList(name, f) that splits and maps with f, then specialize (readAsIntList, readAsDoubleList, readAsBooleanList).
How do I read enum values or arbitrary domain objects from properties?Generalize with readAsType(f: (String) -> Result<T>, name). For enums, use a reified inline helper that calls enumValueOf<T> and wraps errors in Result with a message naming the property and enum type. For domain objects (for example, Person), parse a serialized string (id:3;firstName:Jane;lastName:Doe) by turning “;” into newlines, feeding a secondary PropertyReader, and composing typed reads; also support lists of such objects.
How can I refactor an imperative XML reader into a functional one?Decompose into small, reusable functions returning Result: readFile2String(path), readDocument(rootName, xmlString), and a pure mapper from Element to your output type. Compose with flatMap/map, and push the effect (for example, printing) to the outermost layer via forEach. Optionally return a program (() -> Unit) that, when run, applies the effect to the successful result.
How do I make the XML pipeline safer and more generic?- Use value types (FilePath, ElementName) to prevent argument swapping and validate early. - Pass the element-processing function as a parameter: (Element) -> Result<T>, then sequence the list (List<Result<T>> -> Result<List<T>>). - Wrap JDOM getChildText with a helper that replaces null with a clear message, and make processElement return Result with informative failures.
What general practices help adapt imperative libraries to a functional style?- Build thin wrappers that catch all exceptions and return Result. - Keep effects at the edges; core logic is pure and total. - Parameterize environment/config (paths, element names, effects) instead of closing over them; use currying to inject configuration explicitly. - Prefer Result over nullables; always enrich failures with actionable context. - Reuse small, composable functions; avoid duplication by abstracting common patterns (validation, retry, parsing, sequencing).

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • The Joy of Kotlin ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • The Joy of Kotlin ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • The Joy of Kotlin ebook for free