Editing data type in Haskell - 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.

Related

Trying to add a tuple in front of a list in Haskell but receiving error?

type Name = String
type PhoneNumber = Int
type Person = (Name, PhoneNumber)
type PhoneBook = [Person]
add :: Person -> PhoneBook -> PhoneBook
add (a,b)
add (a,b) ++ []
I'm trying to add an entry in front of the list but its giving me an error
Parse error: module header, import declaration
or top-level declaration expected.
|
30 | add (a,b) : xs
| ^^^^^^^^^^^^^^
where am I going wrong?
You are trying to add a list and a tuple. But ++ is defined for adding lists to lists, not for adding tuples to lists. Haskell is very strict about types, so it won't let you do this.
You should make the tuple a list:
add [(a,b)] ++ []
Or, as was suggesteed by Robin Zigmond in their comment, use the : operator:
add (a,b) : []
You have another issue though - your function definition should contain an = operator:
add (a,b) [] = [(a,b)]
This is how a function is defined in Haskell. On the lefthand side, it tells Haskell wat the inputs are. On the righthand side, it tells Haskell what they should be transformed into.
You may want to refer to Learn You A Haskell : Syntax in Functions for a nice introduction to defining functions in Haskell.
Based on your post you can run the following piece of code:
type Name = String
type PhoneNumber = Int
type Person = (Name, PhoneNumber)
type PhoneBook = [Person]
add :: Person -> PhoneBook -> PhoneBook
add (name, phoneNumber) phoneBook = (name, phoneNumber) : phoneBook
main = print $ add ("xyz", 987) (add ("abcd", 123) [])
which yields:
[("xyz",987),("abcd",123)]

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, ... }

Writing an OOP-style "setter" function in Haskell using record-syntax

I'm reading a tutorial on lenses and, in the introduction, the author motivates the lens concept by showing a few examples of how we might implement OOP-style "setter"/"getter" using standard Haskell. I'm confused by the following example.
Let's say we define a User algebraic data types as per Figure 1 (below). The tutorial states (correctly) that we can implement "setter" functionality via the NaiveLens data type and the nameLens function (also in Figure 1). An example usage is given in Figure 2.
I'm perplexed as to why we need such an elaborate construct (i.e., a NaiveLens datatype and a nameLens function) in order to implement "setter" functionality, when the following (somewhat obvious) function seems to do the job equally well: set' a s = s {name = a}.
HOWEVER, given that my "obvious" function is none other than the lambda function that's part of nameLens, I suspect there is indeed an advantage to using the construct below but that I'm too dense to see what that advantage is. Am hoping one of the Haskell wizards can help me understand.
Figure 1 (definitions):
data User = User { name :: String
, age :: Int
} deriving Show
data NaiveLens s a = NaiveLens { view :: s -> a
, set :: a -> s -> s
}
nameLens :: NaiveLens User String
nameLens = NaiveLens name (\a s -> s {name = a})
Figure 2 (example usage):
λ: let john = User {name="John",age=30}
john :: User
λ: set nameLens "Bob" john
User {name = "Bob", age = 30}
it :: User
The main advantage of lenses is that they compose, so they can be used for accessing and updating fields in nested records. Writing this sort of nested update manually using record update syntax gets tedious quite quickly.
Say you added an Email data type:
data Email = Email
{ _handle :: String
, _domain :: String
} deriving (Eq, Show)
handle :: NaiveLens Email String
handle = NaiveLens _handle (\h e -> e { _handle = h })
And added this as a field to your User type:
data User = User
{ _name :: String
, _age :: Int
, _userEmail :: Email
} deriving (Eq, Show)
email :: NaiveLens User Email
email = NaiveLens _userEmail (\e u -> u { _userEmail = e })
The real power of lenses comes from being able to compose them, but this is a bit of a tricky step. We would like some function that looks like
(...) :: NaiveLens s b -> NaiveLens b a -> NaiveLens s a
NaiveLens viewA setA ... NaiveLens viewB setB
= NaiveLens (viewB . viewA) (\c a -> setA (setB c (viewA a)) a)
For an explanation of how this was written, I'll defer to this post, where I shamelessly lifted it from. The resulting set field of this new lens can be thought of as taking a new value and a top-level record, looking up the lower record and setting its value to c, then setting that new record for the top-level record.
Now we have a convenient function for composing our lenses:
> let bob = User "Bob" 30 (Email "bob" "gmail")
> view (email...handle) bob
"bob"
> set (email...handle) "NOTBOB" bob
User {_name = "Bob", _age = 30, _userEmail = Email {_handle = "NOTBOB", _domain = "gmail"}}
I've used ... as the composition operator here because I think it's rather easy to type and still is similar to the . operator. This now gives us a way to drill down into a structure, getting and setting values fairly arbitrarily. If we had a domain lens written similarly, we could get and set that value in much the same way. This is what makes it look like it's OOP member access, even when it's simply fancy function composition.
If you look at the lens library (my choice for lenses), you get some nice tools to automatically build the lenses for you using template haskell, and there's some extra stuff going on behind the scenes that lets you use the normal function composition operator . instead of a custom one.

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].

Errorhandling and monads?

I'm trying to understand how to apply the Maybe-idiom from Haskel.. I'm reading http://en.wikibooks.org/wiki/Haskell/Understanding_monads/Maybe which shows that a lookup in a dictionary may return a Maybe and that this value propates through the >>= operator.
The example from the URL:
If we then wanted to use the result from the governmental database lookup in a third lookup (say we want to look up their registration number to see if they owe any car tax), then we could extend our getRegistrationNumber function:
getTaxOwed :: String -- their name
-> Maybe Double -- the amount of tax they owe
getTaxOwed name =
lookup name phonebook >>=
(\number -> lookup number governmentalDatabase) >>=
(\registration -> lookup registration taxDatabase)
Or, using the do-block style:
getTaxOwed name = do
number <- lookup name phonebook
registration <- lookup number governmentalDatabase
lookup registration taxDatabase
Question:
How do I deal with error handling? I think most code will benefit from telling where things went wrong. Rather than just reporting "couldn't find John Doe in either phonebook or governmentalDatabase" it should report which ressource had problems.
You could use the monad instance for Either String, which is essentially defined as
instance Monad (Either String) where
fail msg = Left msg
return x = Right x
Left msg >>= k = Left msg
Right x >>= k = k x
(The actual definition is a bit more involved.)
If we then define dictionaries as pairs consisting of a label and a lookup table
type Dict a b = (String, [(a, b)])
phonebook' :: Dict String Int
phonebook' = ("phone book", phonebook)
governmentalDatabase' :: Dict Int Int
governmentalDatabase' = ("governmental database", governmentalDatabase)
taxDatabase' :: Dict Int Double
taxDatabase' = ("tax database", taxDatabase)
where phonebook, governmentalDatabase, and taxDatabase are as you had them defined before, we can use an alternative monadic lookup function that returns its result in the Either String-monad:
lookup' :: (Eq a, Show a) => a -> Dict a b -> Either String b
lookup' key (descr, table) = case lookup key table of
Nothing -> Left ("couldn't find " ++ show key ++ " in " ++ descr)
Just val -> Right val
Illustrating the power of monads, the only thing that now needs to change in your client function is the type signature:
getTaxOwed :: String -- their name
-> Either String Double -- either an error message
-- or the amount of tax they owe
getTaxOwed name = do
number <- lookup' name phonebook'
registration <- lookup' number governmentalDatabase'
lookup' registration taxDatabase'
Running this function on an unknown name gives:
> getTaxOwed "Joe"
Left "couldn't find \"Joe\" in phone book"
The Maybe data type has only the value "Nothing" for signaling an error. If you want to return a specific error message i suggest the data type "Either", which can return either a "Left a" or "Right a" value. Read more about how to use that at http://learnyouahaskell.com/for-a-few-monads-more#error

Resources