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

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")])

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.

Trying to add a tuple in front of a list in Haskell but receiving error?

type Name = String
type PhoneNumber = Int
type Person = (Name, PhoneNumber)
type PhoneBook = [Person]
add :: Person -> PhoneBook -> PhoneBook
add (a,b)
add (a,b) ++ []
I'm trying to add an entry in front of the list but its giving me an error
Parse error: module header, import declaration
or top-level declaration expected.
|
30 | add (a,b) : xs
| ^^^^^^^^^^^^^^
where am I going wrong?
You are trying to add a list and a tuple. But ++ is defined for adding lists to lists, not for adding tuples to lists. Haskell is very strict about types, so it won't let you do this.
You should make the tuple a list:
add [(a,b)] ++ []
Or, as was suggesteed by Robin Zigmond in their comment, use the : operator:
add (a,b) : []
You have another issue though - your function definition should contain an = operator:
add (a,b) [] = [(a,b)]
This is how a function is defined in Haskell. On the lefthand side, it tells Haskell wat the inputs are. On the righthand side, it tells Haskell what they should be transformed into.
You may want to refer to Learn You A Haskell : Syntax in Functions for a nice introduction to defining functions in Haskell.
Based on your post you can run the following piece of code:
type Name = String
type PhoneNumber = Int
type Person = (Name, PhoneNumber)
type PhoneBook = [Person]
add :: Person -> PhoneBook -> PhoneBook
add (name, phoneNumber) phoneBook = (name, phoneNumber) : phoneBook
main = print $ add ("xyz", 987) (add ("abcd", 123) [])
which yields:
[("xyz",987),("abcd",123)]

When are brackets required for pattern matching?

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]

Adding to list and incrementing - Haskell

import Data.Char
-- Sample test data
testData :: [Movies]
testData = [("Me and My Broken Heart","Rixton"),
("It’s My Birthday","will.i.am"),
("Problem","Ariana Grande")]
-- record a sale of a track
record :: [Movies] -> String -> String
record t a = []
record ((t, a): xs) a a
| t == a && a == a = [(t,a)]
| otherwise = record xs a t
The correct output should be a modified version of the database.
First, since it seems you're learning, a few notes on style: in a case like this, it is a convention to name one sale with a singular form, and the list of them with the plural, that is:
type Sale = (String, String, Int)
type Sales = [Sale]
Even better, one would often (depending on intended usage and taste) turn Sale into a newtype or full ADT, since that gives you more abstraction and type safety.
Second, to your actual question: the behavior you're seeing comes from the order of pattern matching. In your first match,
recordSale testData aTitle anArtist = []
testDate matches any list, also non-empty ones, before the second pattern can apply. Change that to
recordSale [] _ _ = []
and you won't get only empty lists anymore. Additionally, as #Aleksandar notes, you should not forget to keep the init of the list in cases where the list is not empty, but the filter criteria don't match.
Haskell is a functional language, and encapsulates stateful modifications to data very well in the type system. The usual way of "mutating" a value in Haskell would be to either use a stateful computation (in the ST or State Monad) or to just use recursion and duplicate the values. I think you should learn the latter first.
The function you used will return [] in case aTitle and anArtist don't match any of the entries in the list. Otherwise it will return a list with one single element in it, namely the newly modified one.
You need to build up the list incrementally as you traverse it. Remember, you're basically copying it:
-- record a sale of a track
recordSale :: [Sales] -> String -> String -> [Sales]
recordSale [] aTitle anArtist = []
recordSale ((title, artist, qty): xs) aTitle anArtist
| title == aTitle && artist == anArtist =
(title, artist, qty+1):recordSale xs aTitle anArtist
| otherwise = (title,artist,qty):recordSale xs aTitle anArtist
This should give you the right results. Notice what's different here: you have a recursive call to the function in both cases, and in both cases, too, you're appending the current element either modified or unmodified to the list you're building.
You should also think about your data structure. Having tuples with comments is not a very nice way of constructing complex data structures. Try
data Record = Record { title :: String, artist :: String, quantity :: Integer }
You could even make a couple of newtypes like newtype Title = Title String. Remember, creating data types in Haskell is cheap, and you should be doing it constantly.

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

Resources