Is there any point in using the constructor after construction when there is only one possible type - haskell

Considering
data Person = Person String Int
let p = Person "Jim" 23
let ofLegalAge :: Person -> Bool
ofLegalAge (_ _ age) -- (instead of (Person _ age))
| age >= 18 = True
| age < 18 = False
Is there any point in ever using the Enum part of the sum type Person over the type of the sum type itself?
You can't add any variants anyway.

Your question, with a few terminology tweaks:
Is there any point in ever [specifying the constructor] Person [...]? You can't add any variants anyway.
In this case, given that there is just one constructor and that you have specified the type of ofLegalAge's argument, specifying the constructor indeed is, in principle, redundant. In practice, though, there is no syntax for ommiting constructors in such a way. One alternative, which works very well for single-constructor types, is using record syntax:
data Person = Person
{ name :: String
, age :: Int
}
ofLegalAge :: Person -> Bool
ofLegalAge person
| age person >= 18 = True
| age person < 18 = False
Here, age is a Person -> Int function that gives access to the relevant field.
Tangential note: your function can be written more simply as...
ofLegalAge :: Person -> Bool
ofLegalAge person
| age person >= 18 = True
| otherwise = False
... and even more simply as:
ofLegalAge :: Person -> Bool
ofLegalAge person = age person >= 18

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)

How to use haskell types?

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.

Rank N types in let bindings

So I've done this...
{-# LANGUAGE Rank2Types, GADTs #-}
type Record fields = forall t. fields t -> t
data PersonField t where
Name :: PersonField String
Age :: PersonField Int
type Person = Record PersonField
And then this...
nigel :: Person
nigel Name = "Nigel"
nigel Age = 39
And it all seems to work as expected.
What I'm struggling with is how to define a Person value inside a let binding. For example this doesn't work:
abigail :: Person
abigail = let x Name = "Abigail"
x Age = 27
in x
Gives me:
Couldn't match expected type `t1' with actual type `[Char]'
`t1' is untouchable ...
Is there a way to make this work inside a let binding?
You need explicit type annotations when GADTs are involved:
abigail :: Person
abigail = let x :: Person
x Name = "Abigail"
x Age = 27
in x
Without it, GHC roughly sees
let x Name = "Abigail"
and says "OK, x is a function from the type of Name, i.e. PersonField String to the type of "Abigail", i.e. String. In the next line,
let x Name = "Abigail"
x Age = 27
GHC now finds out x to accept also a PersonField Int and to return a number. This clashes with the previously inferred type, triggering a type error.
With an explicit type annotation, type inference will not try to infer a wrong type for x: it was provided by the user. Instead, only type checking will be performed.

Can constructor be captured and reused with different set of parameters?

If we have a type Person defined like:
--datatype in record syntax
data Person = Male { firstName :: String, lastName :: String } |
Female { firstName :: String, lastName :: String }
Can this:
flipNames :: Person -> Person
flipNames p#(Male{}) = Male (lastName p) (firstName p)
flipNames p#(Female{}) = Female (lastName p) (firstName p)
be written as one definition of flipNames? Can we somehow capture constructor used, and reuse it with different parametes?
Something like:
flipNames (constructor fname lname) = c lname fname
Although Ganesh has answered your exact question, I want to say that your problem simply indicates an incorrect approach to design of datatypes.
The following approach is much more flexible and removes your problem as such:
data Person = Person { gender :: Gender, firstName :: String, lastName :: String }
data Gender = Male | Female
flipNames (Person gender firstName lastName) = Person gender lastName firstName
The rule behind this is pretty simple: whenever you see yourself creating multiple constructors with the same fields, just use a single constructor and introduce another field with an enum type, as in the code above.
You won't lose any pattern matching capabilities, as the patterns can be like Person Male firstName lastName, and you'll be able to make the Gender type derive Enum and Bounded which will surely help you with types which aren't as trivial. E.g.:
data Gender = Male | Female deriving (Enum, Bounded)
allGenders :: [Gender]
allGenders = enumFrom minBound
maidenName :: Person -> Maybe String
maidenName (Person Female _ z) = Just z
maidenName _ = Nothing
In this particular case, you can do it like this:
flipNames :: Person -> Person
flipNames p = p { firstName = lastName p , lastName = firstName p }
However this only works because the record selectors for Male and Female are the same. There's no general abstraction that captures constructors without their arguments.
To add another option, you could do something similar with phantom types. Note that you're wanting to do this because your Person constructors are redundant, they only exist to differentiate male and female. You could lift this distinction into the type system and let the type inferencing take care of the Male/Female portion.
{-# LANGUAGE FlexibleInstances #-}
data Person a = Person { first :: String, last :: String }
deriving (Show, Eq)
data Male
data Female
flipName :: Person a -> Person a
flipName (Person f l) = Person l f
main = do
let m = Person "John" "Doe" :: Person Male
f = Person "Jane" "Doe" :: Person Female
print m
print f
print (flipName m)
print (flipName f)
print (gender f)
print (gender m)
class Gender a where
gender :: a -> String
instance Gender (Person Male) where
gender _ = "Male"
instance Gender (Person Female) where
gender _ = "Female"
With this in the file person.hs you get this output:
╰─➤ runhaskell person.hs
Person {first = "John", last = "Doe"}
Person {first = "Jane", last = "Doe"}
Person {first = "Doe", last = "John"}
Person {first = "Doe", last = "Jane"}
"Female"
"Male"
The downside of doing this is that you may not want to carry around the extra type parameter. However, the upside is that you could now define typeclass instances with different implementations based on Male and Female types. Although to do the latter requires the FlexibleInstances extension.
Yes, you can do similar (but not identical) using view patterns!
{-# LANGUAGE ViewPatterns #-}
data Person = Male { firstName :: String, lastName :: String }
| Female { firstName :: String, lastName :: String }
| NewBorn { birthdate :: String }
| Child { firstName :: String, lastName :: String }
| Teenager { firstName :: String, lastName :: String }
isAdult :: Person -> Bool
isAdult (Male {}) = True
isAdult (Female {}) = True
isAdult _ = False
flipNames :: Person -> Person
flipNames p#(isAdult -> True) = p{firstName=lastName p, lastName=firstName p}
flipNames p#(isAdult -> False) = p
You can't match a variable against a constructor like that because patterns are not first-class citizens in Haskell. You can have code that's specific to your function, but not something general.
If you're interested in ideas like that, take a look at the research language bondi which does support matching constructors like this. It actually opens up an interesting new vein of expressiveness. In practice, it makes writing code that's generic over the exact structure of the algebraic data type much easier.

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.

Resources