I want to write a class like this:
class C c where
op :: c -> c -> Bool
class A b => B b where
func :: C c => b -> c -- ^ type 'c' is random(forall).
func2 :: b -> b -> Bool
func2 x y = func b `op` func c
Here, c
is a type restricted by C
and this restriction will be used in func2.
But this cannot be compiler. Type c
is not a real type. I try to add forall
or using TypeFamilies
, but none of them can do this. TypeFamilies
looks good, but it cannot use with restriction in funcion definition like C c => b -> c
or `type X x :: C * => *.
Must I use (A b, C c) => B b c
to define this class? I have another class using with B like B b => D d b
. If adding a param for class B, the D class needs one more param as well. In fact, Seq a
will be used with class D
, which cannot match D d b
.
EDIT: one more description.
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module Main where
type Ta = (Integer, Integer)
newtype Tb t = Tb { tb :: [t] } deriving Show
class Eq a => A a where
a1f :: Ord b => a -> b
a2f :: a -> a -> Bool
a2f x y = a1f x >= a1f y
instance A Ta where
a1f (_, y) = y
class A a => B b a where
op :: b a -> b a
instance B Tb Ta where
op x = x
main :: IO ()
main = putStrLn $ show $ op $ (Tb [(1, 1)] :: Tb Ta)
Compiler will complain with the line a2f :: b -> Bool
:
• Could not deduce (Ord a0) arising from a use of ‘>=’
from the context: A a
bound by the class declaration for ‘A’ at test.hs:10:15
The type variable ‘a0’ is ambiguous
These potential instances exist:
instance Ord Ordering -- Defined in ‘GHC.Classes’
instance Ord Integer
-- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
instance Ord a => Ord (Maybe a) -- Defined in ‘GHC.Maybe’
...plus 22 others
...plus four instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: a1f x >= a1f y
In an equation for ‘a2f’: a2f x y = a1f x >= a1f y
EDIT2: Use type families
...
class Eq a => A a where
type AT a :: *
a1f :: Ord (AT a) => a -> AT a
a2f :: a -> a -> Bool
a2f x y = a1f x >= a2f y
instance A Ta where
type AT Ta = Integer
a1f (_, y) = y
...
It will show error with:
• Could not deduce (Ord (AT a)) arising from a use of ‘>=’
from the context: A a
bound by the class declaration for ‘A’ at test.hs:10:15
• In the expression: a1f x >= a1f y
In an equation for ‘a2f’: a2f x y = a1f x >= a1f y
The smallest fix to your type families code that lets it compile is to move the demand for an Ord
constraint from where it is produced to where it is consumed:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ConstrainedClassMethods #-}
type Ta = (Integer, Integer)
class Eq a => A a where
type AT a :: *
a1f :: a -> AT a
a2f :: Ord (AT a) => a -> a -> Bool
a2f x y = a1f x >= a1f y
instance A Ta where
type AT Ta = Integer
a1f (_, y) = y
If you'd like to only demand Ord (AT a)
when the default implementation is used, you can use DefaultSignatures
(and eliminate ConstrainedClassMethods
):
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
type Ta = (Integer, Integer)
class Eq a => A a where
type AT a :: *
a1f :: a -> AT a
a2f :: a -> a -> Bool
default a2f :: Ord (AT a) => a -> a -> Bool
a2f x y = a1f x >= a1f y
instance A Ta where
type AT Ta = Integer
a1f (_, y) = y
However, this typeclass structure is exceeding strange and unidiomatic. (Some red flags it raises as I read it: What is that Eq
constraint doing there? Why is there a class with just one instance? Why is a2f
inside the class instead of outside? Why isn't a1f
simply a non-class-polymorphic function? Why should we believe there is just one canonical selection function for each type?)
I'd like to reiterate that you should tell us more about what goal you are trying to achieve with this, rather than talking about your proposed typeclasses for achieving that goal. Much about this architecture screams "beginner trying to use typeclasses the way OO languages use classes", which is going to be an ongoing source of impedance mismatches and papercuts for you. I strongly suspect you simply shouldn't be defining a new typeclass at all.