I have been searching on documentations and wikis for Haskell's type constraints in function signatures. Unfortunately, I wasn't able to find a satisfactory answer.
At this time, I consider myself as a beginner, so I would like to ask your comprehension if you see misuse of technical terms or lack of advanced knowledge on this topic.
Consider a real example given by the function signature below:
count :: (Eq e, Num n) => e -> [e] -> n -> n
This function has the type constraints: Eq
and Num
, each one with exactly one variable, respectively: e
and n
. I could have provided a simpler signature with only one constraint too (e.g. foo :: Eq a => a -> Bool
).
But what about the following?
Consider the hypothetical examples. I thought of them trying to represent multiple variables that belong to the same type constraint.
-- As far as I tested, the examples below are incorrect
-- and do not reflect proper Haskell syntax
foo :: (Num a b c) => use the variables here
or
foo :: Num a, b, c => use the variables here
Can we specify more than one variable in the same constraint, like I tried above? Would I have to specify them individually?
foo :: Eq a, Eq b ... => use the variables here
Is there any case in which one would use multiple type variables of the same type, what impacts does this bring?
Edit - May 1, 2023: I accepted Ben’s answer because it contains a thorough explanation and is very detailed. I really found it to be clarifying and constructive as he complements the discussion, pointing out to Joseph’s answers, which is extremely relevant.
You can write a type synonym that lets you constrain a lot of types without having to repeat the constraint each time, like this:
import Data.Kind (Constraint)
type Three c t1 t2 t3 = (c t1, c t2, c t3) :: Constraint
allEqual :: Three Eq a b c => a -> a -> b -> b -> c -> c -> Bool
allEqual x1 x2 y1 y2 z1 z2 = x1 == x2 && y1 == y2 && z1 == z2
You could also define a type family that constrains every element of a list:
{-# Language DataKinds, TypeFamilies #-}
import Data.Kind (Constraint)
type
AllCls :: (k -> Constraint) -> ([k] -> Constraint)
type family
AllCls cls as where
AllCls cls '[] = ()
AllCls cls (a:as) = (cls a, AllCls cls as)
allEqual :: AllCls Eq [a, b, c] => a -> a -> b -> b -> c -> c -> Bool
allEqual x1 x2 y1 y2 z1 z2 = and
[ x1 == x2
, y1 == y2
, z1 == z2
]
Three can be implemented in terms of AllCls.
type Three :: (k -> Constraint) -> (k -> k -> k -> Constraint)
type Three cls a b c = AllCls cls '[a, b, c]
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