Haskell - Type conversion? - haskell

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

Related

How to perform a query on a list of a custom type using map and set?

I am trying to figure out how one would be able to use map and set to generate a list based on a custom type.
For example 'type Review' which consists of: type nameOfReviewer = String, type nameOfTool = String, type numberOfStars = Int.
How would I get a list of the names of the people that have left a review so that it fits that definition.
I have tried using Set.union but haven't had any luck.
module Reviews where
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Map (Map)
import qualified Data.Map as Map
type nameOfReviewer = String
type nameOfTool = String
type numberOfStars = Int
type Review = (nameOfReviewer, nameOfTool, numberOfStars)
-- list of people that have left a review.
reviewers :: [Review] -> [nameOfReviewer]
reviewers rl = ???
To write a custom type, you need to use the data or newtype keyword. newtype is only for types with a single constructor and a single field, so we'll use data.
data Review = Review
{ nameOfReviewer :: String
, nameOfTool :: String
, numberOfStars :: Int
}
Should the number of stars really be any Int? Well, probably not, because it likely has to be between 1 and 5, or 0 and 10, or something. So that one deserves its own type.
newtype Stars = Stars Int
mkStars :: Int -> Maybe Stars
mkStars n
| 1 <= n && n <= 5
= Just (Stars n)
| otherwise
= Nothing
getStars :: Stars -> Int
getStars (Stars stars) = stars
data Review = Review
{ nameOfReviewer :: String
, nameOfTool :: String
, numberOfStars :: Stars
}
Since Stars is a full-fledged type, you can export it abstractly, forcing users to use mkStars and getStars to interact with it, thereby maintaining the star number rules.

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

Why doesn't GHC give a compile time warning for the "No match in record selector" exception?

When I run this buggy code...
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
getAge :: Person -> Int
getAge p = pAge p
getName :: Person -> String
getName p = pName p
main :: IO ()
main = do
let p1 = Kid "fred" 5
p2 = Adult "john"
ps = [p1, p2]
names = map getName ps
ages = map getAge ps
putStrLn $ "names: " ++ show names
putStrLn $ "ages: " ++ show ages
... I get this in ghci:
names: ["fred","john"]
ages: [5,* * * Exception: No match in record selector pAge
I know how to avoid this error, but I'm wondering why compiling with "ghc -Wall" didn't warn me about this problem. Is there another tool that can help me to prevent this type of error?
Is there [a] tool that can help me to prevent this type of error?
No, but there could be.
As you know, record syntax automatically generates getters with the same name as the attributes you define. Therefore the code
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
creates the functions pName :: Person -> String and pAge :: Person -> Int. Now, suppose Haskell had subtyping. If it did, then Kid could be a subtype of Person, and pAge could have the more appropriate type Kid -> String. However, Haskell does not have subtyping, and there is therefore no Kid type.
Now, given that Person -> String is the most specific type we can give to pAge, why not warn that pAge is a partial function at compile time? Let me divert the question by referring to the List example
data List a = Cons { head :: a, tail :: List a } | Empty
In this example, head and tail are partial functions: the two components of a non-empty list, but (due to Haskell's lack of subtyping) meaningless accessors on the empty list. So, why no warning by default? Well, by default, you know the code you have written. The compiler doesn't provide warnings if you use unsafePerformIO, because you're the programmer here, you're expected to use such things responsibly.
So tl;dr: if you want the warning here:
getAge :: Person -> Int
getAge p = pAge p
then you're out of luck, because the type system does not have enough information to deduce that this is a problem.
If you want the warning here:
data Person = Adult | Kid { pAge :: Int }
then I'm sure it would be trivial to implement: just check that a given field exists in some constructors but not others. But I do not foresee this warning being widely useful for everyone; some might complain that it would be just noise.
I'd be surprised if http://community.haskell.org/~ndm/catch/ doesn't pick this up.
It does, since 8.4, with -Wpartial-fields.
https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-fields

Haskell introspecting a record's field names and types

Based on a recent exchange, I've been convinced to use Template Haskell to generate some code to ensure compile-time type safety.
I need to introspect record field names and types. I understand I can get field names by using constrFields . toConstr :: Data a => a -> [String]. But I need more than the field names, I need to know their type. For example, I need to know the names of fields that are of type Bool.
How do I construct a function f :: a -> [(String, xx)] where a is the record, String is the field name and xx is the field type?
The type should be available, along with everything else, in the Info value provided by reify. Specifically, you should get a TyConI, which contains a Dec value, from which you can get the list of Con values specifying the constructors. A record type should then use RecC, which will give you a list of fields described by a tuple containing the field name, whether the field is strict, and the type.
Where you go from there depends on what you want to do with all this.
Edit: For the sake of actually demonstrating the above, here's a really terrible quick and dirty function that finds record fields:
import Language.Haskell.TH
test :: Name -> Q Exp
test n = do rfs <- fmap getRecordFields $ reify n
litE . stringL $ show rfs
getRecordFields :: Info -> [(String, [(String, String)])]
getRecordFields (TyConI (DataD _ _ _ cons _)) = concatMap getRF' cons
getRecordFields _ = []
getRF' :: Con -> [(String, [(String, String)])]
getRF' (RecC name fields) = [(nameBase name, map getFieldInfo fields)]
getRF' _ = []
getFieldInfo :: (Name, Strict, Type) -> (String, String)
getFieldInfo (name, _, ty) = (nameBase name, show ty)
Importing that in another module, we can use it like so:
data Foo = Foo { foo1 :: Int, foo2 :: Bool }
foo = $(test ''Foo)
Loading that in GHCi, the value in foo is [("Foo",[("foo1","ConT GHC.Types.Int"),("foo2","ConT GHC.Types.Bool")])].
Does that give you the rough idea?

Resources