Использовать функции, определенные в классе

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

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

В конце концов я хотел бы работать с группами Snпока что я проверил мой код (Zn +).

Я написал это:

data Z2 = Elm Int deriving (Show, Eq)
z2 :: Int -> Z2
z2 a = Elm (a `mod` 2)

class FiniteGroup a where
    identity :: a
    op :: a -> a -> a -- operation
    set :: [a]

instance FiniteGroup Z2 where
    identity = (Elm 0)
    op (Elm x) (Elm y) = z2 (x+y)
    set = [(Elm 0), (Elm 1)]

И это прекрасно работает для функции op, например:

*Main> (Elm 1) `op` (Elm 0)
Elm 1

К сожалению, я не могу использовать identity а также set, Как мне это использовать?

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

Например, у меня есть это:

cyclicGenerators :: (Eq a) => a -> [a] -> (a -> a -> a) -> [a]

и это прекрасно работает, но я хочу иметь что-то вроде этого:

cyclicGenerators :: (Eq a) => FiniteGroup a -> [a]

1 ответ

Решение

Я не могу использовать личность и установить, как это использовать?

Вы должны использовать их в контексте, который определяет a однозначно. Например, предоставление явной аннотации типа

set :: [Z2]
identity :: Z2

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

Как заменить подписи в функции и снова, как использовать идентичность и установить в функции, которую я уже определил.

Попробуйте например

cyclicGenerators :: (Eq a, FiniteGroup a) => [a]
cyclicGenerators = filter isGenerator set
  where isGenerator x = ...  -- a Boolean telling whether it's a generator

Выше компилятор "знает", что set относится к конечной группе a, поскольку мы должны вернуть [a] а также filter не меняет тип списка ввода, следовательно, set должен быть типа [a] также. Здесь вывод типов работает очень хорошо в распространяющихся типах, так что выбран правильный конечный экземпляр группы.


Изменить: ОП предлагает код

cyclicGenerators :: (FiniteGroup a) => [a]
cyclicGenerators = filter isGenerator set
        where isGenerator = (\x -> groupOrder set == elemOrder x)

который вызывает ошибку неоднозначности. Действительно, последнее set не обязательно принадлежать к той же группе - имеет смысл сравнивать групповой порядок другой группы с порядком x, Например groupOrder (set :: [Z5]) == elemOrder (identity :: [Z2]) будет проверка типа, так как оба являются целыми числами.

Таким образом, мы должны заявить, что тип этого set является [a], Простой способ Haskell является

 where isGenerator = (\x -> groupOrder (set `asTypeOf` [x]) == elemOrder x)

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

asTypeOf :: t -> t -> t
asTypeOf x y = x

В качестве альтернативы, мы можем использовать очень распространенное расширение GHC Haskell. Во-первых, нам нужно включить его, добавив

{-# LANGUAGE ScopedTypeVariables #-}

в верхней части файла модуля. Тогда мы можем просто написать

cyclicGenerators :: forall a . (FiniteGroup a) => [a]
                 -- ^^^^^^^^^^ added
cyclicGenerators = filter isGenerator set
        where isGenerator = (\x -> groupOrder (set :: [a]) == elemOrder x)

где мы явно заявляем, что set имеет тип [a]так что он принадлежит к той же группе.

Другие вопросы по тегам