Search code examples
haskellgenericstypesautomata

Type Variables in type classes


I have a strange question in relation to type classes. So you can define a basic type class like this:

class Property x where
    checkThing :: x -> Int -> Bool
    transformThing :: x -> x

If you want to have a type class with multiple parameters you can enable:

{-# LANGUAGE MultiParamTypeClasses #-}

This will let you do things like:

class Property x y where
    checkThing :: x -> Int -> Bool
    transformThing :: x -> y -> Int

Here is my problem: So imagine I wanted to write a type class for automata (The kind that accept a language). I would write a type class that looked something like this:

class Automata machine where
    isDeterministic :: machine -> Bool
    acceptsInput :: machine -> String -> Bool

A automata takes in an input and decides if that input is part of a language. The above class would work for this. But wait this is limited to lists of characters (String) what if I want to generalize by Automata? Well I could add another variable to my class definition:

class Automata machine alphabet where
    isDeterministic :: machine -> Bool
    acceptsInput :: machine -> [alphabet] -> Bool

Hmm that is okay. But alphabet may have no direct relation to machine. Im in luck though! I can enable:

{-# LANGUAGE FunctionalDependencies #-}

And force language to depend on machine

class Automata machine alphabet | machine -> alphabet where

Ok now when I make an instance of Automata I can require alphabet to be related to the machine. For example:

instance Automata (FSM alphabet) alphabet where

works, and this correctly

instance Automata (FSM alphabet) othertypevariable where

gives an error. This is ok but not very generic. For example I have to define an instance for every type of automata, and every type of alphabet they can take. Thats horrible. In addition the functional dependencies does not actually force a relation. You can write:

 instance Automata (FSM alphabet) Int where

without a compiler error. Here is what would be ideal.

class Automata (machine alphabet) where
    isDeterministic :: machine alphabet -> Bool
    acceptsInput :: machine alphabet -> [alphabet] -> Bool

If I could specify a particular type parameter on the data instances are being defined for. For example the data that automata could be defined for would look like:

data FSM alphabet = FSM [alphabet]

or something like that. This would also allow defining single generic instances such as:

instance Automata (FSM alphabet) where

These examples are a simplified version I am trying to do but a solution for this problem would solve my problem. How could I go about expressing something this? Can I bend type classes to my will? Language extensions are acceptable.


Solution

  • Haskell type classes can abstract over arbitrary kinds. This means parameters o type classes can themselves have parameters. Familiar examples include Functor and Monad, that accept arguments like [] or Maybe. Here's a way to write write it with a type of *→* kind:

    class Automata machine where
        isDeterministic :: machine alphabet -> Bool
        acceptsInput :: machine alphabet -> [alphabet] -> Bool
    
    data FSM alphabet = FSM [alphabet] -- just an example, you need more 
                                       -- stuff to define a real FSM...
    
    instance Automata FSM where 
        ...
    

    Using {-# LANGUAGE KindSignatures #-} the kind of machine can be made explicit:

    class Automata (machine :: * -> *) where