2017-02-06 3 views
1

Я пытаюсь создать аа системы, чтобы получить символические функции, и у меня есть проблема:Создание класса типов, который возвращает другой экземпляр этого класса типов в 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) 

Мой вопрос в том, почему проблемы моего экземпляра проблематичны? Производная в каждом всегда разрешается в экземпляр Exp, который требуется типом constrain derivative, поэтому почему он не может соответствовать типу?

+3

Первая очевидная проблема заключается в том, что у вас есть тип переменной 'd' вместо' 'Double' в производной :: (Exp г) => e -> d'. – Alec

+2

Я рекомендую вам не использовать класс типа здесь - у него нет никакой цели. Вместо этого сделайте себе тип данных 'Exp':' data Exp = Number Double | Variable Char | Exp: * Exp | Exp: + Exp', а затем определите 'производный :: Char -> Exp -> Exp', где первый аргумент - это переменная, по отношению к которой вы получаете. – Alec

+0

Проблема в том, что вы хотите вернуть _some_ тип 'd', но ваша декларация объявляет' производную' возвращает _any_ 'd', независимо от того, что может выбрать _caller_. Таким образом, вы использовали универсальную количественную оценку вместо экзистенциальной квантификации. У Хаскелла нет экзистенциальности. Их можно определить, например. с пользовательским GADT, но они выглядят неправильно для этого - Alec above предлагает гораздо более простой вариант. – chi

ответ

5

Когда вы пишете

class Exp e where 
    derivative :: (Exp d) => e -> d 

вы заявляете, что любой тип e, который находится в Exp классе должен иметь функцию derivative :: e -> d. Обратите внимание, что e здесь очень специфический класс, но d только упоминается в Exp. Это почти произвольно типа. Таким образом, вы пытаетесь определить функцию, которая, учитывая аргумент, возвращает значение произвольного типа, принадлежащего Exp

Выбора d остаются компилятор в зависимости от контекста, как with fromInteger. Таким образом, вы не говоря «для каждого e есть d принадлежности к Exp таким образом, что derivative вернется d», вы говорите, что «для каждого eвсехd, принадлежащие Exp таковы, что derivative вернутся d». Если вы хотите сказать первый, вам, вероятно, придется использовать многопараметризированные классы и функциональные зависимости (чтобы указать, что тип вывода однозначно определяется типом ввода).

Если заменить e с каким-то определенным типом, вы пытаетесь осуществить следующее:

derivative :: Exp d => Double -> d 
derivative = (0::Double) 

, который не то, что вы можете сделать, потому что не все Exp s двойники. Скажем, Operator Double Double (который находится в Exp) явно не Double.Еще более искусственный пример с той же проблемой:

derivative :: Double -> a 
derivative = (0::Double) 
1

Есть способы достижения желаемого поведения, один использует 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, есть две ошибки в реализации:

  1. попытка построить бесконечный тип
  2. 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))