Output each type from type list on a seperate line - haskell

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.

Related

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

Why is the restriction on newtype?

I read that "newtype has exactly one constructor with exactly one field inside it." Is this restriction adds any advantage? If the value constructor is limited with only one field, why can't i use the field directly in my code, instead of wrapping it with newtype?
newtype is a tool for creating data abstraction that has no runtime cost.
What do I mean by abstraction?
Suppose you have:
greetPerson :: String -> String -> String
greetPerson greeting name = greeting ++ " " ++ name
greetPerson "Hello" "Mike" => "Hello Mike"
This works fine, but it opens opportunities for misuse:
greetPerson "Mike" "Hello" => "Mike Hello"
The problem is that you're using the same type everywhere (String), carrying no semantic meaning. Let's use a newtype:
newtype Name = Name String
greetPerson :: String -> Name -> String
greetPerson greeting (Name name) = greeting ++ " " ++ name
greetPerson "Hello" (Name "Mike") => "Hello Mike"
We end up with the same functionality, but now the type signature carries more meaning and the compiler can tell us when we misuse it.
What do I mean by no runtime cost?
The newtype from my example exists only at the type level and the compiler generates exactly the same code as if I used String throughout.
This hints at why newtype is only allowed for one constructor with one field.
Imagine you tried to make newtype work for more than one constructor. How would you distinguish which one you have at runtime? You'd have to store some additional information.
Same with more than one field. You'd need some way to bundle two fields together.
All of them add some runtime cost which newtype promises not to do.
If you want more than one field or more than one constructor simply use data:
data Foo = Bar String | Baz Int Bool

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

Haskell data type function parameter

What is the significance of the parenthesis in a function definition in Haskell with respect to the data type of parameters.
For example:
doStuff Name -> Age -> String
doStuff (NameConstr a) (AgeConstr b) = "Nom: " ++ a ++ ", age: " ++ b
with the following defined somewhere beforehand:
data Name = NameConstr String
data Age = AgeConstr Integer
Could the function parameters a and b be captured in a way that negates the need for parenthesis here?
FYI, I'm working through:
http://yannesposito.com/Scratch/en/blog/Haskell-the-Hard-Way/#type-construction
http://learnyouahaskell.com/types-and-typeclasses ,
and I just can't seem grasp this finer detail yet.
Without parentheses, the function would be deemed to have four parameters. I can't think of a counterexample where omitting brackets would lead to ambiguity, though.
If you want, you can redefine your types as follows:
data Name = NameConstr { getName :: String }
data Age = AgeConstr { getAge :: Integer }
so that your function can become:
doStuff n a = "Nom: " ++ getName n ++ ", age: " ++ show (getAge a)
(fixed the last part; a is an Integer and cannot be concatenated to a string)
Indeed, it's possible to parse simple grammar for (even nested) patterns without parens at all. Suppose such one:
<PAT> ::= <WILDCARD> | <VAR> | <CON0> | <CON1> <PAT> | <CON2> <PAT> <PAT> ...
<VAR> ::= <LNAME>
<CON*> ::= <UNAME>
<WILD> ::= "_"
where LNAME is names that starts with lowercase letter and UNAME starts with uppercase letter. While parsing we should look up constructor name so we can find out its arity. Then we can parse constructor fields using arity information. But this lookup might significant complicate and slow down parsing itself. Haskell has much more complex patterns(view patterns, "as" patterns, records, infix constructors with arbitrary fixity, e.t.c.) and omitting parens can lead to ambiguity.
Though there is another reason not to do that. Consider the following code:
data Bar = Bar Int
data Foo = Foo Int
libFunction Foo a Bar b = a + b
someUse bar foo = libFunction foo bar
Next imagine we change datatypes a bit:
data Bar = Bar
data Foo = Foo Int Bar Int
Modified code might still typecheck, but the function will do not that we expect. Not a real world example but nevertheless. And since Haskell have type classes it can be pretty hard to find out where we get something wrong.
In other words: we can loose quality of error messages and parens defends us from unexpected behaviour after changes.
It's slightly silly, but in this case there is actually a way to avoid parentheses:
doStuff :: Name -> Age -> String
NameConstr a `doStuff` AgeConstr b = "Nom: " ++ a ++ ", age: " ++ b
This is exactly the same way you define an infix operator, and using backticks to infix-ify a non-operator identifier works just as well when defining it as when applying it.
I don't recommend actually doing this with functions you don't expect to be used in backtick-y infix style, though.

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

Resources