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

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

Related

What is the type keyword in Haskell

Stumbled on the type keyword in Haskell:
type Item = String
but not sure what it does, how to use it or how it is different from data. The online google search has been of no help.
I tried implementing it in a code like this:
import System.IO
main = do
putStrLn "Hello, what's your name?"
type Item = String
let test :: Item
test = "chris"
putStrLn test
but I got an error
parse error on input ‘type’
Please in a lay man's term what is type and how can it be used and how is it different from data?
It is a type alias. It means that you can use Item in your code where you can use String instead.
A type alias is often used when you for example want to give a name to more complex types. For example:
import Data.Map(Map)
type Dictionary = Map String String
here you thus can use Dictionary instead of each time writing Map String String.
It is furthermore often used if you want to specify that you are working with Items, the alias is then used in the type signature and in the documentation, which is often better than writing String.
It is also used if you do not yet know what type you will use for a specific object. By using a type alias, you can the work with Item, and later if you change your made define a type for Item or make it an alias of another type. This makes it more convenient to change the types.
I tried implementing it in a code like this:
import System.IO
main = do
putStrLn "Hello, what's your name?"
type Item = String
let test :: Item
test = "chris"
putStrLn test
A type alias is defined at the top level, so not in a do block, that would make a type definition locally scoped. While, like #moonGoose says, there are some proposals to make type definitions more locally scoped, currently it is not the case.
You can define the type alias like:
import System.IO
type Item = String
main = do
putStrLn "Hello, what's your name?"
let test :: Item
test = "chris"
putStrLn test
type A = B
means exactly the same as
typedef B A
in C or C++, and it behaves basically the same as simply
a = b
except that A and B are type-level entities, not value-level ones. For example
Prelude> type A = Int
Prelude> :i A
type A = Int -- Defined at <interactive>:1:1
Prelude> a = 37
Prelude> a
37
Because now A = Int, I can then use the type identifier A exactly everywhere I could also use Int directly:
Prelude> 37 :: Int
37
Prelude> 37 :: A
37
and even
Prelude> (37 :: Int) :: A
37
Note that there is no type conversion going on here, like you might have in other languages. Int and A are simply different names for the same type, so annotating with both is merely a tautology.
Contrast this with data (or newtype), which define a new, separate type which just happens to contain the, well, data of the specified type.
Prelude> data A' = A' { getA :: Int }
Prelude> (37 :: Int) :: A'
<interactive>:12:2: error:
• Couldn't match expected type ‘A'’ with actual type ‘Int’
• In the expression: (37 :: Int) :: A'
In an equation for ‘it’: it = (37 :: Int) :: A'

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

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

Lens package with algebraic types

I was coding with with the lens package. Everything was going fine until I tried to access a certain field on an algebraic type:
import Control.Lens
data Type = A { _a :: Char } | B
makeLenses ''Type
test1 = _a (A 'a')
test2 = (A 'a') ^. a
No instance for (Data.Monoid.Monoid Char)
arising from a use of `a'
Possible fix:
add an instance declaration for (Data.Monoid.Monoid Char)
In the second argument of `(^.)', namely `a'
In the expression: (A 'a') ^. a
In an equation for `test2': test2 = (A 'a') ^. a
I could just go with _a, but the datatype in my real program is much deeper and I kind of intended on using lens to lower the amount of work I have to do. I have been looking over the lens library but there's so much there, and I'm not sure if he's dealt with this scenario or it is just something the lens library doesn't support.
As a side note, if I actually use a monoid like String for the datatype instead of Char, it then compiles and gives the right answer, I have no idea why.
Edit: After reading hammar's comment, I tried this and this works:
test2 = (A 'a') ^? a
test3 = B ^? a
But it is kind of weird to get a maybe out of that for something that has to exist.
Just so that this is answered, my problem was that I had an algebraic type where some fields were in common between the different constructors but there was a couple fields that weren't shared would die in runtime if I tried to use them.
data Exercise =
BarbellExercise {
name :: String,
weight :: Int,
reps :: Int
} |
BodyWeightExercise {
name :: String,
reps :: Int
}
exer1 = BarbellExercise "Squats" 235 15
exer2 = BarbellExercise "Deadlifts" 265 15
exer3 = BodyWeightExercise "Pullups" 12
exer4 = BarbellExercise "Overhead Press" 85 15
workout = [exer1, exer2, exer3, exer4]
test = do
mapM_ displayExercise workout
where
displayExercise x = putStrLn $ "Exercise: " ++ (name x) ++ " You must perform " ++ (show $ reps x) ++ "#" ++ (show $ weight x)
This compiles but dies runtime if I make the mistake of using the weight function. Understandable mistake. When lenses uses template haskell to generate instances it notices this and changes its behavior to prevent a mistake. You could remove the field accessors but in my case most of the fields were the same between datatypes. Here's how I should have written the data type once I noticed the fields did not match up:
data Exercise =
BarbellExercise
String -- ^ name
Int -- ^ reps
Int -- ^ weight
|
BodyWeightExercise
String -- ^ name
Int -- reps
name :: Exercise -> String
name (BarbellExercise n _ _) = n
name (BodyWeightExercise n _) = n
reps :: Exercise -> Int
reps (BarbellExercise _ r _) = r
reps (BodyWeightExercise _ r) = r
By doing it this way, while it is a little less clean, the error are caught at compile time. By forcing me to write the functions myself I would notice any partial functions as I was writing them.
I do kind of wish ghc would have warned me. It seems like it would be really easy for it to detect such a thing.

searching in sorted Indexes of tuple list by bubble sort in haskell

I choice binary search for index of array of record ascasestudy for my graduate research by c++ and haskell I write the c++ code and work and now I work for the haskell one
import Data.List
data BookInfo = Book Int String [String]
deriving (Show)
--Entering the variables
entering :: Int String [String]-> Book
entering id name subject= Book id name subject
--make an idex of the array of tuples
index :: [Book]->[Int]
index [m] = getID (Book id _ _ ) = id
main :: IO ()
main = do
putStrLn "Please enter your book id,name,Subject"
Book inpStr <- getLine
putStrLn print r
-- BubbleSort using map
bubbleSort ::(Ord x) => [x] -> [x]
bubbleSort (x':xs) = if x>x' then x': bubbleSort(x:xs)
else
if x<x' then x: bubbleSort(x':xs)
else
x: bubbleSort(X':xs)
bubble ::[a] -> [a]
bubble [a] = map bubbleSort [a]
--Make index for the array
indexsort(ord a)::[int]->[Int]
indexsort a=bubble a
--Make the list of tuples
addBooks2List Book->[Book]->[Book]
addBooks2List b m=b:entering b':m
--binarysearch
binarysearch [Int]->Int->Int->Int->Int
a=bubble x
binaryseach a i m j=
let u=(i+m)/2
if a[u]=j then u
if a[u]>j then binaryseach a i u-1 j
else
if a[u]<j then binarysearch a u+1 m j
if i=m "Not found"
--printing the tuple that has the searched id
print::Int->Book
print r= Book r y z
r=binaryseach m 0 (length m)
it made an error at 8:3 parse error on input 'entering' what this error mean? and how I can correct it ?
For one thing, the type signature for entering should read
entering :: Int -> String -> [String] -> BookInfo
rather than entering :: Int String [String]-> Book—but that's a type error, not a parse error.
Perhaps your indentation is wrong, but that's hard to tell without having your code available verbatim. Remember: in Haskell, as opposed to languages such as C and Java, the layout of your code matters.
Anyway, the code, as you have posted it, is far from a working solution for your problem. You may want to take a few steps back and study how to write elementary functions in Haskell and then, later, how to glue them together in order to get at more involved programs. I really doubt whether learning the language by trying to convert snippets of C++ into Haskell has a high chance of success. Just my two cents, though...
There are so many problems in your code, you best start with a single function and grow your program slowly and see that you get the incremental pieces compiled. Starting with this:
entering :: Int String [String]-> Book
entering id name subject= Book id name subject
First, Book is not a type but a data constructor, the type is BookInfo. Then, you're missing the arrows (as dblhelix pointed out). So it should read:
entering :: Int -> String -> [String]-> BookInfo
entering id name subject= Book id name subject
This will compile. However, entering now is just the same as Book. Anyway, continue with adding the next function and getting it compiled and so on.
If we go on,
index :: [Book]->[Int]
index [m] =
The definition is missing (what's on the right-hand side of =?), and [m] will only match a lists with a single element which is probably not what you want. Complete this function, or comment it out and continue with the rest. Apparently you don't use it at all currently. And so on.

Resources