Dynamic Objects

One of the coolest features of Inform is to have attributes and properties of objects be dynamic; for instance, Slightly Wrong shows a room with a dynamic description. Originally I had this only for descriptions but it made more sense to be applied to entire objects.

The only times that we actually care about the underlying representation of an object is when creating them (because we need to supply an update function) and when reifying AbstractObjects from a State-based implementation. This is a big refactor I'm happy to have made because it cleanly breaks apart implementation (cached objects) and semantics (objects at a point in time). It does have the slight issue that we need to be cautious: if we reify a dynamic object at some point in time, then we change some part of the world that may affect its update function, we will need to re-reify the object.


{-# LANGUAGE TemplateHaskell #-}

module Yaifl.Objects.Dynamic 
  ( -- * Types
    TimestampedObject(..)
  , ObjectUpdate(..)
  , AbstractObject(..)
  , AbstractThing
  , AbstractRoom
  , AnyAbstractObject
  -- * Lenses
  , tsCachedObject
  , tsCacheStamp
  , tsUpdateFunc
  ) where

import Solitude ( Generic, Either, makeLenses, Eff )
import Yaifl.Common ( WorldModel, Timestamp, HasID(..) )
import Yaifl.Objects.Object ( Object )
import Yaifl.Objects.ObjectData ( RoomData, ThingData )

<<timestamped-object>>
<<abstract-object>>

Timestamped Objects

data TimestampedObject wm d = TimestampedObject
  { _tsCachedObject :: !(Object wm d)
  , _tsCacheStamp :: !Timestamp
  , _tsUpdateFunc :: ObjectUpdate wm d
  } deriving stock (Generic)

instance HasID (TimestampedObject wm d) where
  getID (TimestampedObject o _ _) = getID o

-- | Function to update an object. It is read-only on the world; i.e. it can only modify itself
newtype ObjectUpdate (wm :: WorldModel) d = ObjectUpdate
  { runObjectUpdate :: forall es. Object wm d -> Eff es (Object wm d)
  } 

Abstract Objects

data AbstractObject wm d
  = DynamicObject (TimestampedObject wm d)
  | StaticObject (Object wm d)

instance HasID (AbstractObject wm d) where
  getID (StaticObject o) = getID o
  getID (DynamicObject ts) = getID ts

type AbstractThing wm = AbstractObject wm ThingData
type AbstractRoom wm = AbstractObject wm (RoomData wm)
type AnyAbstractObject wm = AbstractObject wm (Either ThingData (RoomData wm))

makeLenses ''TimestampedObject