
2010/11/11 Gábor Lehel
I agree with the people who want to decouple the dot-syntax from TDNR itself. To quote myself from the publicly-editable wiki page:
"This might be a really dumb question, but is there any reason TDNR needs to be tied to a new syntax for function application? It seems strange to me to have one syntax for left-to-right function application without TDNR, and then another for right-to-left application with it. I would much rather gain TDNR for the existing syntax, and then maybe introduce the dot operator as a separate option if people want it, which I don't. The reason I don't is that (.) as composition and ($) already work in one direction, which is the same direction (`foo . bar . baz $ bla` and `foo $ bar $ baz $ bla` are interchangeable), and while in a vacuum I might even prefer the opposite direction which (.)-as-application uses, it is much more important to be consistent. We have a vast body of functions already written and designed to be convenient with the existing direction: functions are generally of the form `f :: (what to do) -> (what to do it with) -> (result)`, which lends itself well to partial application/currying and chaining in the existing direction, but not the other one. (Object oriented languages which use the dot operator indeed also use the reverse order for their methods, with the object first and the action second.) Also, reading expressions where different parts work in different directions is very confusing. The one major exception which works in the other direction is monadic bind, (>>=), which I think was a (minor) mistake, and indeed I frequently end up using (=<<) instead. Anyway, executive summary: TDNR yea, dot operator nay."
Some further thoughts.
I would have TDNR apply only in cases where: - The functions are all imported from different modules (so you can't define overlapping names within the same module); - All of the functions have an explicit type signature; - The ambiguity can be resolved by looking at the type of the first (taking currying into account, only) parameter of each function and, looking at the type constructors from the outside in, comparing only type constructors which are concrete types, rather than type variables (with or without constraints). E.g.: -- f :: Int -> [...] and f :: Char -> [...] could be resolved; -- f :: Foo Int -> [...] and f :: Foo Char -> [...] could be resolved; -- f :: Foo a -> [...] and f :: Bar b -> [...] could be resolved; -- f :: Num a => a -> [...] and f :: IsString b => b -> [...] could *not* be resolved (even if it is known that the argument type doesn't satisfy both constraints); -- f :: a Int -> [...] and f :: b Char -> [...] could *not* be resolved (though I'm less sure about this one). -- Going by the above, neither Foo nor Bar can be type functions. -- With more than two functions, each possible pair has to meet the conditions.
I forgot to mention: some kind of similar criteria should probably apply to the function where 'f' is used (again going by the above examples) and/or its argument as well. E.g. it should not be possible to define "g a = f a" and have f be resolved by TDNR when g is used (even if all of the in-scope functions with the name 'f' meet the previous criteria), because TDNR is not duck typing.
I don't have any well-articulated arguments to support this idea, yet; it mainly just "feels right". My intuition for TDNR is that it has no connection to semantics, and it is not intended as a means of defining an interface: it is merely a syntactic convenience. If you want to define an interface, use a type class. TDNR would be a convenience for the case where you have imported multiple modules using a function of the same name with obviously different types (whether the functions do similar or different things is beside the point). Comparing to C++ (though it's unlikely to help my case to mention that language here, but whatever), I feel that type classes : TDNR :: virtual functions : static overloading.
And, in any case, as a language extension nobody would be forced to use it if they don't like it. (I personally would find it very useful). I think the fact that language extensions need to meet (much) lesser criteria than changes to the language standard itself is plainly evidenced by the existence of IncoherentInstances, which nobody in their right mind would ever consider standardizing (or, for that matter, ever enabling).
On Wed, Nov 10, 2010 at 10:59 AM, John Smith
wrote: Type-directed name resolution, as originally proposed for Haskell', has now been proposed for GHC. Obvious benefits of this are that conflicting function names from imported modules can be used without qualification (verbose) or pseudo-Hungarian renaming (verbose, and requires that you control the source, and perform the same renaming in all dependencies). This is important for both readability and programming in the large, particularly where records are concerned, as the duplicate name problem cannot be alleviated with typeclasses, and it is often desirable to have the same field names for many records in the same module.
http://hackage.haskell.org/trac/ghc/ticket/4479 http://hackage.haskell.org/trac/haskell-prime/wiki/TypeDirectedNameResolutio...
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Work is punishment for failing to procrastinate effectively.
-- Work is punishment for failing to procrastinate effectively.