A state interpreter may need to store StateVar Meteor, StateVar Int, and StateVar (Int, Float) together. A typed collection cannot hold these heterogeneous values directly, while a raw dictionary loses the type information exposed to application code. section 10.2.1
The Typed Avatar pattern separates the public handle from the runtime value. StateVar a carries a numeric identifier and its type parameter, while the runtime dictionary stores each corresponding TVar as Any. The interpreter converts values at this boundary and keeps the storage implementation hidden. section 10.2.1
A practical implementation combines these responsibilities:
- Assign each newly created variable a monotonically increasing identifier. section 10.2.1
- Store identifiers and heterogeneous TVars in a runtime-scoped dictionary protected for concurrent STM access. section 10.2.1
- Require readVar and writeVar to receive StateVar a, preserving the requested type at the language interface. section 10.2.1
- Perform unsafe conversion only inside the interpreter, where Any values are recovered as the handle's type. section 10.2.1
Typed state over an abstract language
A stateful operation can change from direct STM access to an abstract interface: readVar channel followed by writeVar channel (meteor : reported). Callers use StateVar [Meteor], while the interpreter resolves its identifier, accesses the untyped TVar, and returns the typed value. section 10.2.1
Listing 10.11 Runtime and interpreter of the StateL language
data StateRuntime = StateRuntime
{ _varId :: TVar VarId #1
, _state :: TMVar (Map VarId (TVar Any))
}
interpretStateF :: StateRuntime -> StateF r -> STM r #2
interpretStateF stateRt (ReadVar var next) = do
val <- readVar' stateRt var #3
pure (next val)
readVar' :: StateRuntime -> StateVar a -> STM a #4
readVar' stateRt (StateVar varId) = do
nodeState <- readTMVar (_state stateRt)
case Map.lookup varId nodeState of
Nothing -> error ("Var not found: " <> varId)
Just (RVarHandle tvar) -> do
anyVal <- readTVar tvar
let (val :: a) = usafeCoerce anyVal #5
pure a
The runtime and interpreter listing shows the identifier lookup, Any storage, and typed recovery that define the pattern. section 10.2.1
