Basic haskell : Defining types - haskell

sorry to bother you.
I'm new to haskell and trying to define a new type Person.
I'm using the GHCI compiler.
I'm running the file new.hs which includes:
Name = String
Age = Int
Weight = Int
Person = (Name, Age, Weight)
but I get not in scope errors. Can anyone help?
Jeremy D helped me with this and I solved that but how can I add a function such as:
isAdult :: Person -> Bool
George = ("George", 20, 80)
isAdult George = if Age >=18

try with:
type Name = String
type Age = Int
type Weigth = Int
type Person = (Name, Age, Weigth)
For a simple introduction, look here
To answer your second question, here is what I did:
newtype Name = Name String deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Weigth = Weight Int deriving (Show)
newtype Person = Person (Name, Age, Weigth) deriving (Show)
isAdult :: Person -> Bool
isAdult (Person(_, Age a, _)) = a > 18
When executing it:
*Main> let p = Person(Name "Jeremy", Age 18, Weight 199)
*Main> p
Person (Name "Jeremy", Age 18, Weight 199)
*Main> isAdult p
False
*Main> let p = Person(Name "Jeremy", Age 20, Weight 199)
*Main> isAdult p
True
*Main>

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)

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

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

Haskells data types and constructors with functions?

I'm new to Haskell and I'm looking at basic data types and constructors with functions.
I've done the below code:
data Name = Name String deriving (Show)
data Age = Age Int deriving (Show)
data Iq = Iq Int deriving (Show)
data Language = Language String deriving (Show)
data DataSubject = DSInformation Name Age Iq Language | DSConstruct {name :: String, age :: Int, iq :: Int, language :: String} deriving (Show)
makeDataSubject :: DataSubject -> DataSubject --Take in info and output a record
makeDataSubject (DSInformation (Name n) (Age a) (Iq i) (Language l)) = (DSConstruct {name = n, age = a, iq = i, language = l})
main = do
let x = makeDataSubject $ (DSInformation (Name "Ron") (Age 34) (Iq 100) (Language "French"))
putStrLn $ show x
Runs fine, however it seems overly verbose -- how can I make to make it better?
Most of your data declarations can probably be simple type aliases.
type Name = String
type Age = Int
type Iq = Int
type Language = String
With these aliases, there is no significant difference (record syntax aside) between the two constructors for DataSubject. Get rid of one, and dispense with makeDataSubject. (Unless you want to encapsulate some logic or prevent pattern matching, you don't need a smart constructor to do what you are doing.)
data DataSubject = DS { name :: Name
, age :: Age
, iq :: Iq
, language :: Language
} deriving (Show)
main = do
let x = DS { name="Ron", age=34, iq=100, language="French"}
putStrLn $ show x
If you do want real types, not just aliases, use newtype instead of data.
newtype Name = Name String deriving Show
newtype Age = Age Int deriving Show
newtype Iq = Iq Int deriving Show
newtype Language = Language String deriving Show
data DataSubject = DS { name :: Name
, age :: Age
, iq :: Iq
, language :: Language
} deriving (Show)
main = do
let x = DS { name=Name "Ron", age=Age 34, iq=Iq 100, language=Language "French"}
putStrLn $ show x
You might want to add a smart constructor here, but have it take each piece of data as a separate argument (wrapped or unwrapped), not a single argument that is already the return value up to isomorphism. (That is, your constructor was essentially the identity function other than some repackaging of the input.)
makeDataSubject :: String -> Int -> Int -> String -> DataSubject
makeDataSubject name age iq lang = DS {name=Name name, age=Age age, iq=Iq iq, language=Language lang}
or
makeDataSubject' :: Name -> Age -> Iq -> Language -> DataSubject
makeDataSubject' name age iq lang = DS {name=name, age=age, iq=iq, language=lang}
Unfortunately you are running into one of Haskell's weaknesses: the record system. It would be nice if we had some sort notation to express subject.name and subject.age rather than destructuring explicitly but right now there is no good answer. Work coming down the pipeline in GHC 8 should address the problem soon, however, and there are are all sorts of libraries working in the problem space. But, for this question specifically, we can employ a simple trick: -XRecordWildcards.
{-# LANGUAGE RecordWildCards #-}
module Main where
newtype Name = Name String deriving Show
newtype Age = Age Int deriving Show
newtype Iq = Iq Int deriving Show
newtype Language = Language String deriving Show
data DataSubject =
DSInformation Name Age Iq Language
| DSConstruct {name :: String, age :: Int, iq :: Int, language :: String}
deriving Show
-- | Take in info and output a record
makeDataSubject :: DataSubject -> DataSubject
makeDataSubject (DSInformation (Name name) (Age age) (Iq iq) (Language language)) =
DSConstruct {..}
main :: IO ()
main =
print . makeDataSubject $ DSInformation (Name "Ron") (Age 34) (Iq 100) (Language "French")
By destructuring into the names of the fields, {..} will pick up on those bindings in scope to automatically populate the fields. You will absolutely want to turn on -Wall -Werror during compilation because it will be now more than ever easier to misspell something and forget to populate a field and then you end up with a partial record (another wart of the records system) where some fields are left undefined.

basic Haskell : searching through multiple lists for the same elements

I've been battling with this problem for a while. I'm trying to create an "organisation" which is a list of Gyms. These gyms are a list of People. Each person has an ID number, an age and an amount of credit.
I want the FindID function to search through the organisation to search through the list of Gyms to find the users with the inputted ID and then return the total of their credit. However, I feel I'm overcomplcating the problem and I am really struggling now.
newtype ID = ID Int deriving (Show)
newtype Age = Age Int deriving (Show)
newtype Credit = Credit Int deriving (Show)
newtype Person = Person (ID, Age, Weight) deriving (Show)
type Gym = [Person]
type Organisation = [Gym]
getAge :: Person -> Int
getAge (Person(a,Age b,c)) = b
getID :: Person -> Int
getID (Person(ID a,b,c)) = a
getCredit :: Person -> Int
getCredit (Person(a,b,Credit c)) = c
p = Person ( ID 123, Age 65, Credit 12000)
q = Person ( ID 321, Age 64, Credit 0)
e = Person ( ID 453, Age 30, Credit 3000)
r = Person ( ID 123, Age 65, Credit 2310)
s = Person ( ID 364, Age 32, Credit 32340)
t = Person ( ID 123, Age 65, Credit 1300)
org1 = [p,q,e]
org2 = [r,s,t]
hasPerson :: Gym->Int-> Bool
hasPerson gym' id' = not (null(filter hasperson' gym') )
where
hasperson' person' = getID person' == id'
findID:: ID -> Organisation -> Int
findID id' org = total
where
IsInGym org' = hasPerson ( org' id' )
validGym = filter (IsInGym) org'
total = sum ( map getCredit validGym)
First, I would recommend using a record to represent your person, unless you have a particular reason to assign a new type to each field:
type ID = Int
type Age = Int
type Credit = Int
data Person = Person
{ personId :: ID
, personAge :: Age
, personCredit :: Credit
} deriving (Eq, Show)
type Gym = [Person]
type Organization = [Gym]
Next, you can use map to convert a Gym into [Int] with personId, then you can use the built-in elem to check if the ID given appears in that list.
hasPerson :: Gym -> ID -> Bool
hasPerson gym pId = pId `elem` map personId gym
Now, for the findID function, I would suggest renaming it to something like organizationCredit, and I would make a simpler function called gymCredit to calculate it for a single gym:
gymCredit :: ID -> Gym -> Credit
gymCredit pId gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
organizationCredit :: ID -> Organization -> Credit
organizationCredit pId org = sum $ map (gymCredit pId) org
Alternatively, you could declare your functions as
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = sum $ map personCredit $ filter (\p -> personId p == pId) gym
where pId = personId person
organizationCredit :: Person -> Organization -> Credit
organizationCredit person org = sum $ map (gymCredit person) org
EDIT: To stick with your old types, you just have to define a few extra functions yourself, then put them in your code where you need to
newtype ID = ID Int deriving (Eq, Show)
newtype Age = Age Int deriving (Eq, Show)
newtype Credit = Credit Int deriving (Eq, Show)
newtype Person = Person (ID, Age, Credit) deriving (Eq, Show)
type Gym = [Person]
type Organisation = [Gym]
personId :: Person -> ID
personId (Person (i, a, c)) = i
personAge :: Person -> Age
personAge (Person (i, a, c)) = a
personCredit :: Person -> Credit
personCredit (Person (i, a, c)) = c
idVal :: ID -> Int
idVal (ID x) = x
ageVal :: Age -> Int
ageVal (Age x) = x
creditVal :: Credit -> Int
creditVal (Credit x) = x
gymCredit :: Person -> Gym -> Credit
gymCredit person gym = Credit $ sum $ map (creditVal . personCredit) $ filter (\p -> personId p == pId) gym
where pId = personId person
organisationCredit :: Person -> Organisation -> Credit
organisationCredit person org = Credit $ sum $ map (creditVal . gymCredit person) org
It is important to note that I've added Eq to the list of derived typeclasses for each newtype. Without it, you wouldn't be able to directly compare two IDs, you'd have to extract the values first. Another important typeclass to derive is Ord, which lets you use the <, >, <=, and >= operators, as well as a whole bunch of list functions like sort.

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.

Resources