When are brackets required for pattern matching? - haskell

Using this code :
-- Store a person's name, age, and favourite Thing.
data Person = Person String Int Thing
deriving Show
brent :: Person
brent = Person "Brent" 31 SealingWax
stan :: Person
stan = Person "Stan" 94 Cabbage
getAge :: Person -> Int
getAge (Person _ a _) = a
to access age of stan use :
getAge stan
prints :
94
Defining stan does not require brackets.
However getAge Person "a" 1 Cabbage causes error :
<interactive>:60:8:
Couldn't match expected type `Person'
with actual type `String -> Int -> Thing -> Person'
Probable cause: `Person' is applied to too few arguments
In the first argument of `getAge', namely `Person'
In the expression: getAge Person "a" 1 Cabbage
I need to use brackets :
*Main> getAge (Person "a" 1 Cabbage)
1
Why are brackets required in this case ? But when defining stan = Person "Stan" 94 Cabbage does not require brackets ?

getAge Person "a" 1 Cabbage
is parsed as
(((getAge Person) "a") 1) Cabbage
i.e. this would have to be a function accepting a Person-constructor and three more arguments, not a function accepting a single Person-value.
Why it's done this way? Well, it makes multi-parameter functions much nicer. For instance, Person itself is a function, taking three arguments (the data type's fields). If Haskell didn't just feed arguments one-by-one, you would also need to write Person ("Brent", 31, Sealingwax).
The parsing rules Haskell uses are actually much simpler than in most other languages, and they allow partial application very naturally, which is really useful. For instance,
GHCi> map (Person "Brent" 31) [Cabbage, SealingWax]
[Person "Brent" 31 Cabbage, Person "Brent" 31 SealingWax]

Related

Output each type from type list on a seperate line

type FirstName = String
type Surname = String
type Age = Int
type Id = Int
type Student = (FirstName, Surname, Age, Id)
testData :: [Student]
testData = [("Garry", "Queen", 10, 1),
("Jerry", "Bob", 11, 2),
("Amy", "Big", 9, 3)]
I am trying to output each students information on a new line using the testData.
How would I go about doing this?
I tried this but it doesn't work.
studentToString :: Student FirstName Surname Age Id -> String
studentToString (Student FirstName Surname Age Id) = FirstName ++ Surname ++ Age ++ Id
studentsToString :: [Student] -> String
studentsToString (x:xs) = putStrLn(studentToString x) ++ studentsToString xs
It gave me an error
error: Not in scope: data constructor ‘Student’
for this line
studentToString :: Student FirstName Surname Age Id -> String
In your definition, Student is a type alias, not a data constructor, therefore you cannot use it in the way you intended. It would be as if you wrote:
studentToString :: Student FirstName Surname Age Id -> String
studentToString :: (FirstName, Surname, Age, Id) FirstName Surname Age Id -> String
Not much sense there. One way to fix this is to convert your Student definition to a data constructor:
data Student = Student FirstName Surname Age Id
studentToString :: Student -> String
...
One neat trick of data constructors is that it will let you use pattern matching on the wrapped values, just as if it were a tuple:
getAge :: Student -> Age
getAge (Student _ _ age _) = age
getId :: Student -> Id
getId (Student _ _ _ id) = id
...
I am afraid to say that there are very many things wrong with your code. I'll try to go through them one at a time and adapt this to a working solution.
1) This line:
type Student = (FirstName, Surname, Age, Id)
declares what is known as a "type synonym". It just makes Student mean exactly the same thing to the compiler as the 4-tuple (FirstName, Surname, Age, Id). You could instead have done this:
data Student = Student FirstName Surname Age Id
which would have made Student into a completely new type, which you can construct values of by using a function - also called Student, the "data constructor" on the right of the = sign (you could assign any name to this, but it's conventional to use the same name as that of the type itself) - applied to values of types FirstName, Surname, Age and Id.
While I think most experienced Haskell programmers (which I'm not, just a developer who is interested in the language and tries to dabble when I get the chance) would prefer the data declaration because it is more "type safe" (there is no risk of confusing a Student with some 4-tuple of the same types which is intended to be something else), I think the type synonym which you have is fine for casual use, and I'll continue with this below.
Anyway, your first problem is with the type signature of the studentToString function. As I said, Student is a type in its own right, in this case a synonym for a particular type of 4-tuple. Although it has fields of the 4 types you've listed in the signature, it is a type in its own right, and doesn't need - and therefore cannot have - other types after it in order to make a valid type. This is nonsensical here. The input type of your function is a Student - that is, a 4-tuple. So the type signature should be simply:
studentToString :: Student -> String
(As an aside, that fits rather neatly with the accurate name that you've given the function.)
2) You've got similarly confused with the function definition itself:
studentToString (Student FirstName Surname Age Id) = ...
This won't compile, for the reason GHC is giving you in the error message. It only knows Student as the name of a type, and in order for this definition to make sense it would have to also be the name of a function - specifically a constructor function. As I mentioned above, you could have made this happen by using a data declaration for Student, rather than a type synonym. But this isn't what you've done. Your Student type, as I've said, is simply a 4-tuple, so you have to define the function in such a way that it accepts a 4-tuple. In addition, uppercase identifiers like FirstName refer to types and type constructors, while you want lowercase identifiers like firstName which refer to functions and variables. So you should do this instead:
studentToString (firstName, surname, age, id) = ...
3) You also have a type mismatch on the right hand side of the function definition. The ++ operator is used to put lists (of the same type) together into a bigger list. It's fine and normal to do this with strings, because Haskell strings are simply lists of characters. (There are performance reasons not to do this if it's for a performance-critical application, or if your strings will be huge - but don't worry about this for simple learning exercises like this.)
But the problem is that age and id will have type Int, not String. You can't use ++ with them at all - an Int is not a list, and certainly not a list of Char. Unlikely many languages, Haskell will not happily convert numbers to strings for you when used in a "string context". It has a static and very strict type system which simply does not allow you to use values of the wrong type at any point. (In other respects the type system can be very flexible, due to typeclasses and polymorphic functions, as you'll discover as you learn more about the language. But converting a numerical type to a string representation is not something it will do for you.)
So you have to do the conversion explicitly - and Haskell has a simple function for this, called show. Without being too technical, it basically converts anything into a string that can reasonably be converted. So, putting this together with all my previous comments, a working version of studentToString would be:
studentToString :: Student -> String
studentToString (firstName, surname, age, id) = firstName ++ surname ++ show age ++ show id
4) A minor point - the above will compile fine, but for practical purposes you probably want to space out the different parts of the output string:
studentToString :: Student -> String
studentToString (firstName, surname, age, id) = firstName ++ " " ++ surname ++ " " ++ show age ++ " " ++ show id
5) Moving on now to your second function, studentsToString, there is a fundamental type mismatch in your attempted implementation:
studentsToString :: [Student] -> String
studentsToString (x:xs) = putStrLn(studentToString x) ++ studentsToString xs
Your type signature proclaims that the output will be a String (as does the name!). Yet putStrLn does not output a String! It outputs a value of type IO () - without going too deeply into Haskell's type system and how it handles I/O in a pure way, we can say that this is not the same thing, precisely because it has a "side effect" (printing some output to the terminal). Nothing in Haskell has any side effects - all values are "pure" - except for those whose type begins with IO. Basically a value of type IO () is an "action" that, when executed, does something in the "outside world" (in this case, prints something), and returns no useful value of its own. (And note that merely making such an "action" in your code does not execute its effects - that does happen though when you run your final program, or output such a value in GHCi.)
So your function is frankly a bit confused. You can either convert the list of students to a string (a pure operation) and then try to output the result. Or you could simply make the function output the result, using a return value of IO ().
Here, quicky, is how I might do both of these. First, the pure function could be (note how similar it is to your faulty version):
studentsToString :: [Student] -> String
studentsToString [] = ""
studentsToString (x:xs) = studentToString x ++ ", " ++ studentsToString xs
All I have really changed is removing the putStrLn so that you get a "pure" String result. I've also added a "base case" for the empty list - without this your function will crash because each recursive step acts on a shorter list, and eventually it'll reach the empty list and fail because the (x:xs) pattern doesn't match an empty list.
Finally, and least importantly, I've added some "padding" between each output value, so they don't all run on together. I've chosen a comma and space, but this is arbitrary. You may prefer a newline - or indeed anything else.
Having done that, you can just run putStrLn (studentsToString testData) in GHCi, to output the result. (Although the putStrLn isn't necessary, GHCi always prints any expression you give it.)
Finally, here is a very simple - and slightly more advanced - way in which you could output the test data, one student on each line:
mapM_ (putStrLn . studentToString) testData
To understand this fully you have to know a bit about Monads - which, trust me, are not as scary as they sound - but it basically makes an "action" (like a small program) that "loops" over the testData list, applies studentToString to each element, and prints each on a new line.

Couldn't match expected type 'x' with actual type `([Char], [Char], [Char])'

I have created a data type that is to store information about a group of people: their names and date of birth. The data type is simply two lists of 3-tuples, the first list holds names (first, middle, last) and the second holds DOB (Day, Month, Year). You can see the data type below (I've omitted the DOB type because it's irrelevant to this question):
data Names = Names [(String, String, String)]
data People = People Names
I'm trying to write a function that creates the initial list, so it returns the name of the first person and then the list of People. Here it is so far:
initiallist :: ([String], People)
initiallist = (first_name, all_people)
where first_name = "Bob" : "Alice" : "George" : []
all_people = People ("Bob","Alice","George") : []
This results in
error:
* Couldn't match expected type `Names'
with actual type `([Char], [Char], [Char])'
* In the first argument of `People', namely `("Bob", "Alice", "George")'
In the first argument of `(:)', namely
`People ("Bob", "Alice", "George")'
In the expression: People ("Bob", "Alice", "George") : []
Now, in my knowledge of Haskell, I thought that String is just a [Char]. So I figured my code would work fine, but it has me absolutely stumped.
The : operator has lower priority than applying the People constructor. So your expression is actually:
all_people = (People ("Bob","Alice","George")) : []
It is indicated in the error message, saying what did People constructor apply to:
...first argument of `People', namely `("Bob", "Alice", "George")'
You will have to make it explicit:
all_people = People (("Bob","Alice","George")) : [])
Or, with the list notation:
all_people = People [("Bob","Alice","George")]
There are two problems in your code.
First one is, People data type accepts a Names data type but you are trying to feed it with a [(String,String,String)] data type.
Second is, as mentioned in #Koterpillar's answer the precedence of the value constructor (here People and/or Names) are higher than the list value constructor : (left association).
Another point is your data types can be defined by newtype yielding a more efficient code.
So by keeping in mind that the value constructors are also functions, if you wanted to use the : constructor to create your lists you may as well do like;
newtype Names = Names [(String, String, String)]
newtype People = People Names
initiallist :: ([String], People)
initiallist = (first_name, all_people)
where first_name = "Bob" : "Alice" : "George" : []
all_people = People $ Names $ ("Bob","Alice","George") : []
or of course you may preferably do like
all_people = People (Names [("Bob","Alice","George")])

Beginning Haskell: Calling a simple function, matching a data structure

I am a little embarrassed to be asking such a banal question on here, but I am trying to follow the recent 'Beginning Haskell' book (Apress) as an introduction to Haskell, but the code does not work. I found the source code online, which was identical to my own, and that doesn't work either.
data Client = GovOrg String
| Company String Integer Person String
| Individual Person Bool
deriving Show
data Person = Person String String Gender
deriving Show
data Gender = Male | Female | Unknown
deriving Show
clientName :: Client -> String
clientName (GovOrg name) = name
clientName (Company name _ _ _) = name
clientName (Individual (Person fName lName _) _) = fName ++ " " ++ lName
When I attempt to call the function with
clientName (GovOrg "NASA")
It returns "NASA".
But when I try to call it with:
clientName (Company "Virgin")
Or:
clientName (Individual "Adam" "Smith") -- (or any other permutations of this function call)
The result is Type mismatch, and:
Probable cause: `Company' is applied to too few arguments
As you might be able to tell, I have a difficult time with the syntax at this stage, but I'm sure I would have a better time of it if I could get it to work in the first place. Is there something wrong with how I call the function from the interpreter?
That's because you are not passing up the entire data. Try this:
λ> clientName (Company "Virgin" 3 (Person "fname" "lname" Male) "hello")
"Virgin"
λ> clientName (Individual (Person "Adam" "Smith" Male) True)
"Adam Smith"
Both Company and Individual are data constructors and you can inspect their type also:
λ> :t Individual
Individual :: Person -> Bool -> Client
So, for Construcing Individual you should pass Person and Bool type to it. Something like this builds up a Client type:
λ> let a = Individual (Person "Adam" "Roy" Male) True
λ> :t a
a :: Client
The problem is that you try to make a Company with Virgin as its only parameter. The parameters you actually need for a Company are a String and an Integer and a Person and another String.
clientName (Company "Virgin" 123 (Person "Three" "Fx" Unknown) "someString")
will work.
Both Company and Individual have data constructors that take more than 1 argument. Specifically Company also takes an Integer, a Person and a String; Individual also takes a Bool.
Not only that, but Individual takes a Person as first argument.
You should, for example, call:
clientName (Individual (Person "Adam" "Smith" Male) True)
Live demo

Why doesn't GHC give a compile time warning for the "No match in record selector" exception?

When I run this buggy code...
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
getAge :: Person -> Int
getAge p = pAge p
getName :: Person -> String
getName p = pName p
main :: IO ()
main = do
let p1 = Kid "fred" 5
p2 = Adult "john"
ps = [p1, p2]
names = map getName ps
ages = map getAge ps
putStrLn $ "names: " ++ show names
putStrLn $ "ages: " ++ show ages
... I get this in ghci:
names: ["fred","john"]
ages: [5,* * * Exception: No match in record selector pAge
I know how to avoid this error, but I'm wondering why compiling with "ghc -Wall" didn't warn me about this problem. Is there another tool that can help me to prevent this type of error?
Is there [a] tool that can help me to prevent this type of error?
No, but there could be.
As you know, record syntax automatically generates getters with the same name as the attributes you define. Therefore the code
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
creates the functions pName :: Person -> String and pAge :: Person -> Int. Now, suppose Haskell had subtyping. If it did, then Kid could be a subtype of Person, and pAge could have the more appropriate type Kid -> String. However, Haskell does not have subtyping, and there is therefore no Kid type.
Now, given that Person -> String is the most specific type we can give to pAge, why not warn that pAge is a partial function at compile time? Let me divert the question by referring to the List example
data List a = Cons { head :: a, tail :: List a } | Empty
In this example, head and tail are partial functions: the two components of a non-empty list, but (due to Haskell's lack of subtyping) meaningless accessors on the empty list. So, why no warning by default? Well, by default, you know the code you have written. The compiler doesn't provide warnings if you use unsafePerformIO, because you're the programmer here, you're expected to use such things responsibly.
So tl;dr: if you want the warning here:
getAge :: Person -> Int
getAge p = pAge p
then you're out of luck, because the type system does not have enough information to deduce that this is a problem.
If you want the warning here:
data Person = Adult | Kid { pAge :: Int }
then I'm sure it would be trivial to implement: just check that a given field exists in some constructors but not others. But I do not foresee this warning being widely useful for everyone; some might complain that it would be just noise.
I'd be surprised if http://community.haskell.org/~ndm/catch/ doesn't pick this up.
It does, since 8.4, with -Wpartial-fields.
https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-fields

Creating convenient type

I am beginning with Haskell. I have a situation where it is convenient to work the following type synonyms:
type Adult = Int
type Youth = Int
However I can not overload functions on Adult and Youth even if they had been synonyms for different types so have to have two seperate versions of functions eg. doSomethingForAdult and doSomethingForYouth , so next I tried
data Person = Adult Int | Youth Int
Then I can pattern match and use a single version of functions,
but then I loose the option of using the Adult and Youth as types in functions declarations which is convenient. Is there a middle way ? I looked at Either, but from the description in tutorials it seems this would be misuse ? Something similar to a small type hierarchy with Person at the root and Youth and Adult derived and still being synonyms for Int would be perfect but I can not figure out how.
I don't see how that would be a misuse of Either. Just as (,) is a generic product type, Either is perfectly acceptable as a generic sum type, i.e. anything of the form:
data T = T1 A | T2 B C
...can be thought of algebraically as A + (B * C), which is equivalent to Either A (B, C).
On the other hand, if you want to distinguish between Adult and Youth, using synonyms for the same actual type can be counterproductive; they're just transparent aliases. An alternate approach would be to use newtypes, like this:
newtype Adult = Adult Int deriving (Eq, Ord, Show, Read, Num)
newtype Youth = Youth Int deriving (Eq, Ord, Show, Read, Num)
Adjust the deriving clause to taste; I added Num here because the underlying type is numeric, but if all you want is a unique identifier then adding or multiplying them doesn't make sense. At this point you can then use a sum type for Person if you like, or define a type class to get proper overloading:
class Person a where
-- (etc...)
instance Person Adult where -- ...
instance Person Youth where -- ...
Any functions defined in Person are then effectively overloaded on Adult and Youth, letting you dispatch based on type the way "overloaded" functions in other languages do.
Are you wanting a typeclass?
data Adult = Adult Int
data Youth = Youth Int
class Person a where
doSomething :: a -> b
instance Person Adult where
doSomething (Adult i) = ...
instance Person Youth where
doSomething (Youth i) = ...
This is the typical manner of overloading function in Haskell. The typeclass, Person, has a single function, doSomething :: a -> b. Each data type you want to be an instance of Person can have its own, separate, implementation. If you want the underlying implementation to be the same then just use another function, doSomethingGlobal :: Int -> b and let each instance equal this new function.
You can have both:
type Adult = Int
type Youth = Int
data Person = Adult Adult | Youth Youth
Data constructors are in a separate namespace from types so there is no conflict here.
I would suggest to just add the type synonym
type Person = Int
and you are able to give types to functions that work both, for Adults abd Youth, like
doSomething :: Person -> ...
(and you keep the possibility to use Adult and Youth in other type signatures)
I suggest to make Person polymorphic:
data Person a = Person a Int
With this definition you can write general functions on Person level that don't care about the concrete value of a:
getInt :: Person a -> Int
getInt (Person _ i) = i
incInt :: Person a -> Person a
incInt (Person p i) = Person p (inc i)
Then you can define "tags" to substantiate a Person:
data Adult = Adult
data Youth = Youth
Now you can write functions for a specific type of Person
rock :: Person Youth -> String
rock (Person Youth k) = "I rock until " ++ show k ++ " in the morning!!!"

Resources