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 for
it' 的歧义检查引起: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 that
it' 具有推断类型`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
实例的一部分是说明什么类型代表Weight
s。
【讨论】:
请您给出一个使用类型族的等效公式? 完成;但我觉得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概率单子的类型类问题的主要内容,如果未能解决你的问题,请参考以下文章