Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why reimplement Functor, Applicative, Monad instances for known transformers? #209

Open
turion opened this issue Oct 12, 2022 · 1 comment

Comments

@turion
Copy link
Collaborator

turion commented Oct 12, 2022

Many monads defined in monad-bayes are known transformers. For example:

-- | Collection of random variables sampler during the program's execution.
data Trace a = Trace
  { -- | Sequence of random variables sampler during the program's execution.
    variables :: [Double],
    --
    output :: a,
    -- | The probability of observing this particular sequence.
    density :: Log Double
  }

This is isomorphic to:

-- | Collection of random variables sampler during the program's execution.
data TraceData = TraceData
  { -- | Sequence of random variables sampler during the program's execution.
    variables :: [Double],
    -- | The probability of observing this particular sequence.
    density :: Log Double
  }
  deriving (Semigroup, Monoid)

newtype Trace a = Trace { getTrace :: Writer TraceData a }

The advantage is that one gets correct and efficient Functor, Monad,... instances that way, and saves code. (It's notoriously difficult to get a performant Writer, so it's a good idea to use existing code instead of reimplementing it.)

Similarly:

-- | Tracing monad that records random choices made in the program.
data Traced m a = Traced
  { -- | Run the program with a modified trace.
    model :: Weighted (FreeSampler Identity) a,
    -- | Record trace and output.
    traceDist :: m (Trace a)
  }

This is the same as promoting Trace to a transformer and taking the product:

newtype TraceT m a = TraceT { getTrace :: WriterT TraceData m a }

-- | Tracing monad that records random choices made in the program.
newtype Traced m a = Traced { getTraced :: Product (Weighted (FreeSampler Identity)) (TraceT m) }

Again one gets all the instances for free now. The haddocks of the record fields can be moved to a custom constructor function:

traced ::
  -- | Run the program with a modified trace.
  Weighted (FreeSampler Identity) a ->
  -- | Record trace and output.
  m (Trace a) ->
  Traced m a
traced = ...
@reubenharry
Copy link
Contributor

Highly in favour of this refactor!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants