Определение класса типа с функциями в зависимости от дополнительного типа - PullRequest
8 голосов
/ 16 декабря 2011

Все еще новичок в Haskell, я столкнулся со следующим:

Я пытаюсь определить некоторые классы типов для обобщения группы функций, которые используют гауссово исключение для решения линейных систем уравнений.

Для данной линейной системы

M x = k

тип a элементов m(i,j) \elem M может отличаться от типа b из x и k.Чтобы иметь возможность решить систему, a должен быть экземпляром Num, а b должен иметь операторы умножения / сложения с b, как показано ниже:

class MixedRing b where
    (.+.) :: b -> b -> b
    (.*.) :: (Num a) => b -> a -> b
    (./.) :: (Num a) => b -> a -> b

Теперь,даже в самой тривиальной реализации этих операторов я получу Could not deduce a ~ Int. a is a rigid type variable ошибок (давайте забудем о ./., для которого требуется Fractional)

data Wrap = W { get :: Int }
instance MixedRing Wrap where
    (.+.) w1 w2 = W $ (get w1) + (get w2)
    (.*.) w s   = W $ ((get w) * s)

Я прочитал несколько учебных пособий по классам типов, но яне могу найти указатель на то, что на самом деле идет не так.

Ответы [ 3 ]

10 голосов
/ 16 декабря 2011

Давайте посмотрим на тип реализации, который вам нужно будет предоставить для (.*.), чтобы Wrap стал экземпляром MixedRing. Подстановка Wrap для b в типе метода дает

(.*.) :: Num a => Wrap -> a -> Wrap

Поскольку Wrap изоморфно Int и не нужно думать о переносе и развертывании с Wrap и get, давайте сведем нашу цель к поиску реализации

(.*.) :: Num a => Int -> a -> Int

(Вы видите, что это не делает задачу легче или сложнее, не так ли?)

Теперь обратите внимание, что такая реализация должна иметь возможность работать со всеми типами a, которые находятся в классе типов Num. (Это то, что переменная типа в таком типе обозначает: универсальная квантификация.) Примечание: это не то же самое (на самом деле, это наоборот), когда говорят, что ваша реализация сама может выбирать, над чем a работать); тем не менее это то, что вы, похоже, предлагаете в своем вопросе: чтобы вашей реализации было разрешено выбрать Int в качестве выбора для a.

Теперь, когда вы хотите реализовать этот конкретный (.*.) в терминах (*) для значений типа Int, нам нужно что-то вида

n .*. s = n * f s

с

f :: Num a => a -> Int

Я не могу представить себе функцию, которая преобразует из произвольного Num -типа a в Int осмысленным образом. Поэтому я бы сказал, что нет никакого осмысленного способа сделать Int (и, следовательно, Wrap) экземпляром MixedRing; то есть не так, что экземпляр ведет себя так, как вы, вероятно, ожидаете.

6 голосов
/ 16 декабря 2011

Примерно так:

class (Num a) => MixedRing a b where
    (.+.) :: b -> b -> b
    (.*.) :: b -> a -> b
    (./.) :: b -> a -> b

Вам понадобится расширение MultiParamTypeClasses.

Кстати, мне кажется, что математическая структура, которую вы пытаетесьМодель действительно , модуль , а не кольцо.С переменными типа, приведенными выше, говорят, что b является a -модулем .

2 голосов
/ 16 декабря 2011

Ваша реализация недостаточно полиморфна.

Правило состоит в том, что если вы пишете a в определении класса, вы не можете использовать конкретный тип в экземпляре.Поскольку экземпляр должен соответствовать классу, а класс обещал принять любое значение a, равное Num.

Другими словами: переменная класса - это именно та переменная, для которой должен быть создан конкретный тип вопределение экземпляра.

Вы пробовали:

data Wrap a = W { get :: a }

Обратите внимание, что если Wrap a является экземпляром, вы все равно можете использовать его с функциями, которые принимают только Wrap Int.

...