How do I use Consumer and Acceptor with concrete types?
← Generics · Ref: Q707
Consumer and Acceptor are the void-returning single-parameter function types. They have identical signatures except for purity.
CONSUMER IS PURE
Consumer of T takes a single parameter 't' and returns nothing. Because it is pure, it cannot call impure functions. Consumer implementations must be side-effect free.
ACCEPTOR IS IMPURE
Acceptor of T has the same signature but is NOT pure, so it CAN have side effects like I/O.
CREATING INSTANCES
Use dynamic function syntax to create concrete instances:
validator <- () is Consumer of String as pure function require t?
The body uses 't' — the parameter name defined by Consumer.
Note: Consumer is pure, so the dynamic function must use 'as pure function'.
USING AS PARAMETERS
Functions can accept Consumer or Acceptor to control what callers can do:
processItem()
-> item as String, handler as Consumer of String
handler(item)
BUILT-IN INTEGRATION
Optional and Result use both:
opt.whenPresent(myConsumer) // Pure read-only access opt.whenPresent(myAcceptor) // Can modify state
See Q54 for pure vs impure concepts. See Q87 for Result operations. See Q55 for passing functions as delegates.
Example
defines module qa.genericsdeep.consumeracceptor defines function <?- Helper that accepts a Consumer parameter (pure). Because the handler is a Consumer, this function knows no side effects will occur when it is called. -?> processWithConsumer() as pure -> item as String handler as Consumer of String handler(item) <?- Helper that accepts an Acceptor parameter (impure). The Acceptor may perform side effects like I/O. -?> processWithAcceptor() -> item as String handler as Acceptor of String handler(item) defines program ConsumerAcceptorDemo() stdout <- Stdout() //Consumer: pure, read-only — validate without side effects //Body uses 't' — the parameter name from Consumer's signature validator <- () is Consumer of String as pure function require t? processWithConsumer("Hello", validator) stdout.println("Consumer validation passed") //Acceptor: impure, can have side effects like printing printer <- () is Acceptor of String as function stdout <- Stdout() stdout.println(`Acceptor received: ${t}`) processWithAcceptor("World", printer) //Both work with Optional via whenPresent opt <- Optional("Steve") if opt? stdout.println(`Optional contains: ${opt.get()}`)
Common mistakes
E05150 — Consumer is pure — dynamic functions extending Consumer must use 'as pure function', not just 'as function'. The compiler requires the purity of the implementation to match its pure super type. See ek9 -h E05150 for details.
Incorrect:
() is Consumer of String as function
Correct:
() is Consumer of String as pure function
Other ways to ask this
- How do I create a Consumer of String or Acceptor of Integer?
- What is the difference between Consumer and Acceptor in practice?
- How do I pass Consumer and Acceptor as function parameters?
Coming from another language?
Java: java.util.function.Consumer<T> — no purity distinction, all consumers can have side effects. Kotlin: (T) -> Unit — no pure/impure variants. Rust: Fn(&T) for immutable borrow, FnMut(&mut T) for mutable — structural not nominal. Go: func(T) — no purity enforcement. C#: Action<T> — no purity concept. EK9: Consumer (pure) vs Acceptor (impure) gives compile-time purity guarantees.
Keywords: acceptor, function-type, delegate, void, pure, consumer, callback, parameter, impure, generic, side-effect