将数据类型定义为 MonadSample

Defining a data type as MonadSample

我正在尝试定义一种玩具概率编程语言来测试各种推理算法及其有效性。我按照 this tutorial to create a Scheme like language with a basic structure. Now I want to use the monad-bayes 库添加了概率后端。我的最终目标是支持从分布中抽样和观察。这是我的表达式的定义

data LispVal = Atom String                  -- Stores a string naming the atom
             | List [LispVal]               -- List of expressions
             | DottedList [LispVal] LispVal -- List of elements but last
             | Integer Integer               -- Int
             | Float Double
             | String String                -- Str
             | Bool Bool                    -- Bool
             | Port Handle
             | PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
             | IOFunc ([LispVal] -> IOThrowsError LispVal)
             | Func { params :: [String], vararg :: Maybe String,
                      body :: [LispVal], closure :: Env }

现在,我想添加 MonadSample 和 MonadInfer 类型来支持库中的函数。但是,简单地添加 | ModelSample MonadSample LispVal 是行不通的。我多次查看库的源代码,但我似乎不太了解它,其中有很多 monad-transformations 正在进行。这就是他们在库中定义基本分布的方式

class Monad m => MonadSample m where
  -- | Draw from a uniform distribution.
  random ::
    -- | \(\sim \mathcal{U}(0, 1)\)
    m Double

  -- | Draw from a uniform distribution.
  uniform ::
    -- | lower bound a
    Double ->
    -- | upper bound b
    Double ->
    -- | \(\sim \mathcal{U}(a, b)\).
    m Double
  uniform a b = draw (uniformDistr a b)

  -- | Draw from a normal distribution.
  normal ::
    -- | mean μ
    Double ->
    -- | standard deviation σ
    Double ->
    -- | \(\sim \mathcal{N}(\mu, \sigma^2)\)
    m Double
  normal m s = draw (normalDistr m s)

  -- | Draw from a gamma distribution.
  gamma ::
    -- | shape k
    Double ->
    -- | scale θ
    Double ->
    -- | \(\sim \Gamma(k, \theta)\)
    m Double
  gamma shape scale = draw (gammaDistr shape scale)

  -- | Draw from a beta distribution.
  beta ::
    -- | shape α
    Double ->
    -- | shape β
    Double ->
    -- | \(\sim \mathrm{Beta}(\alpha, \beta)\)
    m Double
  beta a b = draw (betaDistr a b)

  -- | Draw from a Bernoulli distribution.
  bernoulli ::
    -- | probability p
    Double ->
    -- | \(\sim \mathrm{B}(1, p)\)
    m Bool
  bernoulli p = fmap (< p) random

  -- | Draw from a categorical distribution.
  categorical ::
    Vector v Double =>
    -- | event probabilities
    v Double ->
    -- | outcome category
    m Int
  categorical ps = fromPMF (ps !)

  -- | Draw from a categorical distribution in the log domain.
  logCategorical ::
    (Vector v (Log Double), Vector v Double) =>
    -- | event probabilities
    v (Log Double) ->
    -- | outcome category
    m Int
  logCategorical = categorical . VG.map (exp . ln)

  -- | Draw from a discrete uniform distribution.
  uniformD ::
    -- | observable outcomes @xs@
    [a] ->
    -- | \(\sim \mathcal{U}\{\mathrm{xs}\}\)
    m a
  uniformD xs = do
    let n = Prelude.length xs
    i <- categorical $ V.replicate n (1 / fromIntegral n)
    return (xs !! i)

  -- | Draw from a geometric distribution.
  geometric ::
    -- | success rate p
    Double ->
    -- | \(\sim\) number of failed Bernoulli trials with success probability p before first success
    m Int
  geometric = discrete . geometric0

  -- | Draw from a Poisson distribution.
  poisson ::
    -- | parameter λ
    Double ->
    -- | \(\sim \mathrm{Pois}(\lambda)\)
    m Int
  poisson = discrete . Poisson.poisson

有没有办法让所有这些都包含在我拥有的 LispVal 数据中?或者我只是遵循了错误的逻辑,有没有更好的方法来做到这一点?

我也欢迎任何关于如何用我的语言集成这个库的更多建议。附带说明一下,我的目标不是集成所有功能,而只是构建功能性概率编程语言的最低限度。

提前致谢!

编辑:澄清一下,这里有一个示例程序,我希望能够 运行 使用我的语言。

(define (model1 upper lower) (sample (uniform upper lower)))

此函数将 return 来自具有给定 constraints.The haskell 的均匀分布的样本 haskell 库允许通过以下方式执行此操作

a = sampleIO $ (uniform upper lower)

我希望能够使用相同的功能。我最初尝试的只是将 | ModelSample MonadSample LispVal 给出错误“Expected a type, but 'MonadSample LispVal' has type Constraint” 我查找了错误但找不到将此 Monad 固化为类型的方法可以被我的表情使用

数据声明需要使用具体类型,但MonadSample是一个约束。它描述的是行为而不是实现。来自 hackage,MonadSample 的一个实例是 SamplerIO,您可以在数据声明中使用它。例如

data LispVal =
--  [...]
  | Sample (SamplerIO LispVal)

通过快速查看您拥有的内容,我建议 'compiling' 调用 sample 到您现有的 IOFunc 构造函数中。例如你的榜样

(sample (uniform upper lower))

可以编译成IOFunc (\_ -> sampleIO $ uniform upper lower)

这样你就不需要添加你决定添加为构造函数的每个未来单子。他们都可以 运行 in IO.