Haskell概率单子的类型类问题

Posted

技术标签:

【中文标题】Haskell概率单子的类型类问题【英文标题】:Typeclass Trouble with Haskell Probability Monad 【发布时间】:2014-11-12 06:21:16 【问题描述】:

我正在尝试在 haskell 中为离散随机变量编写一个 monad。类型类看起来像这样:

- LANGUAGE MultiParamTypeClasses, FlexibleInstances #-

class (Num w, Monad m) => MonadDiscrete w m where
  sample :: [(a, w)] -> m a

我想为此做两个实例。第一个就像 list monad:

newtype Discrete w a = Discrete [(a, w)]

instance (Num w) => Monad (Discrete w) where
  ...

instance (Num w) => MonadDiscrete w (Discrete w) where
  sample = Discrete

第二个是在 MonadRandom 中使用 PRNG:

instance (MonadRandom m) => MonadDiscrete Rational m where
  sample = fromList

但是,如果我尝试执行以下操作:

x :: (Num w, MonadDiscrete w m) => m String
x = sample [("x", 1)]

GHC 给我一个错误:

无法推断 (MonadDiscrete w0 m) 由 it' from the context (MonadDiscrete w m) bound by the inferred type forit' 的歧义检查引起:MonadDiscrete w m => m [Char] 在:7:1-17 类型变量w0' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance Num w => MonadDiscrete w (Discrete w) -- Defined at lib/Discrete.hs:64:10 instance Control.Monad.Random.Class.MonadRandom m => MonadDiscrete Rational m -- Defined at lib/Discrete.hs:58:10 When checking thatit' 具有推断类型`forall w (m :: * -> *)。 MonadDiscrete w m => m [字符]' 可能原因:推断类型不明确

我尝试了各种方法,包括添加 FunDeps 或使 w 成为关联类型,但都失败了。

【问题讨论】:

你能修复你的代码示例吗? MonadDiscrete (Discrete w) 的实例需要另一个类型参数...也就是说,您是否尝试过消除 x 中的常量 1 的歧义,例如:(1::Int) 答案将是FunctionalDependencies 【参考方案1】:

问题在于声明

class (Num w, Monad m) => MonadDiscrete w m where
    sample :: [(a, w)] -> m a

没有表示对于给定的离散概率单子,表示权重的类型选择是固定的。例如,在您的Discrete 示例中,对于w 的任何选择,Discrete w monad 只能处理w 类型的概率权重。

因为这在上面的声明中没有表达出来,所以可以定义两个MonadDiscrete实例,它们只在w的选择上不同,而在m的选择上没有区别。这表示 在sample的定义中,MonadDiscrete实例不能单独从m解析。

解决这个问题的正确方法是使用函数依赖或MonadDiscrete相关类型族来表达m的选择唯一决定w的选择这一事实。下面是一个使用函数依赖的例子:

-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-

class (Num w, Monad m) => MonadDiscrete w m | m -> w where
    sample :: [(a, w)] -> m a

newtype Discrete w a = Discrete [(a, w)]

instance (Num w) => Monad (Discrete w)
instance (Num w) => MonadDiscrete w (Discrete w)

x :: (Num w, MonadDiscrete w m) => m String
x = sample [("x", 1)]

因为m决定了w,所以即使w没有出现在x的类型中,它仍然可以唯一解析,因此可以选择正确的MonadDiscrete实例。

这是使用类型族的相同解决方案:

-# LANGUAGE TypeFamilies, FlexibleContexts #-

class (Num (Weight m), Monad m) => MonadDiscrete m where
    type Weight m :: *
    sample :: [(a, Weight m)] -> m a

newtype Discrete w a = Discrete [(a, w)]

instance (Num w) => Monad (Discrete w)

instance (Num w) => MonadDiscrete (Discrete w) where
    type Weight (Discrete w) = w
    sample = undefined

x :: (MonadDiscrete m) => m String
x = sample [("x", 1)]

这个感觉有点不同,因为这里没有说明依赖关系,我们只是说MonadDiscrete 实例的一部分是说明什么类型代表Weights。

【讨论】:

请您给出一个使用类型族的等效公式? 完成;但我觉得fundep 更接近弗拉德的原始代码。 您没有包含第二个 (MonadRandom m) 实例。添加这会导致 ghc 抱怨:“MonadDiscrete Rational m 的重叠实例”。类型族也会发生类似的错误。有没有办法解决这个问题? 考虑当有人为Discrete 添加MonadRandom 实例时会发生什么...查看Control.Applicative.WrappedMonad 以了解此类问题的解决方法。 我认为问题在于,实际上我想说的是“如果它还不是 MonadDiscrete,而是 MonadRandom,那么使用它使其成为 MonadDiscrete”,但是这个在 Haskell 中并不能真正表达。这个问题导致我阅读了关于类型类的各种内容。我已经发现了这些“ConstraintKinds”,但还没有很好地了解它们以确定它们是否适用于这里(iirc 它们允许你做一些你以前无法做到的事情,比如设置一个 Monad)。 【参考方案2】:

我想我已经发现我实际上并不想要

instance (MonadRandom m) => MonadDiscrete Rational m where
  sample = fromList

但是:

instance RandomGen g => MonadDiscrete Rational (Rand g) where
  sample = fromList

因为我的目标是使用特定的现有实现,而不是所有可能的 MonadRandom 实例。

【讨论】:

以上是关于Haskell概率单子的类型类问题的主要内容,如果未能解决你的问题,请参考以下文章

Haskell 中有啥有趣的交换单子吗?

如何在 Haskell 中构建一个不确定的状态单子?

Haskell:类型类问题

Haskell 可能永远都不会变成主流

为啥 Haskell 没有在函数签名中推断数据类型的类型类?

Haskell 的类型类和 Go 的接口