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

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

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.

Editing data type in Haskell

i need help with haskell i'm kinda confused.
I have custom types/Data like this:
type Name = String
type LastName = String
type Mail = String
type FullName = (LastName, Name)
data Person = Person Mail FullName deriving (Show, Read)
type Contact = (FullName,Mail)
type MailAccount = (Person,[Contact])
Let's assume that mail account is stored in a data_base , what i want from now is update the list of contact and i don't know how to do it using this signature:
updateContact :: Mail -> LastName -> Name ->MailAccount -> IO MailAccount
I tried with this :
updateContact l n m macc = do
x <- createPerson l n m
return $ mailAccountUpdate macc x
I created these three function:
--return my list of contacts
contacts:: MailAccount->[Contact]
contacts (_,_,con) = con
createPerson l n m = do
return (Person m (l,n))
mailAccountUpdate acc x = do
return (x:contact acc)
My problem is that thes code won't work because return $ mailAccountUpdate macc x retruns a list and not an IO MailAccount.
I'm not skilled enough to play with the functors and fmap yet.
I want a way to update my list of contact in the mail account , meaning i want a way to access and edit this data and override it with the updated list.
I have to respect the signature , so i tried to play with the logic in the terminal, so i tried a few things so my question is :
Is there a way to edit the data directly like OOP ex : MailAccount.contact() ? if not how can i create a function that can do the work.
Is there a way to have for example two mailAccount with the same type and code it to do the equivalent of this in the ghci terminal :
mail1 = mail2
This will override the data in mail1 with the data from mail2. but i dont know how to code it in haskell with data type.
Thank you everyone in advance for helping me.
There is a neater version of this but I'm doing like this so it can be more understanding:
type Name = String
type LastName = String
type Mail = String
type Id = Int
type FullName = (LastName, Name)
type Contact = (Id, FullName)
data Person =
Person Mail FullName [Contact]
deriving (Show, Read)
updatePersonContact :: Person -> FullName -> Id -> Person
updatePersonContact (Person m fn contacts) newfullName id =
Person m fn (updateContact id newfullName contacts)
updateContact :: Id -> FullName -> [Contact] -> [Contact]
updateContact id newfullName contacts =
map (\(i, fn) ->
if i == id
then (i, newfullName)
else (i, fn)) contacts
person1 :: Person
person1 = Person "email#me.com"("last","first")
[(1,("last1","first1")), (2,("last2","first2"))]
then using it you would:
> updatePersonContact person1 ("NEW","NEWW") 2
-- Person "email#me.com" ("last","first") [(1,("last1","first1")),(2,("NEW","NEWW"))]
We've update Person to have a list of [Contact] so now contacts are attached to a person. Each Contact now has an Id that we can use for getting to the right contact.
You can deconstruct your types in the input like so updatePersonContact (Person m fn contacts) so now we can use all the bits we need to reconstruct our person.
With m fn we give them straight back as those don't need changing. But we're interested in contacts so we pass it to this function updateContact id newfullName contacts.
In updateContact as inputs we have an Id, FullName and list of [Contact].
the way to loop over a list is to use map so here we are using map to go through our list of contacts one by one. map () contacts
the () needs to be a function from a -> b and our a when it loops the first time is (1,("last1","first1") so as you can see it has 2 values which we deal with by having \(i, fn) ->.
So i == 1 and fn == ("last1","first1"). These would update on every iteration of the list like any loop.
The next part we check if i == id and if it does then that's the contact we want to update so we return (i, newfullName) which is our new full name originally passed into updatePersonContact. If i doesn't match id then let's just leave those values alone and return them as they came.
map is a functor so as you might see it's not that bad you might need to do a bit more reading on it to get a better understanding. :)
In Haskell lists are immutable which means every time you are given a list you must return a brand new list.
Check you contacts function. It should be
contacts (_,x) = x
MailAccount is a tuple, with first element as person and second as contact. So it cannot be (,,x) but should be (_,x).
The below updatecontact definition is working fine.
updateContact l n m macc = let myP = Person l (n,m)
newMacc = (myP,((n,m),l):contacts macc)
in return newMacc
I agree with others that there is no need for using IO in definition. The error would have been understood easily without IO.

Haskell - Type conversion?

I'm trying to convert data from a list, 'profile1', into a custom type called 'DataSubject'.
I'm passing this to a function 'makeDS' to attempt this conversion - however the following isn't working:
type Name = String
type Age = Int
type Iq = Int
type Language = String
data DataSubject = DS {name :: Name, age :: Age, iq :: Iq, language :: Language} deriving (Show)
data Contain = Name String | Age Int | Iq Int | Language String deriving (Show) --Use so list can take multiple types
profile1 = [Name "Bob", Age 22, Iq 100, Language "French"]
makeDS :: [Contain] -> DataSubject
makeDS t = DS {name = t!!0, age = t!!1, iq = t!!2, language = t!!3}
main = do
let x = makeDS profile1
putStrLn $ show x
Error:
Couldn't match type ‘Contain’ with ‘[Char]’
I'm just getting started with Haskell - could someone advise on my error? And if there's better ways of doing this?
In the definition of makeDS, the variable t is of type [Contain] (i.e. a list of Contain), so when you say t!!0 this will extract the first element of that list, which has type Contain. The problem is that the name field of DataSubject contains a String (which is an alias of [Char]). So you are trying to store a Contain in the place of [Char], which is not possible because the types are different. You need a different approach in you code.
One issue is that every single Contain value represents a single field of DataSubject. So if we are given a list of Contain, there is no guarantee that the values will be given in a specific order (e.g. Name first, followed by Age, etc) or even that all fields are provided. Even if you always provide all fields in a specific order in your code as convention, haskell cannot possibly know that. One solution that does not depend on order is to try to "build" the DataSubject object step-by-step, by starting with an "empty" DataSubject and then examining the list of Contain and adding the corresponding DataSubject field:
makeDS :: [Contain] -> DataSubject
makeDS = foldr updateDS emptyDS
where
updateDS (Name s) ds = ds {name = s}
updateDS (Age n) ds = ds {age = n}
updateDS (Iq n) ds = ds {iq = n}
updateDS (Language s) ds = ds {language = s}
emptyDS = DS {name = "", age = 0, iq = 0, language = ""}
So here, I defined emptyDS which is an "empty" DataSubject object and a function called updateDS which take a (single) Contain and a DataSubject and updates the DataSubject based on the field specified by Contain and then it returns it. Finally, I use a fold to run repeatedly update the DataSubject (starting with emptyDS) using updateDS.
You have a type mismatch. You have a list of Contain. So when you use
t !! 0
you get a Contain, not a String, which is necessary for name in DS. You need a function Contain -> Name, e.g.
containToName :: Contain -> Name
containToName (Name xs) = xs
containToName _ = error "not a name"
However, that's a partial function, since containToName (Age 12) will lead to an error.
Note that this has nothing to do with typeclasses. Now, if you want to use profile1, one way would be to just use
profile1 :: DataSubject
instead of
profile1 :: [Contain]
e.g.
profile1 :: DataSubject
profile1 = DS "Bob" 22 100 "French"
After all, there's nothing in the type [Contain] that will make sure that you have all the ingredients for a complete DataSubject.
And if there's better ways of doing this?
That depends on what you want to do. If you just want to handle DataSubjects, don't use an temporary list of Contain. If you want to handle user input (or similar), it gets a little bit more tricky.
The declaration of DataSubject says that we need a Name for the name field. And Name is the same as String. So in the expression DS {name = t!!0, ...}, we need t !! 0 to return a String. However, t !! 0 returns an element of t, and t has type [Contain]. So t !! 0 has type Contain, which is different from String.
To fix this type error, you need to convert the Contain to a String, maybe so:
DS { name = case t !! 0 of Name s => s, ... }

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]

Haskell: Create a list of only certain "kind" of type?

I've been working through both Learn You a Haskell and Beginning Haskell and have come on an interesting problem. To preface, I'm normally a C++ programmer, so forgive me if I have no idea what I'm talking about.
One of the exercises in Beginning Haskell has me create a type Client, which can be a Government organization, Company, or Individual. I decided to try out record syntax for this.
data Client = GovOrg { name :: String }
| Company { name :: String,
id :: Integer,
contact :: String,
position :: String
}
| Individual { fullName :: Person,
offers :: Bool
}
deriving Show
data Person = Person { firstName :: String,
lastName :: String,
gender :: Gender
}
deriving Show
data Gender = Male | Female | Unknown
deriving Show
This is used for an exercise where given a list of Clients, I have to find how many of each gender are in the list. I started by filtering to get a list of just Individuals since only they have the Gender type, but my method seems to be completely wrong:
listIndividuals :: [Client] -> [Client]
listIndividuals xs = filter (\x -> x == Individual) xs
How would I get this functionality where I can check what "kind" of Client something is. Also for the record syntax, how is my coding style? Too inconsistent?
First of all, I would recommend not using record types with algebraic types, because you end up with partial accessor functions. For example, it is perfectly legal to have the code position (Individual (Person "John" "Doe" Male) True), but it will throw a runtime error. Instead, consider something more like
data GovClient = GovClient {
govName :: String
} deriving Show
data CompanyClient = CompanyClient {
companyName :: String,
companyID :: Integer, -- Also, don't overwrite existing names, `id` is built-in function
companyContact :: String,
companyPosition :: String
} deriving Show
data IndividualClient = IndividualClient {
indvFullName :: Person,
indvOffers :: Bool
} deriving Show
Then you can have
data Client
= GovOrg GovClient
| Company CompanyClient
| Individual IndividualClient
deriving (Show)
Now you can also define your function as
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [IndividualClient]
listIndividuals clients = filter isIndividualClient clients
Or the more point-free form of
listIndividuals = filter isIndividualClient
Here, in order to make the decision I've simply used pattern matching in a separate function to determine which of Client's constructors was used. Now you get the full power of record and algebraic types, with just a hair more code to worry about, but a lot more safety. You'll never accidentally call a function expecting a government client on an individual client, for example, because it wouldn't type check, whereas with your current implementation it would be more than possible.
If you're concerned with the longer names, I would recommend eventually looking into the lens library that is designed to help you manipulate complex trees of record types with relative ease.
With your current implementation, you could also do something pretty similar to the final solution:
isIndividualClient :: Client -> Bool
isIndividualClient (Individual _ _) = True
isIndividualClient _ = False
listIndividuals :: [Client] -> [Client]
listIndividuals clients = filter isIndividualClient clients
The main difference here is that Individual takes two fields, so I have two _ wildcard matches in the pattern, and the type of listIndividuals is now [Client] -> [Client].

Resources