我可以在 Haskell 中的 type/data 构造函数上约束参数多态类型吗?
Can I constrain the parametric polymorphic type on type/data constructor in Haskell?
我有一个参数化类型,我想将其限制为数字类型,更具体地说是 Fractional
,例如:
data Rating a = (Fractional a) => Score a | Unscored deriving (Show, Eq)
以便 API 的用户可以定义他们可能使用的非整数类型(Float
或 Double
?),但是内部 API 代码我写的仍然可以对数字类型执行算术运算。我不希望它是一个整数,因为我的 "internal operations" 的结果可能不是整数,我的理解是使用 Fractional
会导致更准确的结果。
编译以上内容(至少在 GHCI 中)会出现以下错误:
Data constructor `Score' has existential type variables, a context, or a specialised result type
Score :: forall a. Fractional a => a -> Rating a
(Use ExistentialQuantification or GADTs to allow this)
In the definition of data constructor `Score'
In the data declaration for `Rating'
这对我来说意味着我正在做一些我可能不想继续尝试的事情;即我的设计是垃圾。
我想我想在此 API 中表达以下内容:"when you use a Rating type, its parameter must be a subclass of Fractional
so I can perform accurate arithmetic on it"。我怎样才能做到这一点?还是我偏离了目标 and/or 过度设计?
您不应该将 Fractional
约束放在数据类型上,而应该放在使用它的函数上。所以
data Rating a = Score a | Unscored deriving (Show, Eq)
makeSomeRating :: Fractional a => a -> Rating a
makeSomeRating x = Score (x / 2) -- can use Fractional functions here
doSomethingElseWithRating :: Fractional a => Rating a -> Something
如错误所述,您可以使用 GADTs
:
{-# LANGUAGE GADTs, StandaloneDeriving #-}
data Rating a where
Score :: (Fractional a) => a -> Rating a
Unscored :: Rating a
deriving instance (Show a) => Show (Rating a)
deriving instance (Eq a) => Eq (Rating a)
-- No need for a context here!
halfScore :: Rating a -> Rating a
halfScore (Score a) = Score (a / 2)
halfScore Unscored = Unscored
StandaloneDeriving
是必需的,因为 GHC 无法为 GADT 推导出 Show
和 Eq
。
我有一个参数化类型,我想将其限制为数字类型,更具体地说是 Fractional
,例如:
data Rating a = (Fractional a) => Score a | Unscored deriving (Show, Eq)
以便 API 的用户可以定义他们可能使用的非整数类型(Float
或 Double
?),但是内部 API 代码我写的仍然可以对数字类型执行算术运算。我不希望它是一个整数,因为我的 "internal operations" 的结果可能不是整数,我的理解是使用 Fractional
会导致更准确的结果。
编译以上内容(至少在 GHCI 中)会出现以下错误:
Data constructor `Score' has existential type variables, a context, or a specialised result type
Score :: forall a. Fractional a => a -> Rating a
(Use ExistentialQuantification or GADTs to allow this)
In the definition of data constructor `Score'
In the data declaration for `Rating'
这对我来说意味着我正在做一些我可能不想继续尝试的事情;即我的设计是垃圾。
我想我想在此 API 中表达以下内容:"when you use a Rating type, its parameter must be a subclass of Fractional
so I can perform accurate arithmetic on it"。我怎样才能做到这一点?还是我偏离了目标 and/or 过度设计?
您不应该将 Fractional
约束放在数据类型上,而应该放在使用它的函数上。所以
data Rating a = Score a | Unscored deriving (Show, Eq)
makeSomeRating :: Fractional a => a -> Rating a
makeSomeRating x = Score (x / 2) -- can use Fractional functions here
doSomethingElseWithRating :: Fractional a => Rating a -> Something
如错误所述,您可以使用 GADTs
:
{-# LANGUAGE GADTs, StandaloneDeriving #-}
data Rating a where
Score :: (Fractional a) => a -> Rating a
Unscored :: Rating a
deriving instance (Show a) => Show (Rating a)
deriving instance (Eq a) => Eq (Rating a)
-- No need for a context here!
halfScore :: Rating a -> Rating a
halfScore (Score a) = Score (a / 2)
halfScore Unscored = Unscored
StandaloneDeriving
是必需的,因为 GHC 无法为 GADT 推导出 Show
和 Eq
。