
On Sat, Feb 25, 2012 at 3:54 PM, Barney Hilken
After more pondering, I finally think I understand what the DORFistas want. Here is an example:
You want to define records which describe people, and include (among other things) a field called "name". There might be several different record types with a name field, depending on whether the record refers to a customer, an employee, a business contact etc., but in each case "name" is the name of the person to which the record refers. You then write various functions which assume this, such as
spam :: Has r "name" String => r -> String spam r = "Dear " ++ r.name ++ "\nHave you heard..."
Now I want to define records which describe products, and I also use a field "name" in the same way, except that it is the brand name of the product. I also define functions such as
offer :: Has r "name" String => r -> String offer r = "Reduced! " ++ r.name ++ " 50% off!"
It doesn't make any sense to apply your functions to my records or vice-versa, but because we both chose the same label, the compiler allows it. Putting the code in separate modules makes no difference, since labels are global.
Exactly!
Here is a simple solution, using SORF:
The real problem is that the polymorphism of spam and offer is too general. We should each define new classes
class Has r "name" String => HasPersonalName r class Has r "name" String => HasBrandName r
and make each of our record types an instance of this class
instance HasPersonalName EmployeeRecord instance HasPersonalName CustomerRecord instance HasBrandName FoodRecord
then we can define functions with a more specific polymorphism
spam :: HasPersonalName r => r -> String spam r = "Dear " ++ r.name ++ "\nHave you heard..."
offer :: HasBrandName r => r -> String offer r = "Reduced! " ++ r.name ++ " 50% off!"
Now there is no danger of confusing the two uses of "name", because my records are not instances of HasPersonalName, they are instances of HasBrandName. You only use the class Has if you really want things to be polymorphic over all records, otherwise you use the more specific class.
This seems to me a much simpler approach than building the mechanism in to the language as DORF does, and it's also more general, because it isn't hard linked to the module system. Does it have any disadvantages?
I can't tell offhand whether it has any drawbacks with respect to expressiveness. It seems to be a good solution to the stated problem, so thank you for proposing it. My objection is that I'm not sure if there is ever a case where "you really want things to be polymorphic over all records". There is nothing (as far as I know) analogous to this kind of implicit name-based polymorphism anywhere in Haskell. It doesn't seem like the Haskell way to have the less safe thing as the one that's default and convenient, and to allow the programmer to layer a more-safe thing on top of it if he or she wants to. It seems more like the Haskell way to have the safer thing be the default and to require extra work if you want to do something less safe*. In this specific case, is there any actual use case for global implicitly-polymorphic-by-name record fields, where that is actually what you want, and where the DORFish way which is analogous to classes-and-instances wouldn't be appropriate? * (Now granted, if pure code versus unsafePerformIO is white versus black, then this is shade-of-gray versus slightly-darker-shade-of-gray, but the principle is the same.)