我有一种情况,我有一些类似的东西newtypes
都需要是Random
,Arbitrary
和许多其他东西的实例。它们都需要函数、、、等的相同自定义实现。所以我将所有这些实现放在一个类中。randomR
random
arbitrary
这是一个简化的示例,仅处理Random
.
{-# LANGUAGE ConstrainedClassMethods #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
import qualified System.Random as SR
-- Numbers that are restricted to a narrower range
class Narrow t where
type BaseType t
-- Unsafe constructor for the instance type
bless :: BaseType t -> t
-- Safe constructor for the instance type
narrow :: (Ord t, Bounded t) => BaseType t -> t
narrow x | x' < (minBound :: t) = error "too small"
| x' > (maxBound :: t) = error "too big"
| otherwise = x'
where x' = bless x :: t
-- Deconstructor for the instance type
wide :: t -> BaseType t
-- Random
randomR
:: (Ord t, Bounded t, SR.Random (BaseType t), SR.RandomGen g)
=> (t, t) -> g -> (t, g)
randomR (a, b) g = (narrow x, g')
where (x, g') = SR.randomR (wide a, wide b) g
random
:: (Ord t, Bounded t, SR.Random t, SR.RandomGen g)
=> g -> (t, g)
random = SR.randomR (minBound, maxBound)
这是我想要的其中一种类型的示例。
-- | A number on the unit interval
newtype UIDouble = UIDouble Double
deriving (Eq, Ord)
instance Bounded UIDouble where
minBound = UIDouble 0
maxBound = UIDouble 1
instance Narrow UIDouble where
type BaseType UIDouble = Double
bless = UIDouble
wide (UIDouble x) = x
我希望这是一个实例Random
。理想情况下,我想写一些类似的东西:
deriving ?strategy? instance SR.Random UIDouble
并让编译器知道使用中定义的方法Narrow
来实现Random
. 但相反,我必须写
instance SR.Random UIDouble where
randomR = randomR
random = random
对几种方法执行此操作不是问题,但是对我的每种类型执行Num
, Fractional
, RealFrac
, Floating
,Serialize
等操作有点乏味。
我探索过的另一种方法是写
instance (Narrow t) => SR.Random t where
randomR = randomR
random = random
因为我只需要为班级写一次,而不是为每种类型重复一次。但这导致UndecidableInstances
我理解是不好的。我可以做到这一点TemplateHaskell
,我敢肯定。但我想知道是否有一些花哨的语言编译指示或类型级编程魔法可以简化这一点?