So, I have a pair of typeclasses that I'll be using a lot together, and I want to avoid specifying both each time. Basically, instead of putting
:: (Ord a, Fractional a, Ord b, Fractional b, ... Ord z, Fractional z) =>
at the beginning of all my type specifications, I'd rather put
:: (OrdFractional a, OrdFractional b, ... OrdFractional z)
So, my initial idea on how to do this was to just declare a new typeclass
module Example where
class (Fractional a, Ord a) => OrdFractional a
example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3)
But this didn't work as automagically as I wished it would:
% ghci
Prelude> :l Example.hs
Ok, modules loaded: Example.
Prelude Example> example (1::Float,3::Float) (2,2) (3,1)
<interactive>:1:0:
No instance for (OrdFractional Float)
arising from a use of `example' at <interactive>:1:0-39
Possible fix:
add an instance declaration for (OrdFractional Float)
In the expression: example (1 :: Float, 3 :: Float) (2, 2) (3, 1)
In the definition of `it':
it = example (1 :: Float, 3 :: Float) (2, 2) (3, 1)
Manually creating instances seems like a drag so, next, I thought I might try to automatically create instances:
module Example where
class OrdFractional a
instance (Fractional a, Ord a) => OrdFractional a
example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3)
But the compiler didn't like that:
ghc -c Example.hs
Example.hs:4:0:
Illegal instance declaration for `OrdFractional a'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are type *variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `OrdFractional a'
So is there a way I can do this?
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
The Eq typeclass provides an interface for testing for equality. Any type where it makes >sense to test for equality between two values of that type should be a member of the Eq >class. All standard Haskell types except for IO (the type for dealing with input and >output) and functions are a part of the Eq typeclass."
In Haskell, Rust, and Elm, the unit type is called () and its only value is also () , reflecting the 0-tuple interpretation. In ML descendants (including OCaml, Standard ML, and F#), the type is called unit but the value is written as () . In Scala, the unit type is called Unit and its only value is written as () .
The shows functions return a function that prepends the output String to an existing String . This allows constant-time concatenation of results using function composition.
With the ConstraintKinds extension introduced in GHC 7.4, constraints are now types of kind Constraint
, so you can use ordinary type synonyms to get what you want:
{-# LANGUAGE ConstraintKinds #-} type OrdFractional a = (Ord a, Fractional a)
What you want is a class alias. There is a proposal to add it to Haskell at http://repetae.net/recent/out/classalias.html
When the compiler says "Use -XFlexibleInstances
", you should try adding
{-# LANGUAGE FlexibleInstances #-}
to the top of your source (and go read the documentation to learn what it does, of course!).
In this specific case, this will make your code work:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
Flexible instances are required in order to enable the =>
context on the instance head, and undecidable instances are required because the compiler, when handling an OrdFractional a
context, can end adding Fractional a
and Ord a
to the context -- which doesn't directly help with finally determining a
, and under suitably horrible circumstances, typechecking may diverge; the compiler really doesn't like that. (You probably wouldn't like it if the compiler went on forever or ran out of memory, either.)
No.
Your solution of a superclass implying the other classes is the closest to what you want that is possible in Haskell. Even though that requires manual instances of that new class it is sometimes used, for example in the rewriting library.
As CesarB mentioned class aliases do what you want (and more), but they're just a proposal that's been around for years now and have never been implemented, probably because there are numerous problems with it. Instead, various other proposals have popped up, but none of those were implemented either. (For a list of those proposals, see this Haskellwiki page.) One of the projects at Hac5 was to modify the GHC to include a small subset of class aliases called context synonyms (which do exactly what you are asking for here and nothing more), but sadly it was never finished.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With