将数据类型定义为 MonadSample

问题描述

我正在尝试定义一种玩具概率编程语言来测试各种推理算法及其有效性。我按照 this tutorial 创建了一个具有基本结构的类似 Scheme 的语言。现在我想使用 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 转换正在进行。这就是他们在库中定义基本分布的方式

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)))

函数将仅从具有给定约束的均匀分布中返回一个样本。haskell 库允许通过以下方式执行此操作

a = sampleIO $ (uniform upper lower)

我希望能够使用相同的功能。我最初尝试的只是放置 | ModelSample MonadSample LispVal ,它给出了错误“预期的类型,但 'MonadSample LispVal' 具有类型约束”我查找了错误,但找不到将这个 Monad 固化为可以的类型的方法被我的表达所使用

解决方法

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

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

快速浏览一下您拥有的内容,但我建议您将对 sample 的调用“编译”到您现有的 IOFunc 构造函数中。例如你的例子

(sample (uniform upper lower))

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

这样您就不需要添加您决定添加为构造函数的每个未来 monad。它们都可以在 IO 中运行。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...