Ananth
Ananth

Reputation: 889

Understanding Haskell's type system

I've got three functions

boombangs xs = [if x < 10 then "BOOM" else "BANG" | x <- xs odd x]

length' xs = sum [1 | _ <- xs]

removeNonUppercase st = [x | x <- st, x `elem` ['A'..'Z']]

Here's what I get for the type(type signature?) of each function

*Main> :t boombangs
boombangs :: Integral a => [a] -> [[Char]]
*Main> :t length'
length' :: Num a => [t] -> a
*Main> :t removeNonUppercase 
removeNonUppercase :: [Char] -> [Char]

Haskell should give me an error when I pass an array of ints to removeNonUppercase or a string to boombangs. It does too, but how does it know it's wrong when I haven't specified the type anywhere.

Also why are the types of boombangs and removeNonUppercase different even though they work on the same input.

I apologize if the question seems vague or downright ignorant. I've just started on Learn You a Haskell and I'm slowly wrapping my head around this paradigm coming from programming mainly in C and python.

Any resources for learning more about haskell and the type system would also be appreciated.

Upvotes: 0

Views: 286

Answers (3)

Lee Duhem
Lee Duhem

Reputation: 15121

how does it know it's wrong when I haven't specified the type anywhere.

Haskell compiler, GHC in your case, can do type inference, which means it can figure out (almost always) what the type of an expression by context information. For example, for

mbangs xs = [if x < 10 then "BOOM" else "BANG" | x <- xs, odd x]

you could infer its type manually like this:

  1. You define mbangs by list comprehension, so xs must be a list, and

  2. you apply odd on the element of xs, therefore the type of xs must be (Integral a) => [a], because the type of odd is (Integral a) => a -> Bool.

  3. Also, for each element of xs, you generate a String by your output function if x < 10 then "BOOM" else "BANG",

  4. therefore the type of mbangs must be Integral a => [a] -> [[Char]], as ghci just told you.

why are the types of boombangs and removeNonUppercase different even though they work on the same input.

Because you are using different output function in their list comprehension definitions.

Upvotes: 2

bheklilr
bheklilr

Reputation: 54058

GHC uses type inference to try to guess what type your function can be.

In boombangs, it can tell that you're passing in a list because of the x <- xs in the list comprehension, and it can tell that the list elements must have type Integral a => a because you're applying odd to each element. It knows you're returning a list of Strings because of the literal "BOOM" and "BANG" inside the list comprehension.

In length', your type is only constrained by sum, which requires Num a => [a]. Your input type is a list of any type because you don't actually perform any operations on the elements themselves, but you do indicate that the parameter is a list by the _ <- xs.

In removeNonUpperCase, it sees that you're passing in a list for the same reasons as the first two functions, and since you're checking if each element is in the list ['A'..'Z'], so each element must be a Char, so you're taking a list of Chars and returning a list of Chars, since no other transformation is performed on the elements. Since String is just an alias for [Char], it knows the type fully.

Upvotes: 5

Scott Hunter
Scott Hunter

Reputation: 49803

For boombangs, it can see by the operators you apply to x that it can't be a string. For removeNonUpperCase, it can see that the elements have to be in ['A'..'Z'], and so can't be ints. Which also explains why they DON'T work on the same input.

Upvotes: 0

Related Questions