[Haskell-cafe] Type Directed Name Resolution
Mark Lentczner
markl at glyphic.com
Fri Nov 12 01:13:20 EST 2010
My tuppence:
I feel like the main impetus for TDNR is the awkwardness of records, especially when there are multiple record types within a module (as there often are). Now, if one proceeds as one has to today, then one may find:
data Foo = Foo { fooName :: String, fooValue :: Double }
data Bar = Bar { barName :: String, barValue :: [String], barSubbars :: [Bar] }
Let us, for sake of argument, ignore that perhaps fooName and barName represent the same semantic concept and these should all somehow be refactored.
I suspect that the prime annoyance, the thing that makes us yearn for our old C/C++/Java/Python ways, is the tediousness of having to prefix all the field names with "foo" or "bar". Especially when the data type name is large, one ends up having to invent coding conventions one doesn't want to:
data ExecutionTraceSummary = ExecutionTraceSummary { etsStart :: Time; ... }
So imagine that we take the tedium out of typing all those prefixes by anointing some initial character, say the apostrophe, as a sort of name mangler:
data Foo = Foo { 'name :: String, 'value :: Double }
data Bar = Bar { 'name :: String, 'value :: [String], 'subbars :: [bar] }
data ExecutionTraceSummary = ExecutionTraceSummary { 'start :: Time, ... }
Now, to use them, perhaps we have to explicitly write the full form:
showFoo :: Foo -> String
showFoo f = Foo'name f ++ "(" ++ show (Foo'value f) ++ ")"
We could allow a form of shortened local reference by allowing the full form to "flow through" type declarations:
type ETS = ExecutionTraceSummary
logExecutionTraceSummary :: ExecutionTraceSummary -> IO ()
logExecutionTraceSummary s = do
putStr $ ETS'start s
Mind you, I realize that apostrophe may not work here, and details haven't been worked out.
[...that was the first pence, here comes the second...]
If you buy any of that, then one could allow, in the manner pointed out by some (though in particular I'm thinking of David Menendez's example), that this construction could imply a type class and associated type. That is, the first appearance of 'name in a record implies this:
class <C>'name a where
type <R>'name a :: *
'name :: a -> <R>'name a
and for each appearance of 'name :: X as a field of Foo:
instance <C>'name Foo where
type <R>'name Foo = X
'name = Foo'name
(Here, <C> and <R> are some unwritable prefixes used by the compiler. It remains to be seen if these should be module scoped or program global.)
So, in the case (repeated from above):
data Foo = Foo { 'name :: String, 'value :: Double }
data Bar = Bar { 'name :: String, 'value :: [String], 'subbars :: [Bar] }
We get auto generated:
class <C>'name a where
type <R>'name a :: *
'name :: a -> <R>'name a
class <C>'value a where
type <R>'value a :: *
'value :: a -> <R>'value a
class <C>'subbars a where
type <R>'subbars a :: *
'subbars :: a -> <R>'subbars a
instance <C>'name Foo where
type <R>'name Foo = String
'name = Foo'name
instance <C>'name Bar where
type <R>'name Bar = String
'name = Bar'name
instance <C>'value Foo where
type <R>'value Foo = Double
'value = Foo'value
instance <C>'value Bar where
type <R>'value Bar = [String]
'value = Bar'value
instance <C>'subbars Bar where
type <R>'subbars Bar = [Bar]
'subbars = Bar'subbars
*Now* one can write:
showFoo :: Foo -> String
showFoo f = 'name f ++ "(" ++ show ('value f) ++ ")"
nameBoth :: Foo -> Bar -> String
nameBoth f b = 'name f ++ " " ++ 'name b
None of this requires any more type machinery than already exists with TypeFamilies. It perhaps suffer some of the prior objections to implying semantic equivalence (in say the 'value fields) where none exists. But, it is limited in scope to fields, and only when one uses the special naming sigil (apostrophe here).
Of course, this approach would meld nicely with any better record/label system. For starters:
class <C>'name a where
type <R>'name a :: *
'name :: a -> <R>'name a
''name :: <R>'name a -> a -> a
instance <C>'name Foo where
type <R>'name Foo = String
'name = Foo'name
''name = \v x -> x { Foo'name = v }
There now -- I feel like TNDR is placating the muscle memory in our fingers that wants to type "f.name" ... and I hope we find a solution to replacing the tedium of so many "fooName" fields, and perhaps solve the record update ugliness as well!
- Mark
Mark Lentczner
http://www.ozonehouse.com/mark/
IRC: mtnviewmark
More information about the Haskell-Cafe
mailing list