LangL is a free-monadic language for application business logic. Instead of running raw IO directly, a LangL script describes operations such as delays, random-number generation, and mutable-state access; interpreters supply their runtime behavior. This keeps domain logic separate from application setup and process management. section 9.1.3 section 9.2.3
The separation is architectural. AppL prepares the application environment, configuration, processes, and their relationships, while LangL describes the logic that runs within that environment. Once work is evaluated in additional processes, that work is restricted to LangL scripts, making unconstrained nested thread creation unrepresentable through the business-logic interface. section 9.1.3
Listing 9.1 Two languages for the business logic layer
data LangF next where #1 Delay :: Int -> (() -> next) -> LangF next GetRandomInt :: (Int, Int) -> (Int -> next) -> LangF next NewVar :: a -> (IORef a -> next) -> LangF next ReadVar :: IORef a -> (a -> next) -> LangF next WriteVar :: IORef a -> a -> (() -> next) -> LangF next type LangL = Free LangF #2 delay :: Int -> LangL () #3 getRandomInt :: (Int, Int) -> LangL Int newVar :: a -> LangL (IORef a) readVar :: IORef a -> LangL a writeVar :: IORef a -> a -> LangL () data AppF next where #4 EvalLang :: LangL a -> (a -> next) -> AppF next ForkProcess :: LangL () -> (() -> next) -> AppF next type AppL = Free AppF forkProcess :: LangL () -> AppL () #5 evalLang :: LangL a -> AppL a
LangL is defined as Free LangF. Its smart constructors expose delay, random values, and IORef operations, while AppL can evaluate a LangL script or fork one as a process. section 9.1.3
Use the interface to make allowed business operations explicit:
- Describe pauses and random values with LangL operations rather than performing them directly. section 9.1.3
- Create, read, and write state only through LangL wrappers, not through arbitrary IO actions. section 9.2.3
- Move unsafe shared-state updates into STM transactions when concurrent business logic needs them. section 9.2.3
- Adapt focused subsystems, such as logging, into LangL rather than giving business code a broad undifferentiated interface. section 10.1.1
Wrapping IORef operations does not make their runtime effects disappear; it prevents client scripts from directly invoking IO. Allowing a general evalIO operation would let any LangL function introduce arbitrary effects, obscuring variable creation, ownership, and cross-thread access. For shared reporting state, STM provides a safer abstraction: the business operation is expressed as STM and executed atomically by the interpreter. section 9.2.3 section 9.2.3
Meteor reporting and logging
A meteor observer can generate a random meteor, add it to a reporting channel, and delay before the next observation, all as LangL actions. The application evaluates setup logic and forks the repeated observer as a process. Logging is added by adapting a LoggerL debug message into LangL, leaving formatting and console output to the logger interpreter. section 9.1.3 section 10.1.1
LangL is one layer in a hierarchy, not a claim that every concern belongs in one effect list. Its interface should stay focused on a coherent subsystem level. Additional languages can be nested where needed, but several free-monadic layers are not automatically appropriate for every application. section 9.1.3 section 10.1.1
