Haskell 中类型变量相关的默认方法实现
Type-variable-dependent default method implementation in Haskell
我正在尝试定义默认方法实现,但前提是 class 的类型变量派生某些其他 classes。
我尝试使用 =>
创建依赖于类型的实例(我是否正确使用它?),但我得到了 "duplicate instance declaration error": (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
然后当我尝试添加真实实例(并注释掉我之前创建的一些 "instances" 以便编译器可以达到那个点)时,它给我一个关于实例冲突的错误。
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)
澄清一下,我的目标是为所有 Distributions
定义 probability
和 values
,并为所有 Distributions
定义 probabilityOfRange
] 的值派生 Ord
。我也希望在满足额外约束时提供默认值,因为没有它们,合理的默认值(基于其他方法)是不可能的。
尝试使用类似以下内容为单个方法指定默认值:
instance (Ord v, Fractional w) => Distribution d v w where
probability d v = probabilityOfRange d v v
行不通。 Haskell 个实例不会 "accumulate"。对于给定的三元组类型 d v w
,最多应用一个 instance Distribution d v w
子句。 (如果多个子句 可以 由于 "overlapping" 个实例而适用,则有选择 "best" 匹配的机制,但没有直接的机制来组合来自多个实例的方法条款。)
一般来说,如果您有 class 方法:
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
扩展。这允许您将方法的类型签名与 default 方法的(可能更严格)类型签名分开。语法是:
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
,否则它会获胜't typecheck.
如果您想更好地控制何时使用默认方法,那么通常的方法是将默认定义分离到一个单独的函数中,该函数必须显式包含在希望使用它的实例中。所以,你会:
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
我正在尝试定义默认方法实现,但前提是 class 的类型变量派生某些其他 classes。
我尝试使用 =>
创建依赖于类型的实例(我是否正确使用它?),但我得到了 "duplicate instance declaration error": (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
然后当我尝试添加真实实例(并注释掉我之前创建的一些 "instances" 以便编译器可以达到那个点)时,它给我一个关于实例冲突的错误。
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)
澄清一下,我的目标是为所有 Distributions
定义 probability
和 values
,并为所有 Distributions
定义 probabilityOfRange
] 的值派生 Ord
。我也希望在满足额外约束时提供默认值,因为没有它们,合理的默认值(基于其他方法)是不可能的。
尝试使用类似以下内容为单个方法指定默认值:
instance (Ord v, Fractional w) => Distribution d v w where
probability d v = probabilityOfRange d v v
行不通。 Haskell 个实例不会 "accumulate"。对于给定的三元组类型 d v w
,最多应用一个 instance Distribution d v w
子句。 (如果多个子句 可以 由于 "overlapping" 个实例而适用,则有选择 "best" 匹配的机制,但没有直接的机制来组合来自多个实例的方法条款。)
一般来说,如果您有 class 方法:
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
扩展。这允许您将方法的类型签名与 default 方法的(可能更严格)类型签名分开。语法是:
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
,否则它会获胜't typecheck.
如果您想更好地控制何时使用默认方法,那么通常的方法是将默认定义分离到一个单独的函数中,该函数必须显式包含在希望使用它的实例中。所以,你会:
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