{-# LANGUAGE TypeFamilies #-} import GHC.Exts (IsList(..)) fizzbuzz :: (IsList l, Item l ~ Int) => l -> IO () fizzbuzz = go . toList where go [] = return () go (n:m) | n`mod`3==0 = putStrLn "fizz" >> go m | n`mod`5==0 = putStrLn "buzz" >> go m | otherwise = print n >> go m
Then
Prelude> fizzbuzz [1..7] 1 2 fizz 4 buzz fizz 7 Prelude> import Data.Vector.Unboxed as UA Prelude UA> fizzbuzz (UA.fromList[1..7] :: UA.Vector Int) 1 2 fizz 4 buzz fizz 7
Now you can argue that this would be better done using the Foldable constraint instead of the ugly conversion to list. In fact, this could not be done, because unboxed vectors do not have a folding instance due to the Unbox restriction!
However, this could also be done with a nonequilibrium limitation, namely
fizzbuzz :: (IsList l, Num (Item l), Eq (Item l), Show (Item l)) => l -> IO ()
This is more general, but perhaps also more inconvenient. When you need, in practice, only one low-key type anyway, an equivalent constraint may be a good choice.
In fact, sometimes itβs convenient for me to move around in an equational constraint to make a type signature shorter if it repeats a little: signature
complicatedFunction :: Long (Awkward (Type a) (Maybe String)) -> [Long (Awkward (Type a) (Maybe String))] -> Either String (Long (Awkward (Type a) (Maybe String)))
can be replaced by
complicatedFunction :: r ~ Long (Awkward (Type a) (Maybe String)) => r -> [r] -> Either String r
which may be better than another DRY opportunity
type LAwkTS a = Long (Awkward (Type a) (Maybe String)) complicatedFunction :: LAwkTS a -> [LAwkTS a] -> Either String (LAwkTS a)