I am new to haskell and I read some stuff about this thing called type signatures, but there are a few things that I dont understand.
Here is the the code am looking at:
--mult applies product
mult :: Num a => [a] -> a
mult = foldr (*) 1
--posList filters positive numbers out
posList :: (Ord a, Num a) => [a] -> [a]
posList = filter (>0)
--trueList determines whether all of the members of a list are T or not
trueList :: [Bool] -> Bool
trueList = foldr(&&) True
--evenList determines where all of the members of a list are even or not
evenList :: (Integral a, Foldable t) => t a -> Bool
evenList x = if (((foldr (+) 2 x ) `mod` 2) == 0) then True else False
So, I know you can do those functions easier in a different way, but I had assignments to use higher order functions map, filter and foldr, so I had to do them that way. Anyway, I understand how the functions work since I am the one who wrote them , what I dont understand is those terms Integral, Foldable, what do they mean? and what are they called in terms of Haskell?
Those are constraints to a polymorphic type.
Haskell, despite being statically typed, makes it very easy to write function that can work with different types, through a system called parametric polymorphism. I'll first give all your functions concrete (monomorphic) signatures instead:
mult :: [Integer] -> Integer
mult = foldl' (*) 1 -- foldl' is better for performance/memory than foldr
posList :: [Integer] -> [Integer]
posList = filter (>0)
trueList :: [Bool] -> Bool
trueList = foldl' (&&) True
evenList :: [Integer] -> Bool
evenList x = foldl' (+) 2 x `mod` 2 == 0
-- `if True then True else False` is tautological
All of these signatures work (both with the improved implementation and with your original ones).
But they only work with lists of Integer. That's not always sufficiently general; for instance, it's perfectly reasonable to compute the product of a list of fractional numbers. But with the monomorphic signature, mult [1.5, 2.4, 20] does not work: these numbers aren't compatible with Integer. You don't want to restrict the function to integers, you just want it to work with any number type and always get a result of the same type as the elements. I.e., you basically want the signature
mult :: ∀ a . [a] -> a
...to be read "for all types a, the function mult takes a list whose elements have type a, and gives a single a-value as the result. The ∀ is implicit in Haskell when you have such type variables, i.e. you could as well make it
mult :: [a] -> a
That doesn't work yet though, because the function must be able to multiply elements. But that is not possible for all types, only for number types. Hence you add the constraint
mult :: Num a => [a] -> a
For posList it's much the same thing: the signature is essentially
posList :: [a] -> [a]
but you also need to be able to compare (Ord) the elements with 0 (Num). Thus the constraint
posList :: (Num a, Ord a) => [a] -> [a]
In case of evenList, the numerical operations are (+), (==) and mod. So in principle we need Num, Eq and Integral, but Integral has Num and Eq as superclasses anyway, so that alone is sufficient:
evenList :: Integral a => [a] -> Bool
...which is not the most general form yet, though. You reduce that list with a fold, but lists aren't the only thing that can be folded, for instance you can also fold arrays, maps and Maybe values. The type class for all containers that can be folded is called, you won't guess it, Foldable. So we end up with
evenList :: (Integral a, Foldable t) => t a -> Bool
You could apply the same generalisation to mult and trueList:
mult :: (Num a, Foldable t) => t a -> a
trueList :: Foldable t => t Bool -> Bool
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