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