
Imagine the following data type: data Person = Child { childAge :: Int, school :: School } | Adult { adultAge :: Int, job :: Job } Both the alternatives share an "age" field, but in order to access it we are obliged to match all the constructors: personAge :: Person -> Int personAge (Child {childAge = age}) = age personAge (Adult {adultAge = age}) = age Is there a way to define a common field in a data type (somehow like inheritance in the OOP world)? It would be practical if we could define "age :: Int" as a common field and do something like: personAge (_ {age = age}) = age An alternative solution could be extracting the varying fields in a different type: data WorkingPerson = Child School | Adult Job data Person = Person { age :: Int, workingPerson :: WorkingPerson } Or to make Person a type class (which would probably require existential types in order to be useful). But the first one looks simpler and more natural. Does this feature exist (maybe in other forms) and, if not, is there a reason? Would it make sense to have a GHC extension (or even to make a Template Haskell hack) for this feature?

Hi Lorenzo, I don't think that the first one is more natural, because you could define a child like: Child { childAge = 99, ... }, but you also might have someone quite old still studying, so I might go with something like: data Occupation = Student School | Employee Job data Person = Person { age :: Int, occupation :: Occupation } But if you really want the first one, then you're most likely only one "lens"[1] away from a solution: age :: Lens' Person Int age = lens getAge setAge where getAge Child { childAge = age } = age getAge Adult { adultAge = age } = age setAge (c@Child {}) newAge = c { childAge = newAge } setAge (a@Adult {}) newAge = a { adultAge = newAge } To get the age of a person you could now write: person ^. age And to modify it: person & age .~ 22 But I wouldn't prefer the lens solution. Greetings, Daniel [1] https://hackage.haskell.org/package/lens

In addition to the comments made by Daniel, as a general rule, you don’t want a sum type with selectors functions as your example stands, because you can end up with compiling programs like this: module Main where main :: IO ()main = let p = Child 10 age = adultAge p in print age this craps out with an error at runtime.

If you could have a single polymorphic "age" function for all types having an age, you could never have this runtime error, because the compiler would infer which "age" we are referring to. The GHC OverloadedRecordFields extension that Michael linked seems to do what I am looking for (http://www.well-typed.com/blog/84/).

On Tue Apr 29 2014 at 16:42:40, Lorenzo Tabacchini lortabac@gmx.comhttp://mailto:lortabac@gmx.comwrote: If you could have a single polymorphic "age" function for all types
having an age, you could never have this runtime error, because the compiler would infer which "age" we are referring to. The GHC OverloadedRecordFields extension that Michael linked seems to do what I am looking for (http://www.well-typed.com/blog/84/).
This is accurate. In the meantime avoid partiality as much as possible. If you must have an age accessor, better to define the lens explicitly. module Main where import Control.Applicativeimport Control.Lens main :: IO ()main = let p = Child 10 in print $ p ^. age data Person = Child Int | Adult Int age :: Lens' Person Intage k (Child x) = Child <$> k xage k (Adult x) = Adult <$> k x Yeah it’s boilerplate. You could makeLenses and then not export the automatically generated selector functions.

On 4/29/2014 10:55 AM, Benjamin Edwards wrote:
|module Mainwhere
import Control.Applicative import Control.Lens
main ::IO () main =let p =Child 10 in print $ p ^. age
data Person =Child Int |Adult Int
age ::Lens' Person Int age k (Child x) =Child <$> k x age k (Adult x) =Adult <$> k x |
Hmm, what does the <$> mean here? I know it for functor application, but I don't understand how that works in this example.

Hmm, what does the <$> mean here? I know it for functor application, but I don't understand how that works in this example.
It's a synonym for fmap. Lens' is a type synonym. age really has function type forall Functor f. (Int -> f Int) -> Person -> f Person. Applying k x gives me f Int and fmapping the constructor gives me f Person. If you want to read an excellent but challenging article on why lenses work this way click here https://www.fpcomplete.com/user/tel/lenses-from-scratch. Otherwise the signature should give you enough to be able to play type tetris to figure out where the fmap comes in (hopefully). This isn't super beginner friendly, but it's where haskell seems to be headed these days. Ben

On 2014-04-28 20:26, Lorenzo Tabacchini wrote:
Imagine the following data type:
data Person = Child { childAge :: Int, school :: School } | Adult { adultAge :: Int, job :: Job }
Both the alternatives share an "age" field, but in order to access it we are obliged to match all the constructors:
personAge :: Person -> Int personAge (Child {childAge = age}) = age personAge (Adult {adultAge = age}) = age
Is there a way to define a common field in a data type (somehow like inheritance in the OOP world)?
Since you already have dedicated types for the school and the job, why not have a descriptive name for the age as well so that you can drop the accessor functions altogether? E.g. type Age = Int data Person = Child Age School | Adult Age Job If needed, you could of course still define age :: Person -> Age age (Child x _) = x age (Adult x _) = x but you may also find that you don't even need such a function in the first place, pattern matching may do the job just fine: mayRideRollerCoaster :: Person -> Bool mayRideRollerCoaster (Child age _) = age > 12 mayRideRollerCoaster _ = True -- Frerich Raabe - raabe@froglogic.com www.froglogic.com - Multi-Platform GUI Testing

On 04/28/2014 02:26 PM, Lorenzo Tabacchini wrote:
Would it make sense to have a GHC extension (or even to make a Template Haskell hack) for this feature?

http://www.well-typed.com/blog/84/ http://www.well-typed.com/blog/84/
Thank you! It's the kind of solution I was looking for. Apparently I'll have to wait for GHC 7.10.

Hi, On 2014-04-28 19:26, Lorenzo Tabacchini wrote:
Imagine the following data type:
data Person = Child { childAge :: Int, school :: School } | Adult { adultAge :: Int, job :: Job }
Both the alternatives share an "age" field, but in order to access it we are obliged to match all the constructors:
personAge :: Person -> Int personAge (Child {childAge = age}) = age personAge (Adult {adultAge = age}) = age Is there a way to define a common field in a data type (somehow like inheritance in the OOP world)?
Yes, there is: data Person = Child { personAge :: Int, school :: School } | Adult { personAge :: Int, job :: Job } No extension needed! Thomas H

Yes, there is:
data Person = Child { personAge :: Int, school :: School } | Adult { personAge :: Int, job :: Job }
No extension needed!
Thanks! This helps reduce the number of field names, but it does not address the main problem, which is the obligation of pattern matching on all constructors. The Person type is just a simple example. If you have a lot of constructors repeating all of them without a real reason can be time-consuming and boring. I am aware that the real reason of the problem may be a wrong architecture, but I still have the feeling that my solution does have some use cases.

Thanks! This helps reduce the number of field names, but it does not address the main problem, which is the obligation of pattern matching on all constructors. The Person type is just a simple example. If you have a lot of constructors repeating all of them without a real reason can be time-consuming and boring.
I am aware that the real reason of the problem may be a wrong architecture, but I still have the feeling that my solution does have some use cases.
If you use a sum type, you *always* need to check every case. That is what you sign up for. If you want to express a commonality that should expressed in the type [1]. Otherwise if you extend Person with a constructor that has *no* notion of age then you cannot have a total function from Person -> Int. You would want something like a Prism or just a simple function from Person -> Maybe Int. Try to avoid non-total functions. That way lies madness. [1] as has already been suggested, this involves factoring out the things that are common from the things that aren't data SomeSum = Part1 String | Part2 Int | Part3 data Common a = Common Int a data Composite = Common SomeSum Now you can see that a function wanting the common Int must necessarily succeed when applied to Composite, but that there is no total function Composite -> String that accesses the String value held by Part1. I hope this is useful to you.

On 4/29/2014 12:42 AM, Lorenzo Tabacchini wrote:
Yes, there is:
data Person = Child { personAge :: Int, school :: School } | Adult { personAge :: Int, job :: Job }
No extension needed!
Thanks! This helps reduce the number of field names, but it does not address the main problem, which is the obligation of pattern matching on all constructors. The Person type is just a simple example. If you have a lot of constructors repeating all of them without a real reason can be time-consuming and boring.
I am aware that the real reason of the problem may be a wrong architecture, but I still have the feeling that my solution does have some use cases.
That reminds me of database design: Normalize! In this case, Daniel's suggestion is in line with the theory. With varying numbers of fields between the different kinds of people, it suggests making separate tables and joining them to produce the child or adult views. More generally, perhaps database schema rather than Object Oriented is a better place to transfer engineering skills from when it comes to designing the information model. —John
participants (7)
-
Benjamin Edwards
-
Daniel Trstenjak
-
Frerich Raabe
-
John M. Dlugosz
-
Lorenzo Tabacchini
-
Michael Orlitzky
-
Thomas Hallgren