[英]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.