How to use haskell types? - haskell

Really simple question, i can't for the life of me google the info :(
I have the following types:
type NI = Int
type Age = Int
type Balance = Int
type Person = (NI, Age, Balance)
How do I create a function that returns true if a person's age is over 65? I've tried:
retired :: Person -> Bool
retired p = p >= 65
It doesn't work obviously, i realise that even when i tried it. I'm stumped on something so simple.

as Person is a tuple you should pattern-match it like this:
retired (_,age,_) = age >= 65
and it should work

type X = Y defines a type alias. In this example, X is defined to be the same as Y. Therefore, retired :: Person -> Bool is the same as retired :: (Int, Int, Int) -> Bool.
Then we can ask the question: how do we obtain a person's age? Well, Person is the same as (Int, Int, Int), and we know the second element of the tuple is the person's age. Therefore, an equivalent question is: how do we obtain the second element of a 3-tuple?
This is done in Haskell by deconstructing the tuple, which is the opposite of constructing the tuple. An example of constructing a tuple is x = (1,2,3). To deconstruct a tuple, we use a similar notation but flip the sides: (a,b,c) = x. This is the same as (a,b,c) = (1,2,3), and the same as the three assignments a = 1; b = 2; c = 3.
Haskell allows you to use this deconstruction notation for function arguments. Therefore, retired p = e could be written retired (ni, age, balance) = e, recalling that Person is a 3-tuple. Now it is straight-forward that e should be age >= 65.
To elaborate further, retired (ni, age, balance) = e is equivalent to retired p = let (ni, age, balance) = p in e. This is useful to know because then function application is clearer. retired x is let (ni, age, balance) = x in [x/p]e where [x/p]e means "substitute x for p in e".
Another approach is to define a data type using record notation.
data Person = Person { ni :: Int, age :: Int, balance :: Int }
This defines a new type called Person, and is not the same as the 3-tuple of type (Int, Int, Int). Additionally, this defines three projection functions ni :: Person -> Int, age :: Person -> Int, and balance :: Person -> Int.
Now if we implement retired :: Person -> Bool we can do so as retired p = age p >= 65, or in point-free form retired = (>= 65) . age.
To reconnect with the first approach, can you also use deconstruction? Absolutely. A Person is constructed as x = Person m n p, and so similarly can be deconstructed as Person a b c = x. Therefore, another definition for retired is retired (Person n a b) = a >= 65.

Related

Getting values from Data in Haskell

type Name = String
type Age = age
data Person = P Name Age derieving (eq)
type People = [Person]
smiths = [P "John" 21, P "Willy" 26]
How to get Johns age? Is there any function like (smiths!!0).age? Or is it only possible with pattern matching?
You can perform pattern matching:
(\(P _ a) -> a) (smiths !! 0)
But it might be better to define Person with record syntax:
data Person = P {
name :: Name
, age :: Age
} deriving Eq
Then Haskell will automatically construct a "getter" age :: Person -> Age, and then you can access this with:
age (smiths !! 0)

Haskell does not support the C++ overloading style in which functions with different types share a common name

I am learning Haskell. When I gone through following documentation.
https://www.haskell.org/tutorial/classes.html
It is mentioned that "Haskell does not support the C++ overloading style in which functions with different types share a common name." I am not getting this statement, I guess ad-hoc polymorphism (which is done by using type classes) is equivalent to method overloading in C++, Java. Can some body explain me is my understanding correct ?
class Equal a where
isEquals :: a -> a -> Bool
type Id = Int
type Name = String
data Employee = Engineer Id Name
data Student = Student Id Name
getEmpId (Engineer empId _) = empId
getStudId (Student studId _) = studId
instance Equal Employee where
isEquals emp1 emp2 = getEmpId emp1 == getEmpId emp2
instance Equal Student where
isEquals stud1 stud2 = getStudId stud1 == getStudId stud2
In the above snippet 'isEquals' function is applied to two different types Employee, Student which is equivalant of overloading in C++, Java. Is my understanding correct?
Partially, yes. However, keep in mind that signature of your isEquals is always a -> a. In C++ you could easily write:
int foo(int a, int b)
int foo(int a, char b)
int foo(char a, char b)
By using typeclasses you're only able to get first and third function, never the second.
UPDATE 1:
as noted in comments, you can achieve the second version by using MultiParamTypeClasses extension (if you're using GHC). Still, there is fourth version:
int foo(int a, int a, int a)
which has wrong arity if you use a typeclass, but is perfectly fine in C++.
Type classes in Haskell are just a fancy way of providing implicit arguments. Consider this code:
data Equal a = Equal {isEquals :: a -> a -> Bool}
type Id = Int
type Name = String
data Employee = Engineer Id Name
data Student = Student Id Name
getEmpId (Engineer empId _) = empId
getStudId (Student studId _) = studId
equalEmployee :: Equal Employee
equalEmployee = Equal {
isEquals = \emp1 emp2 -> getEmpId emp1 == getEmpId emp2
}
equalStudent :: Equal Student
equalStudent = Equal {
isEquals stud1 stud2 = getStudId stud1 == getStudId stud2
}
equalThree :: Equal a -> a -> a -> a -> Bool
equalThree e a1 a2 a3 = isEquals e a1 a2 && isEquals e a2 a3
What is the difference with your code? Well, if in your code we define
equalThree :: Equal a => a -> a -> a -> Bool
equalThree a1 a2 a3 = isEquals a1 a2 && isEquals a2 a3
The difference is that in your code (with classes) compiler finds a suitable class instance by itself, using given types. In my variant, required instances are provided explicitly. But that's the only difference; in fact, your variant would be converted to mine under the hood.
So, classes do not override anything. They are just a fancy syntax for data types and implicit arguments.
Not really. The only way to get the equivalent of C++ (function; Haskell has no methods!) overloading would be a type class
class SomeFunc a where
someFunc :: a
Now you can define someFunc at (more or less) any type you want. But you would have to declare it for every function you want to 'overload', whereas in C++ it's implicit simply from having two functions with the same name.
Haskell's type system is designed to provide something C++'s doesn't: standardized behavior (expressed as laws). So, for example, the standard Prelude defines
class Eq a where
(==), (/=) :: a -> a -> Bool
and now you know every == operator in the language takes two arguments, of the same type, and returns a boolean; by contrast, C++ is perfectly fine with something like
mystring operator (==)(mystring x, string y) {
return x + " == " + y;
}
that breaks all of those 'reasonable' rules. Beyond that, type classes come with laws, e.g.
x == x = True -- x total
x == y = y == x
x == y = True => y == z = True => x == z = True
which are additional restrictions on all instances. That's made a lot easier by the fact that every instance is an instance of this type class (defined in one place), which gives you a single place to define what those laws are. In C++, such 'laws' have more of the character of conventions. Furthermore, C++ is explicitly designed to allow things like using + for string concatenation, while Haskell is explicitly designed to dis-allow things like that (that's what 'Haskell does not supporth the C++ overloading style' probably means), so laws have to have more of the character of conventions in C++, which individual types can ignore if it's convenient for them.

Basic Haskell: problems with a function

me again with another basic problem I have. I'm using ghci.
I (with help) created this working code:
newtype Name = Name String deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Weight = Weight Int deriving (Show)
newtype Person = Person (Name, Age, Weight) deriving (Show)
isAdult :: Person -> Bool
isAdult (Person(_, Age a, _)) = a > 18
However problems occur when I tried making a more complex function updateWeight that allows the user to change a Person's weight from it's previous value. Can you point out where I have gone wrong?
updateWeight :: Person -> Int -> Person
updateWeight (Person(_,_,Weight w) b = (Person(_,_,w+b))
The problem is that you can't use the _ placeholder on the right hand side of an expression. You'll have to pass through the unchanged values. Also, you must wrap the result of w + b with a Weight again. This should work:
updateWeight :: Person -> Int -> Person
updateWeight (Person(n, a, Weight w) b = (Person(n, a, Weight (w + b)))
You can get rid of the boilerplate of passing through the unchanged values by using record syntax for the Person type.

Haskell: Typeclass implies other typeclass

Is it possible to have a typeclass imply another typeclass in Haskell? For example let's say there is a bunch of "things" that can be ordered by an "attribute":
data Person = Person { name :: String, age :: Int }
Person p1 <= Person p1 = (age p1) <= (age p2)
To avoid repetition one could define a "orderable by key" type class
class OrdByKey o where
orderKey :: (Ord r) => o -> r
x <= y = (orderKey x) <= (orderKey y)
Then the instance declaration for Person could look like this
instance OrdByKey Person where
orderKey Person p = age p
Now this does obviously not work for multiple reasons. I wonder if it's possible at all?
As you have specified it, the OrdByKey class can only have one instance
per type, when it sounds like you would like to be able to declare an instance
for each field in your record type.
To accomplish that, you will have to put the field type into the class
definition as well. This lets you do something like the following:
{-# LANGUAGE MultiParamTypeClasses #-}
data Person = Person { name :: String, age :: Int }
class (Ord r) => OrdByKey o r where
orderKey :: o -> r
instance OrdByKey Person Int where
orderKey p = age p
x <=? y = (orderKey x :: Int) <= (orderKey y :: Int)
However, you can only have one instance per field type, so if your
Person type looks like
data Person = Person { name :: String, age :: Int, ssn :: String}
you will not be able to have a version to compare on both the name and
the ssn fields. You could get around this by wrapping each field in a
newtype so each field has a unique type. So your Person type would look
like
data Person = Person { name :: Name, age :: Age, ssn :: SSN}
That would lead to a lot of newtypes floating around though.
The real downside of this is the need to specify the return type for the
orderKey function. I would recommend using the on function from
Data.Function to write the appropriate comparison functions. I think a
function like
compareByKey :: (Ord b) => (a -> b) -> a -> a -> Bool
compareByKey = on (<=)
generalizes your idea of "can be compared by some key". You just have to give
it the function that extracts that key, which would be exactly the accessor
functions for your Person type, in this case.
I can't think of an instance where the OrdByKey class would be useful and trying to overload the <= with multiple versions for the same type seems like it would be down right
confusing in practice.
You could do this:
instance Ord Person where
compare p1 p2 = compare (age p1) (age p2)
Now the standard <= operator will work on Persons and compare their ages.

something like database but not a database and sum price

How can I sum prices where month = 5 and year = 2010 ? I have date in data Subject and prices in data Sell, they are connected by id. This is my code:
-- subject id, date
data Subject = Subject Int CalendarTime deriving (Read, Show)
-- sell id, subject id, price
data Sell = Sell Int Int Double deriving (Read, Show)
Real world uses should probably use a database or at least a mapping (from the containers or unordered-containers packages), but a simple solution can be obtained using simple list comprehension.
Assuming you have simple lists of Subject and Sell:
type Subjects = [Subject]
type Sells = [Sell]
You could make an O(n*m) implementation (good for play only!):
price :: Sell -> Double
price (Sell _ _ d) = d
calTime :: Subject -> CalendarTime
calTime (Subject _ c) = c
sIdent :: Subject -> Int -- Omitted, you should use record syntax anyway
eIdent :: Sell -> Int -- Omitted
sumPred :: (CalendarTime -> Bool) -> Subjects -> Sells -> Double
sumPred js es = sum [price e | j <- js, e <- es
, sIdent j == eIdent e
, pred (calTime j)]
But as I said, that's foolish. Using a DB with Subjects keyed by CalendarTime and Sell's keyed by identity will give you a more practical solution.

Resources