[英]Creating a typeclass that returns another instance of that typeclass in haskell
我正在尝试创建一个系统以导出符号函数,但我遇到了一个问题:
我有一个表达式的类型类Exp
,它定义了一个派生函数:
class Exp e where
derivative :: (Exp d) => e -> d
我希望该类具有一些实例:
data Operator a b = a :* b | a :+ b
instance (Exp a, Exp b) => Exp (Operator a b) where
derivative (f :* g) = ((derivative f) :* g) :+ (f :* (derivative g)) --The derivative of the multiplication of two expressions
derivative (f :+ g) = derivative f :+ derivative g --The derivative of the addition of two expressions
instance Exp Double where
derivative a = (0 :: Double) --The derivative of a constant value is 0
instance Exp Char where
derivative c = (1 :: Double) --The derivative of just a variable is one
我用ghci编译的结果是:
math.hs:19:21: error:
• Couldn't match expected type ‘d’ with actual type ‘Double’
‘d’ is a rigid type variable bound by
the type signature for:
derivative :: forall d. Exp d => Double -> d
at math.hs:19:5
• In the expression: (0 :: Double)
In an equation for ‘derivative’: derivative a = (0 :: Double)
In the instance declaration for ‘Exp Double’
• Relevant bindings include
derivative :: Double -> d (bound at math.hs:19:5)
math.hs:22:21: error:
• Couldn't match expected type ‘d’ with actual type ‘Double’
‘d’ is a rigid type variable bound by
the type signature for:
derivative :: forall d. Exp d => Char -> d
at math.hs:22:5
• In the expression: (1 :: Double)
In an equation for ‘derivative’: derivative c = (1 :: Double)
In the instance declaration for ‘Exp Char’
• Relevant bindings include
derivative :: Char -> d (bound at math.hs:22:5)
math.hs:28:27: error:
• Couldn't match expected type ‘d’
with actual type ‘Operator (Operator a0 b) (Operator a b0)’
‘d’ is a rigid type variable bound by
the type signature for:
derivative :: forall d. Exp d => Operator a b -> d
at math.hs:27:5
• In the expression: derivative f :+ derivative g
In an equation for ‘derivative’:
derivative (f :+ g) = derivative f :+ derivative g
In the instance declaration for ‘Exp (Operator a b)’
• Relevant bindings include
g :: b (bound at math.hs:28:22)
f :: a (bound at math.hs:28:17)
derivative :: Operator a b -> d (bound at math.hs:27:5)
我的问题是:为什么我的实例减速度有问题? 每个导数始终解析为derivative
类型约束所要求的Exp
实例,那么为什么它不能与类型匹配?
当你写
class Exp e where
derivative :: (Exp d) => e -> d
您声明Exp
类中的任何类型e
都应具有函数derivative :: e -> d
。 请注意,这里的e
是一个非常特定的类,但是d
只能说在Exp
。 这几乎是任意类型。 因此,您尝试定义一个函数,该函数在给定参数的情况下返回属于Exp
的任意类型的值
取决于上下文,将d
选择留给编译器,例如fromInteger
。 因此,您并不是说“对于每个e
都有一个属于Exp
的d
使得derivative
将返回d
”,而是说“对于每个e
所有 d
属于Exp
所有 d
都将使得derivative
将返回d
”。 如果要说前者,则可能必须使用多参数化的类和函数依赖项(以指定输出的类型由输入的类型唯一地确定)。
如果我们将e
替换为某些特定类型,则您正在尝试实现以下内容:
derivative :: Exp d => Double -> d
derivative = (0::Double)
您无法执行此操作,因为并非所有Exp
都是双精度。 说, Operator Double Double
(位于Exp
)显然不是Double
。 具有相同问题的更多人工示例:
derivative :: Double -> a
derivative = (0::Double)
有多种方法可以实现所需的行为,一种方法是使用FunctionalDependencies
和MultiParamTypeClasses
,另一种方法是使用TypeFamilies
,如下所示:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
class Exp e where
type ResExp e :: * -- type family of resulting expression
derivative :: (Exp (ResExp e)) => e -> ResExp e
instance Exp Double where
type ResExp Double = Double
derivative a = 0
instance Exp Char where
type ResExp Char = Double
derivative c = 1
但是,当涉及Operator
的实例时,实现中存在两个错误:
derivative (f :* g)
和derivative (f :+ g)
具有不同的返回类型。 但是,这是解决此问题的方法:
data Mult a b = a :* b
data Plus a b = a :+ b
instance (Exp a, Exp b, Exp (ResExp a), Exp (ResExp b)) => Exp (Plus a b) where
type ResExp (Plus a b) = (Plus (ResExp a) (ResExp b))
derivative (f :+ g) = derivative f :+ derivative g
instance (Exp a, Exp b, Exp (ResExp a), Exp (ResExp b)) => Exp (Mult a b) where
type ResExp (Mult a b) = Plus (Mult (ResExp a) b) (Mult a (ResExp b))
derivative (f :* g) = ((derivative f) :* g) :+ (f :* (derivative g))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.