I'm writing a program for representation of finite groups and simple operations on their elements. I wrote most of the functionality which I need, for example inverse elements, generators, checking if subset is subgroup etc.
Unfortunately all my functions need identity element, set and operation. I want to define the appropriate instance of the class and use it instead of this trio. And here begins my problem, I don't quite know how to go about it and all I can find on the internet contains only examples of definitions, without usage examples.
Eventually I would like to work with groups Sn
, so far I've tested my code on (Zn +
).
I wrote this:
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)]
And it works fine for the function op
, for example:
*Main> (Elm 1) `op` (Elm 0)
Elm 1
Unfortunately, I cant use identity
and set
. How do I use it?
I also can't figure out how to refactor an existing function to use my type class.
For example I have this:
cyclicGenerators :: (Eq a) => a -> [a] -> (a -> a -> a) -> [a]
and it works fine, but I want to have something like this:
cyclicGenerators :: (Eq a) => FiniteGroup a -> [a]
You must use them in a context which defines
a
unambiguously. E.g. providing an explicit type annotationPassing them to some function requiring a
Z2
also works. As long as the compiler can figure out which finite group you are talking about, you will be fine. Otherwise, you'll get an "ambiguous ..." error.Try e.g.
Above the compiler "knows" that
set
refers to the finite groupa
, since we must return an[a]
andfilter
doesn't change the type of the input list, hence theset
must be of type[a]
as well. Here type inference works very well in propagating types so that the correct finite group instance is selected.Edit: the OP proposes the code
which triggers an ambiguity error. Indeed, the last
set
is not forced to belong to the same group -- it would make sense to compare the group order of another group to the order ofx
. E.g.groupOrder (set :: [Z5]) == elemOrder (identity :: [Z2])
would type check, since both are integers.So we need to state that the type of that
set
is[a]
. The plain Haskell way iswhere
asTypeOf
is a library function which simply returns its first argument, but requires it shares its type with the second argument. It is defined in the libraries as:Alternatively, we can exploit a very commonly used extension of GHC Haskell. First, we need to enable it by adding
at the top of the module file. Then, we can simply write
where we explicit state that
set
is of type[a]
, so it belongs to the same group.