在Haskell中的多态类型族实例化

3

我正在尝试编写代码来模拟随机变量,我希望尽可能地保持多态性。这可能涉及到类型族的使用,而这对我来说是全新的。

这是我代码的简化版本:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}


data TrivialDist a = Trivial a

getVal :: TrivialDist a -> a
getVal (Trivial x) = x

class JointDist d a where
    toTrivialDist :: d a -> TrivialDist [a]

data TrivialJointDist a = TrivialJoint [a]

instance JointDist TrivialJointDist a where
    toTrivialDist :: TrivialJointDist a -> TrivialDist [a]
    toTrivialDist (TrivialJoint xs) = Trivial xs

class Simulable s a where
    type Samp s a :: *
    -- generates infinite stream of random samples from a distribution
    samples :: s a -> IO [Samp s a]
    -- generates a single random sample from a distribution
    sample :: s a -> IO (Samp s a)
    sample = fmap head . samples

instance Simulable TrivialDist a where
    type Samp TrivialDist a = a
    samples :: TrivialDist a -> IO [a]
    samples (Trivial x) = return $ repeat x

instance (JointDist d a) => Simulable d a where
    type Samp d a = [a]
    samples :: d a -> IO [[a]]
    samples = samples . toTrivialDist

当我将其加载到ghci中时,我会收到以下错误信息:
test.hs:30:10: error:
    Conflicting family instance declarations:
      Samp TrivialDist a = a -- Defined at test.hs:30:10
      Samp d a = [a] -- Defined at test.hs:40:10
   |
30 |     type Samp TrivialDist a = a
   |          ^^^^^^^^^^^^^^^^^^^^^^
Failed, 0 modules loaded.

这个问题似乎类似于在这里发现的问题(解释是GHC无法根据约束区分类型),但没有提出解决方案。如果我将最后一个实例声明更改为以下内容,则可以编译代码:
instance Simulable TrivialJointDist a where
    type Samp TrivialJointDist a = [a]
    samples :: TrivialJointDist a -> IO [[a]]
    samples = samples . toTrivialDist

然而,我将失去多态的优势,因为我需要为每个 JointDist d a 的子类型进行单独的实例声明。
非常感谢您的帮助!

1
正确的做法是,您必须为每个 JointDist 实例进行单独的实例声明。话虽如此,这里有几个代码异味,让我认为更简单的设计是值得的。也许如果您能回答这个问题,就可以给出更好的建议:是否存在一个 JointDist 实例,它不同构于 TrivialDist(如果有,它是什么样子)? - Daniel Wagner
或者你可能想查看一些为您提供随机变量的库。我更喜欢MonadBayes,它实现了一个概率单子,允许采样、条件和推断。 - C. Hammill
1个回答

0

经过一番折腾,我认为我已经找到了一个解决方案,它使用等式约束而不是显式类型族:

class Simulable s a b where
    -- generates infinite stream of random samples from a distribution
    samples :: s a -> IO [b]
    -- generates a single random sample from a distribution
    sample :: s a -> IO b
    sample = fmap head . samples

instance (b ~ a) => Simulable TrivialDist a b where
    samples :: TrivialDist a -> IO [a]
    samples (Trivial x) = return $ repeat x

instance {-# OVERLAPPABLE #-} (JointDist d a, b ~ [a]) => Simulable d a b where
    samples :: d a -> IO [[a]]
    samples = samples . toTrivialDist

{-# OVERLAPPABLE #-} pragma指示GHC允许TrivialDist a b实例与d a b实例重叠(否则会出现错误)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接