Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Monadic Operations on MLIR’s FailureOr

7/23/2025

While working on my toy MLIR-based OCaml compiler, I found myself wishing I had a way to use the monadic operations I’m familiar with in functional languages, but with the primitive types in MLIR (and C++ more generally). This is not an uncommon desire, but this particular case was painful enough get me to write some utilities.

LLVM and MLIR Types

There are a few types used all over MLIR that are ripe for monadic operations in my opinion, the llvm::FailureOr and llvm::LogicalResult types being the most obvious. In fact, the C++ standard library already has some monadic operations for the std::expected and std::optional types in the form of member functions taking callables.

mlir::FailureOr<mlir::Value> MLIRGen3::genInfixExpression(const Node node) {
  const auto children = getNamedChildren(node);
  const auto lhs = children[0];
  const auto rhs = children[2];
  const auto op = children[1];
  auto l = loc(node);
  return genCallee(op) |
    and_then([&](auto callee) -> mlir::FailureOr<mlir::Value> {
    return gen(lhs) |
        and_then([&](auto lhsValue) -> mlir::FailureOr<mlir::Value> {
        return gen(rhs) |
            and_then([&](auto rhsValue) {
                return genApplication(l, callee, {lhsValue, rhsValue});
            });
        });
    });
}

Sugar for Monadic Bind

As I detaild in this post, functional programming is uniquely suited to the task of writing compilers.

// Wrapper that stores the continuation for a successful value.
template <typename F>
struct AndThenWrapper {
  F func;
};

template <typename F>
auto and_then(F &&f) -> AndThenWrapper<std::decay_t<F>> {
  return {std::forward<F>(f)};
}

template <typename T, typename F,
          typename R = decltype(std::declval<F &&>()(*std::declval<mlir::FailureOr<T>>() ))>
auto operator|(mlir::FailureOr<T> value, AndThenWrapper<F> wrapper) -> R {
  if (mlir::failed(value)) {
    return mlir::failure();
  }
  return wrapper.func(*value);
}