haskellfunctional-programmingfinite-group-theory

use the functions defined in the class


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]

Solution

  • I cant use the identity and set, how to use it?

    You must use them in a context which defines a unambiguously. E.g. providing an explicit type annotation

    set :: [Z2]
    identity :: Z2
    

    Passing 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.

    How to replace signatures in function and again how to use identity and set in function which I've already defined.

    Try e.g.

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

    Above the compiler "knows" that set refers to the finite group a, since we must return an [a] and filter doesn't change the type of the input list, hence the set 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

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

    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 of x. 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 is

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

    where 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:

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

    Alternatively, we can exploit a very commonly used extension of GHC Haskell. First, we need to enable it by adding

    {-# LANGUAGE ScopedTypeVariables #-}
    

    at the top of the module file. Then, we can simply write

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

    where we explicit state that set is of type [a], so it belongs to the same group.