Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Typeclass shorthand

Tags:

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?

like image 855
rampion Avatar asked Nov 20 '08 18:11

rampion


People also ask

What is a Typeclass in Haskell?

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.

How do you use EQ in Haskell?

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

What is unit type in Haskell?

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

What is show in Haskell?

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.


4 Answers

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) 
like image 183
javawizard Avatar answered Oct 20 '22 06:10

javawizard


What you want is a class alias. There is a proposal to add it to Haskell at http://repetae.net/recent/out/classalias.html

like image 26
CesarB Avatar answered Oct 20 '22 06:10

CesarB


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

like image 37
ephemient Avatar answered Oct 20 '22 04:10

ephemient


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.

like image 45
Martijn Avatar answered Oct 20 '22 06:10

Martijn