有一个类,我想为其定义一个实例.它看起来像这样:
data MyValue a = MyValue a class TestClass a where funcOne:: (Real b) => a b -> a b funcTwo:: (Real b) => a b -> a b -> a b instance TestClass MyValue where funcOne (MyValue x) = MyValue (x*pi) funcTwo (MyValue x) (MyValue y) = MyValue (x*y)
我收到以下错误:
Could not deduce (Floating b) arising from a use of `pi' from the context: Real b
我理解错误,但我不知道如何解决它.
我无法更改(Real b)
to,(Floating b)
因为其他实例也应该使用Integral类型.但仅有MyValue
意义Floating
.有可能告诉编译器,instance TestClass MyValue
只能使用Floating
?
如果不是,那么如何将结果转换x*pi
回Real
与x
参数相同的?如果类型是例如Integral,会发生什么并不重要,因为MyValue
在这种情况下没有意义
您可以实现此目的,但您需要修改该数据类型或类.
如果MyValue
使用Floating特别有意义,那么将该约束烘焙到其构造函数中可能是有意义的.
{-# LANGUAGE GADTs #-} data MyValue :: * -> * where MyValue :: Floating a => a -> MyValue a
这保证任何功能接受MyValue a
其a
实际上是一个Floating
实例,因此
funcOne (MyValue x) = MyValue $ x*pi
然后会工作.
如果这是一个共同的主题,需要对包含的类型进行特定约束,那么您可以(而不是总是要求Real
)使约束依赖于实例:
{-# LANGUAGE TypeFamilies, ConstraintKinds #-} import GHC.Exts (Constraint) class TestClass a where type Testable a b :: Constraint type Testable a b = Real b -- default constraint funcOne:: Testable b => a b -> a b funcTwo:: Testable b => a b -> a b -> a b instance TestClass MyValue where type Testable MyValue b = Floating b funcOne (MyValue x) = MyValue $ x*pi ...
但是,如果您需要再次人为地约束参数,那么首先TestClass
处理参数化(* -> *
)类型可能不是正确的决定.为什么不简单地做
class TestClass q where funcOne :: q -> q funcTwo :: q -> q -> q instance Floating a => TestClass (MyValue a) where funcOne (MyValue x) = MyValue $ x*pi funcTwo (MyValue x) (MyValue y) = MyValue $ x*y
无论如何,这对我来说似乎更干净.如果某些方法确实需要访问包含的类型,那么使用相关类型系列也可以使用此方法:
class TestClass q where type ToTest q :: * ... instance Floating a => TestClass (MyValue a) where type ToTest (MyValue a) = a ...