1

我正在尝试定义默认方法实现,但前提是类的类型变量派生某些其他类。

我曾尝试使用=>(我什至正确使用它吗?)创建依赖于类型的实例,但我得到一个“重复的实例声明错误”:(https://repl.it/@solly_ucko/Distributions

{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, InstanceSigs #-}

import Data.Int
import Data.Ratio
import Data.Set
import System.Random

duplicate :: a -> (a, a)
duplicate a = (a, a)

listRange :: Enum a => a -> a -> [a]
listRange a b = [a..b]

class Fractional w => Distribution d v w where
    probability :: d v w -> v -> w

    probabilityOfRange :: Ord v => d v w -> v -> v -> w

    ranges :: (Ord v) => d v w -> Set (v, v)
    ranges = (Data.Set.map duplicate) . values

    sample :: RandomGen g => d v w -> g -> (v, g)
    --sample d g = (scanl1 (+) $ flip Prelude.map $ probability d, g) -- Will need to implement some sort of binary tree, most likely.

    sampleIO :: d v w -> IO v
    sampleIO = getStdRandom . sample

    values :: d v w -> Set v

instance (Ord v, Fractional w) => Distribution d v w where
    probability d v = probabilityOfRange d v v

instance Enum v => Distribution d v w where
    probabilityOfRange d v1 v2 = sum $ Prelude.map (probability d) [v1..v2]

instance (Enum v, Ord v) => Distribution d v w where
    values = fromList . (concatMap $ uncurry listRange) . toList . ranges

然后当我尝试添加真实实例(并注释掉我之前创建的一些“实例”以便编译器可以达到那个点)时,它给了我一个关于冲突实例的错误。

data Empty v w = Empty

instance Distribution Empty v (Ratio Int8) where
    sample _ g = (undefined, g)
    sampleIO _ = return undefined
    probabilityOfRange _ _ _ = 0
    values _ = empty

data Singleton v w = Singleton v

instance Distribution Singleton v Integer where
    sample (Singleton v) g = (v, g)
    sampleIO (Singleton v) = return v
    probabilityOfRange (Singleton v1) v2 v3
        | v2 <= v1 && v1 <= v3 = 1
        | otherwise        = 0

data Uniform v w = Uniform (Set v)

澄清一下,我的目标是 for probabilityand valuesto be defined for all Distributions,以及 for probabilityOfRangeto be defined for all Distributionswith values deriving Ord。我还希望在满足其他约束时提供默认值,因为没有它们,合理的默认值(基于其他方法)是不可能的。

4

1 回答 1

4

尝试使用以下方法为单个方法指定默认值:

instance (Ord v, Fractional w) => Distribution d v w where
    probability d v = probabilityOfRange d v v

不会工作。Haskell 实例不会“累积”。对于给定的三元组 types d v w,最多instance Distribution d v w适用一个子句。(如果由于“重叠”实例而可能应用多个子句,则存在选择“最佳”匹配的机制,但没有直接机制来组合来自多个实例子句的方法。)

一般来说,如果你有一个类方法:

class Distribution d v w where
    probability :: d v w -> v w

并且您想定义一个具有更严格类型签名的默认方法(即,对某些类型有约束):

probability :: (Ord v) => d v w -> v -> w
probability d v = probabilityOfRange d v v

有两种方法。

首先是利用DefaultSignatures扩展。这允许您将方法的类型签名与默认方法的(可能更具限制性)类型签名分开。语法是:

class Fractional w => Distribution d v w where
    probability :: d v w -> v -> w
    default probability :: (Ord v) => d v w -> v -> w
    probability d v = probabilityOfRange d v v
    ...

这里需要注意的是,如果你定义了一个覆盖默认方法的实例,那么它必须满足约束Ord v,否则它不会进行类型检查。

如果您想更好地控制何时使用默认方法,那么通常的方法是将默认定义分离到一个单独的函数中,该函数必须显式包含在希望使用它的实例中。所以,你有:

class Fractional w => Distribution d v w where
    probability :: d v w -> v -> w

probabilityDefault :: (Distribution d v w, Ord v) => d v w -> v -> w
probabilityDefault d v = probabilityOfRange d v v

并且希望使用默认值的实例必须明确地这样做:

instance Distribution Whatever Int w where
    probability = probabilityDefault
于 2019-04-05T04:09:55.223 回答