有一个类,我想为其定义一个实例.它看起来像这样:
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)
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
Could not deduce (Floating b) arising from a use of `pi'
from the context: Real b
Run Code Online (Sandbox Code Playgroud)
我理解错误,但我不知道如何解决它.
我无法更改(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
Run Code Online (Sandbox Code Playgroud)
这保证任何功能接受MyValue a其a实际上是一个Floating实例,因此
funcOne (MyValue x) = MyValue $ x*pi
Run Code Online (Sandbox Code Playgroud)
然后会工作.
如果这是一个共同的主题,需要对包含的类型进行特定约束,那么您可以(而不是总是要求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
...
Run Code Online (Sandbox Code Playgroud)但是,如果您需要再次人为地约束参数,那么首先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
Run Code Online (Sandbox Code Playgroud)
无论如何,这对我来说似乎更干净.如果某些方法确实需要访问包含的类型,那么使用相关类型系列也可以使用此方法:
class TestClass q where
type ToTest q :: *
...
instance Floating a => TestClass (MyValue a) where
type ToTest (MyValue a) = a
...
Run Code Online (Sandbox Code Playgroud)