yi-0.5.2: Data/Prototype.hs
{-# LANGUAGE Rank2Types #-}
-- | Support for OO-like prototypes.
module Data.Prototype where
import Data.Function (fix)
-- | A prototype. Typically the parameter will be a record type.
-- Fields can be defined in terms of others fields, with the
-- idea that some of these definitons can be overridden.
--
-- Example:
--
-- > data O = O {f1, f2, f3 :: Int}
-- > deriving Show
-- > o1 = Proto $ \self -> O
-- > {
-- > f1 = 1,
-- > f2 = f1 self + 1, -- 'f1 self' refers to the overriden definition of f1.
-- > f3 = f1 self + 2
-- > }
newtype Proto a = Proto {fromProto :: a -> a}
-- | Get the value of a prototype.
-- This can return bottom in case some fields are recursively defined in terms of each other.
extractValue :: Proto t -> t
extractValue (Proto o) = fix o
-- | Override a prototype. Fields can be defined in terms of their definition in the base prototype.
--
-- Example:
--
-- > o2 = o1 `override` \super self -> super
-- > {
-- > f1 = f1 super + 10,
-- > f3 = f3 super + 1
-- > }
override :: Proto a -> (a -> a -> a) -> Proto a
override (Proto base) extension = Proto (\self -> let super = base self
in extension super self)
-- | Field access
(.->) :: forall t a. Proto t -> (t -> a) -> a
p .-> f = f (extractValue p)