IO action is repeated after changin its internal value - io

I try printing a value (of the showable Person type) and then changing the return type from IO () to IO Person.
import qualified Data.Text as T
data Person = Person
{ firstName :: T.Text
, lastName :: T.Text
} deriving Show
writePerson :: Person -> IO Person
writePerson p = const p <$> print p
Expected Result:
Person {firstName = "Maria", lastName = "do Rosario"}
Actual Result:
Person {firstName = "Maria", lastName = "do Rosario"}
Person {firstName = "Maria", lastName = "do Rosario"}

You are running this in ghci. The first line is the output of the call to print. The second line is the interpreter showing the return value of the call to writePerson. They are identical because you pass p as the argument to both const and print.

Related

Force keyword arguments in Haskell record init

Can I force the use of keywords only when initializing a record in Haskell?
data Person
= Person
{
name :: String,
idnum :: String
}
deriving( Show )
main = putStrLn $ show $ Person "oren" "9200"
-- I want to only allow such initializations:
-- main = putStrLn $ show $ Person { name = "moish", idnum = "7400" }
(This is especially useful when two fields have the same type)
As far as I know, no.
Consider maybe the following solution?
newtype Name = Name String
newtype Idnum = Idnum String
data Person = Person { name :: Name, idnum :: Idnum }
Another possibility, worse in my opinion, is this:
module A (Person, name, idnum) where
data Person = Person
{ _name :: String
, _idnum :: String
}
name :: String -> (Person -> Person)
name n p = p { _name = n }
idnum :: String -> (Person -> Person)
idnum n p = p { _idnum = n }
emptyPerson :: Person
emptyPerson = Person "" ""
# in another module
module B
import A (Person, name, idnum)
myPerson = name "myname" . idnum "myidnum" $ emptyPerson
In this case there's no guarantee that both name and idnum get a value.
The fact that Person can always be used as a 'plain function' may turn out to be very useful. If you have, for instance, getName :: IO String and getIdnum :: IO String, then combining these to form a getPerson :: IO Person is concise: getPerson = Person <$> getName <*> getIdnum. This is only possible because we don't use record syntax here!

Haskell - Produce a List of People from a list of lists

The List of Lists contains the following entries.
[
["John","Doe","38\r"],
["Jane","Doe","35\r"]
]
The Person data type is constructed as follows:
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Show)
I am trying to make a list of Persons in Haskell such that the final Person list will look as follows:
[
Person {firstName = "John", lastName = "Doe", age = 38},
Person {firstName = "Jane", lastName = "Doe", age = 35}
]
Any help is appreciated! Please and thank you!
So we need a function of this type:
f :: [[String]] -> [Person]
And there's a function which can help us - map
stringsToPerson :: [String] -> Person
f = map stringsToPerson
Now, a fundamental problem becomes clear(er) - stringsToPerson is not type-safe. A user could give it an empty list and the function has no choice but to return an error. Better types would be:
stringsToPerson :: (String, String, Int) -> Person
stringsToPerson :: [String] -> Maybe Person
Buuut assuming you want to stick with the type you chose, you can do it with pattern matching:
stringsToPerson [firstName, lastName, age] = Person firstName lastName (read age)
Your may do as follows;
data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Show)
mapper :: [[String]] -> [Person]
mapper = map makePerson
where makePerson [f,l,a] = Person f l $ read a
*Main> mapper [["John","Doe","38\r"], ["Jane","Doe","35\r"]]
[Person {firstName = "John", lastName = "Doe", age = 38},Person {firstName = "Jane", lastName = "Doe", age = 35}]

*** Exception: Prelude.read: no parse

I am new to Haskell, I wrote this small script but I am having this read: no parse Exception, anyone could help me?
thanks
import System.Environment
import Data.List
data ContactInfo = Contact { name :: String
, surname :: String
, mobile :: String
} deriving (Read, Show)
fromDt :: [String] -> ContactInfo
fromDt ds = read $ "Contact " ++ (Data.List.concat $ Data.List.intersperse " " $ Data.List.map show ds)
main = do
let val = fromDt ["John","Smith","223 455 2703"]
putStrLn ("Name: " ++ name val ++ ", " ++ surname val)
Using read is horrible for that task, simply use the constructor Contact.
fromDt :: [String] -> ContactInfo
fromDt [n,s,m] = Contact n s m
Note that you will still get an error if the list you pass to fromDt is not 3 cells long. I would simply avoid defining this fragile function and use the constructor Contact directly wherever you would call fromDt.
When you define a data type using record syntax, the derived read instance requires the full record syntax - i.e. you must pass a string like
ContactInfo { name = "...", surname = "...", mobile = "..." }
to read to obtain a ContactInfo value. A string like:
ContactInfo "..." "..." "..."
will result in a no parse exception. Here is a quick demo:
data ABC = ABC { a :: Int, b :: Int, c :: Int }
deriving (Show, Read)
test1 :: ABC -- throws no parse exception
test1 = read "ABC 1 2 3"
test2 :: ABC -- works
test2 = read "ABC { a = 1, b = 2, c = 3 }"

accessing the property of a data type

Here is my code:
data Person = Person {name :: String}
greet :: Person -> String -> String
greet person lastName =
"Hi my name is " ++ name ++ ", last name: " ++ lastName
This is my interpretation of what I've written here: There is a data type Person for which there exists a method name that returns a string.
There also exists a function greet which takes in a Person and a String and returns a different String. However, the greet method calls the name method of the Person data type to concatenate this string.
However when I compile this code, I get this error:
Couldn't match expected type `[Char]' with actual type `Person -> String'
Why is this happening?
Note that name is a function you get for free from Record syntax. You can see it's type in ghci:
λ> :t name
name :: Person -> String
You are getting an error because you are trying to apply ++ function to a function named name instead of a String. So your code should be like this:
greet :: Person -> String -> String
greet person lastName = "Hi my name is " ++ name person ++ ", last name: " ++ lastName
#Sibi's answer is correct, but with RecordWildCards you can get close to your original:
{-# LANGUAGE RecordWildCards #-}
data Person = Person {name :: String}
greet :: Person -> String -> String
greet Person{..} lastName =
"Hi my name is " ++ name ++ ", last name: " ++ lastName

How can I use read on a string that is not double quoted?

I'm reading values from in from a console using readLn.
I'd like to write a function:
requestValue :: String -> IO a
requestValue s = do
putStrLn $ "Please enter a new value for " ++ s
readLn
I'd then be able to do, for example,
changeAge :: Person -> IO Person
changeAge p = do
age' <- requestValue "age"
return $ p { age = age'}
changeName :: Person -> IO Person
changeName p = do
name' <- requestValue "name"
return $ p { name = name'}
The problem I have is that the read instance of String seems to require the string to be in quotes. I don't want to have to enter "Fred" in the console to change name when I really only want to type in Fred.
Is there an easy way to do this that keeps requestValue polymorphic?
Since you want to add your own custom read behavior for user names, the way to do that is to actually write a new instance for readings names. To do that we can create a new type for names:
import Control.Arrow (first)
newtype Name = Name { unName :: String }
deriving (Eq, Ord, Show)
and write a custom read for it:
instance Read Name where
readsPrec n = map (first Name) . readsPrec n . quote
where quote s = '"' : s ++ ['"']
this is the same as the read instance for strings, but we first quote the string, after reading it in.
Now you can modify your Person type to use Name instead of String:
data Person = Person { age :: Int
, name :: Name } deriving Show
and we're in business:
*Main> changeName (Person 31 (Name "dons"))
Please enter a new value for name
Don
Person {age = 31, name = Name {unName = "Don"}}
You want getLine, not readLn.

Resources