Использовать функции, определенные в классе
Я пишу программу для представления конечных групп и простых операций над их элементами. Я написал большую часть необходимой мне функциональности, например, инверсные элементы, генераторы, проверку подгруппы и т. Д.
К сожалению, все мои функции нуждаются в элементе идентификации, настройке и работе. Я хочу определить соответствующий экземпляр класса и использовать его вместо этого трио. И тут начинается моя проблема, я не совсем знаю, как ее решить, и все, что я могу найти в Интернете, содержит только примеры определений, без примеров использования.
В конце концов я хотел бы работать с группами 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]
так что он принадлежит к той же группе.