liveBook · Manning

Typed avatar pattern

As explained in Functional Design and Architecture: Examples in Haskell

The Typed Avatar pattern represents an instantiated runtime object with a typed identifier while the actual value is stored in an untyped structure.

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

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains identifiers, runtime dictionaries, STM protection, typed constructors, and interpreter-level unsafe conversion.
Examples
Uses StateVar values, Meteor collections, ReadVar and WriteVar operations, and a concurrent StateRuntime dictionary.
What it assumes
Assumes STM and free-monad concepts, while demonstrating how typed state operations cross an untyped runtime boundary.
What you can do after
Build it from scratch as a typed state language backed by heterogeneous STM storage.
How it connects
Connects the Typed-Untyped design pattern with STM, free monads, embedded DSLs, and abstracted state subsystems.

This explainer was generated by Manning's knowledge graph from the text of Functional Design and Architecture: Examples in Haskell. The section links point to the parts of the book it draws on.