haskell variant of records with different field names - haskell

Given this:
data Foo = Bar { name :: String } | Baz { nickname :: String }
Both the functions name and nickname seem to be of type Foo -> String:
:t name
name :: Foo -> String
:t nickname
nickname :: Foo -> String
However, the definitions are incomplete since both of the following expressions will raise pattern match errors:
name $ Baz { nickname = "Bob" }
nickname $ Bar { name = "Fred" }
Is it possible to complete the definitions of name and nickname, i.e. something like:
name Baz { nickname = n } = ...
nickname Bar { name = n } = ...
Trying this in hugs yields errors like "Multiple declarations for variable name".

No, it's not possible. The field names are top-level functions with respect to scope and thus cannot be redefined or extended. It's as impossible to make them total functions as it is to make head one.

Try the -XDataKinds extension, it allows you to promote data constructors to be kinds.
Note that you'll need GHC 7.4.1, which was released just 12 days ago. This is a good time to be asking this question!

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'

Silly duplicated record fields error

Consider the following:
{-# LANGUAGE DuplicateRecordFields #-}
data A = A { name :: String }
data B = B { name :: String }
main = print $ name (A "Alice")
When compiled, I get the following message (on GHC 8.0.2)
duplicatedrecords.hs:7:16: error:
Ambiguous occurrence ‘name’
It could refer to either the field ‘name’,
defined at duplicatedrecords.hs:5:14
or the field ‘name’, defined at duplicatedrecords.hs:3:14
But if I modify the main line as follows:
main = print $ name ((A "Alice") :: A)
Compilation proceeds successfully.
Why is this? The type signature :: A seems redundant to me, as surely the A constructor makes it clear to the compiler that (A "Alice") is of type A. But for some reason it makes a difference. Why is this and is there a way I can get this to compile without littering extra type signatures everywhere?
Note:
It's worth noting that the following compiles fine:
data A = A { a_name :: String }
data B = B { b_name :: String }
class Name t where
name :: t -> String
instance Name A where name = a_name
instance Name B where name = b_name
main = print $ name (A "Alice")
We can even go further as follows, allowing different result types:
{-# LANGUAGE TypeFamilies #-}
data A = A { a_name :: String }
data B = B { b_name :: Int }
class Name t where
type family T t
name :: t -> T t
instance Name A where
type T A = String
name = a_name
instance Name B where
type T B = Int
name = b_name
main = print $ name (A "Alice")
It seems like GHC just has to mechanically add a class for each unique record name and an instance for each record in each data type. This will mean however that name x == name y not implying that the types of x and y are the same but I'd expect that when using this extension anyway.
Just wondering if there's anything tricky I'm missing here regarding the implementation or that it just needs someone to do it?
-XDuplicateRecordFields currently doesn't infer types from arguments.
See GHC user guide section about this extension.
However, we do not infer the type of the argument to determine the datatype, or have any way of deferring the choice to the constraint solver. Thus the following is ambiguous:
But things are improving. So we might expect and finally get desired behavior:
https://prime.haskell.org/wiki/TypeDirectedNameResolution

Why is the restriction on newtype?

I read that "newtype has exactly one constructor with exactly one field inside it." Is this restriction adds any advantage? If the value constructor is limited with only one field, why can't i use the field directly in my code, instead of wrapping it with newtype?
newtype is a tool for creating data abstraction that has no runtime cost.
What do I mean by abstraction?
Suppose you have:
greetPerson :: String -> String -> String
greetPerson greeting name = greeting ++ " " ++ name
greetPerson "Hello" "Mike" => "Hello Mike"
This works fine, but it opens opportunities for misuse:
greetPerson "Mike" "Hello" => "Mike Hello"
The problem is that you're using the same type everywhere (String), carrying no semantic meaning. Let's use a newtype:
newtype Name = Name String
greetPerson :: String -> Name -> String
greetPerson greeting (Name name) = greeting ++ " " ++ name
greetPerson "Hello" (Name "Mike") => "Hello Mike"
We end up with the same functionality, but now the type signature carries more meaning and the compiler can tell us when we misuse it.
What do I mean by no runtime cost?
The newtype from my example exists only at the type level and the compiler generates exactly the same code as if I used String throughout.
This hints at why newtype is only allowed for one constructor with one field.
Imagine you tried to make newtype work for more than one constructor. How would you distinguish which one you have at runtime? You'd have to store some additional information.
Same with more than one field. You'd need some way to bundle two fields together.
All of them add some runtime cost which newtype promises not to do.
If you want more than one field or more than one constructor simply use data:
data Foo = Bar String | Baz Int Bool

Mandatory Maybes in the type system

I have something similar to the following
data A = A
{ id :: Integer
, foo :: Maybe String
, bar :: Maybe String
, baz :: Maybe String
}
This data is coming in to my service as JSON. This request is only considered valid when one or more of foo, bar, or baz are given. Is there a better way to express this within Haskell's type system?
Note: Unfortunately I am unable to make this separate requests. I'm just following a defined protocol.
http://hackage.haskell.org/package/these-0.4.2/docs/Data-These.html
import Data.These
data A = A
{ id :: Integer
, fooBarBaz :: These Foo (These Bar Baz)
}
type Foo = String
type Bar = String
type Baz = String
If it is not mandatory to have three separate fields with foo,bar and baz, I'd go with this, NonEmpty guarantees that there is at least one element, though there can of course be more.
import Data.List.NonEmpty
data Impression = Banner String | Video String | Native String
data A = A
{ id :: Integer
, fooBarBaz :: NonEmpty Impression
}
I would use a Map Field String with data Field = Foo | Bar | Baz (this can easily be replaced with String if needed, and then have:
data A = A
{ id :: Integer
, fields :: Map Field String
}
Now checking for the validity condition is as simple as:
isValid :: A -> Bool
isValid = not . Map.null . fields
Expanding on ʎǝɹɟɟɟǝſ's suggestion to use a map: there's also a type specifically for non-empty maps. (Note however that this sort of clashes with the more popular nonempty-list type from the semigroups library.)
import qualified Data.NonEmpty.Map as NEM
data Field = Foo | Bar | Baz
data A = A { id :: Integer
, fields :: NEM.T Field String
}
Consider giving one branch for each possible required field:
data A
= Foo
{ foo :: String
, barM, bazM :: Maybe String
}
| Bar
{ bar :: String
, fooM, barM :: Maybe String
}
| Baz
{ baz :: String
, fooM, barM :: Maybe String
}
It's a fair bit of boilerplate, but it's very direct and quite clear about what's required.

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

Resources