Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type constraints with multiple type variables in Haskell function signature

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.

First scenario

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?

Second scenario

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.

like image 906
NickS1 Avatar asked Sep 11 '25 03:09

NickS1


2 Answers

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
like image 176
Joseph Sible-Reinstate Monica Avatar answered Sep 12 '25 21:09

Joseph Sible-Reinstate Monica


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]
like image 43
Iceland_jack Avatar answered Sep 12 '25 19:09

Iceland_jack