TDNR without new operators or syntax changes

Previous attempts to propose TDNR [1] have met with opposition over the accompanying proposal to change the syntax of the dot or add a new operator for postfix application. However, nothing about TDNR - other than certain motivating examples - actually requires changes to the syntax of Haskell or new operators. TDNR could be implemented as an extension which just give GHC a new way of disambiguating function names, and nothing else. This would still have some advantages: - Redundant module qualification no longer required. - Unqualified imports could be changed to a different module with the same interface (a very poor-man's backpack) without any other code changes. - People who want TDNR with postfix function application will only need to define a simple postfix operator. I would therefore like to propose TNDR without any syntax/prelude changes. [1] https://prime.haskell.org/wiki/TypeDirectedNameResolution -- View this message in context: http://haskell.1045720.n5.nabble.com/TDNR-without-new-operators-or-syntax-ch... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Jeremy
writes:
Hi Jeremy, I feel your frustration at the slow evolution of records proposals. There are many reasons, including that there has been much debate and little consensus.
Previous attempts to propose TDNR [1] have met with opposition over the accompanying proposal to change the syntax of the dot or add a new operator for postfix application.
However, nothing about TDNR - other than certain motivating examples - actually requires changes to the syntax of Haskell or new operators. ...
You are possibly confusing parts of one records proposal with parts of TDNR. In https://ghc.haskell.org/trac/ghc/wiki/Records/ DeclaredOverloadedRecordFields/DotPostfix dot as postfix function apply could indeed be dispensed with. But that is not within a TDNR approach.
TDNR could be implemented as an extension which just give GHC a new way of disambiguating function names, and nothing else.
No. For TDNR GHC needs some syntactic signal to trigger disambiguation. The idea was the dot (or some other operator) says "here comes a field label". Without that signal, all GHC can see is a name -- could be a variable, a function, anything.
I would therefore like to propose TNDR without any syntax/prelude changes.
I suspect that if you took the syntax away from TDNR, you'd have very little left. If there were a feasible records approach which needed no syntax/prelude changes, I rather think it would have been found by now. In terms of what's available now, have you looked at the lens approaches or Nikita's recent Anonymous Records? https://ghc.haskell.org/trac/ghc/wiki/Records/Volkov Have you looked at what's coming very shortly in GHC v8.0? https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields I suspect Part 1: Duplicate Record Fields will go a long way towards disambinguating label names, with only an occasional need for explicit type signatures. How does that compare with TDNR minus syntax? AntC

AntC wrote
No. For TDNR GHC needs some syntactic signal to trigger disambiguation. ... I suspect that if you took the syntax away from TDNR, you'd have very little left.
To copy an example from the TDNR wiki page: module Foo where import Button( Button, reset ) as B import Canvas( Canvas, reset ) as C f :: Button -> Canvas -> IO () f b c = do { B.reset b; C.reset c } With syntaxless TDNR enabled, the last line could be: f b c = do { reset b; reset c } This requires no syntactic signal. The compiler will see two candidate definitions for reset, and in each case, pick the one which matches its argument. -- View this message in context: http://haskell.1045720.n5.nabble.com/TDNR-without-new-operators-or-syntax-ch... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

With syntaxless TDNR enabled, the last line could be:
f b c = do { reset b; reset c }
Heck, I didn't think you meant something that radical. So bare name in a function application context is to need disambiguating. I think you'll find rather a lot of those in existing code. So this is a code-breaking change. The merit of adding an extension via new syntax, is that in the places you didn't and don't use the syntax, nothing breaks. Note that currently if you have in scope both a bare name and that same name qualified, the bare name applies, the compiler ignores the qualified one, unless you deliberately write in the qualification. How much are people deliberately playing with same name bare and qualified? Probably seldom. How much are people importing qualified so they don't have to worry about their local name clashing? Suddenly they'll have to start worrying. So I see difficulties. Perhaps TDNR's dot-apply would have suffered them too, but * it was expected that dot-suffixing would only be used for field access (and access to field-like functions) * it was expected that the argument to the function would be near by, so the compiler wouldn't have to look far for the type by which to resolve * the debate around TDNR didn't get into this much detail, because the dot syntax proposal met such a violent counter-reaction * so TDNR never got beyond a wiki page, AFAIK. One thing TDNR certainly suffers is that you still can't declare two distinct data types in the same module with the same field name. ORF Part 1 in GHC 8.0 at least allows that. Difficulties: overloaded functions (methods) don't have a specific data type argument, by which we could disambiguate the bare name. (Disambiguation is supposed to be lightweight, we don't want to go hunting for instances.) So a lot of the bare function names (eg imported from the Prelude) are going to run into trouble. In f $ g $ h x we have to disambiguate h to know its result type, before we can disambiguate g, before we can disambiguate f. Type improvement could rapidly get stuck. Ah! but those are not bare names in a function application context. So ($) contexts (and function composition (.) contexts) are going to behave different to f (g (h x)) But then your OP suggested users who want postfix apply could define their own operator. Presumably TDNR *is* to apply to that sort of function application(?) So to ($) or not? to (.) or not? to sections or not? to bare name functions supplied as arguments (to say map) or not? I think this is the point SPJ would ask you to write up a proposal on a wiki, to the same level of detail as all those other records proposals. AntC

AntC wrote
I think you'll find rather a lot of those in existing code. So this is a code-breaking change.
Could you give an example of existing code that would break? This certainly wasn't what I had in mind. -- View this message in context: http://haskell.1045720.n5.nabble.com/TDNR-without-new-operators-or-syntax-ch... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Jeremy
writes:
AntC wrote I think you'll find rather a lot of those in existing code. So this is a code-breaking change.
Could you give an example of existing code that would break? This certainly wasn't what I had in mind.
Then what do you have in mind? "Do not break existing code" is not a design. "Syntaxless TDNR" is not a design; it's not even a thing. I've given you two lengthy replies, with pointers to further material. You've given me two sentences, and a snippet of code indistinguishable from gazillions of bare name function calls in existing code. I urge you (for the third time) to look at ORF Part 1: Duplicate Record Fields. See the type-directed resolution of label names. AntC

AntC wrote:
With syntaxless TDNR enabled, the last line could be:
f b c = do { reset b; reset c }
Heck, I didn't think you meant something that radical. So bare name in a function application context is to need disambiguating.
I think you'll find rather a lot of those in existing code. So this is a code-breaking change.
I don't understand your conclusion. The code above, in context, is currently illegal: There are two "reset" functions in scope, and the compiler will ask the programmer to specify which of them they intended to use. Jeremy's proposal, I believe, is that the compiler should pick /the/ possibility that type-checks (f had a type signature that would allow only one combination to work); . Note that this has nothing to do with record fields at all, except that they give rise to a compelling use case. (I'm not endorsing the proposal, just trying to clarify what it is.) Cheers, Bertram

Bertram Felgenhauer
writes: ... I don't understand your conclusion.
Hi Bertram, I'm trying to tease out of Jeremy, what he thinks his proposal amounts to. Seeing as he's given very little clue so far. So if I've arrived at an incorrect conclusion, I want him to tell me where/what's gone wrong. The point, I believe, although Jeremy's not said, is exactly as you say:
Note that this has nothing to do with record fields at all, ...
So let's modify Jeremy's code snippet
With syntaxless TDNR enabled, the last line could be:
f b c = do { reset b; reset c }
to something that's nothing to do with record fields: f b c = do { print b; print c } `print` is a bare name appearing in a function application context. It therefore needs disambiguating. It's type :: (Show a) => a -> IO () Are `f`s params of type `a`, or of type (Show a) => a ? No, they're String or Int or whatever. Compile fail. Now you might say `print` doesn't need disambiguating. Why not? Jeremy hasn't said. Perhaps Jeremy intends a rule: If there's only one version of the function in scope, choose that. If so, Jeremy hasn't said. "syntaxless TDNR" doesn't give me a clue. I don't think TDNR intended such a rule. Suppose I do Jeremy's job for him and presume such a rule. OK then. Second example: Suppose I don't like what's happening with the FTP library changes, specifically not the generalisation of `length`. So I write my own import qualified Data.List as D -- note the qualified length :: [a] -> Int. -- my version restricted to lists length = D.length Now I use length in a bare name function application ... (length [1, 2, 3, 4, 5]) ... Therefore length needs disambiguating. a) the disambiguator can't match argument type [Int] to [a]. b) even if it could, `length` remains ambiguous between my definition and the import. (As it happens, they're the same, but can the disambiguator tell that? Jeremy hasn't said what he thinks the disambiguator can do. But this is way beyond the original TDNR proposal.)
Jeremy's proposal, I believe, is that the compiler should pick /the/ possibility that type-checks ..
Well you're guessing what is the proposal, as much as I am. In both the examples above, neither type checks. (Or arguably both do if we allow polymorphic type checking. But should that allow both paramteric and ad-hoc polymorphic? Polymorphic checking is too generous for the way TDNR was spec'd.)
Note that this has nothing to do with record fields at all, except that they give rise to a compelling use case.
So the compelling use case has given us ORF Part 1 in GHC 8.0. That has a clear antecedent in TDNR. But, pace your note, it applies _only_ for record fields. If you look at the implementation notes, the functions derived from field labels are specially flagged. So disambiguation applies only to them, not in general to bare function names.
(I'm not endorsing the proposal, ...
Quite.
... just trying to clarify what it is.)
Quite. And since 8.0 is officially released as of this weekend, I rather think "syntaxless TDNR", whatever it is/was, is stillborn. AntC

Adam Gundry writes: ... Having spent more time thinking about record field overloading than perhaps I should, ...
Thanks Adam, another thing on the back burner ... The earlier design for SORF tried to support higher-ranked fields. https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/SORF That had to be abandoned, until explicit type application was available IIRC. We now have type application in GHC 8.0. Is there some hope for higher-rank type fields? AntC

On 15/06/16 04:29, AntC wrote:
...
The earlier design for SORF tried to support higher-ranked fields. https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/SORF
That had to be abandoned, until explicit type application was available IIRC.
We now have type application in GHC 8.0.
Is there some hope for higher-rank type fields?
Unfortunately, doing ORF with higher-rank fields is a bit of a non-starter, even with explicit type application, because the combination would break bidirectional type inference and require impredicativity. This was part of the reason we ended up preferring an explicit syntactic marker for ORF-style overloaded labels. For example, consider this (rather artificial) type: data T = MkT { foo :: ((forall a . a -> a) -> Bool) -> Bool } -- foo :: T -> ((forall a . a -> a) -> Bool) -> Bool Suppose `t :: T`. When type-checking `foo t (\ k -> k k True)`, the compiler will infer (look up) the type of `foo` and use it to check the types of the arguments. The second argument type-checks only because we are "pushing in" the type `(forall a . a -> a) -> Bool` and hence we know that the type of `k` will be `forall a . a -> a`. Now suppose we want to type-check `#foo t (\ k -> k k True)` using ORF instead. That ends up deferring a constraint `HasField r "foo" a` to the constraint solver, and inferring a type `a` for `#foo t`, so we can't type-check the second argument. Only the constraint solver will figure out that `a` should be impredicatively instantiated with a polytype. We end up needing to do type inference in the presence of impredicativity, which is a Hard Problem. There is some work aimed at improving GHC's type inference for impredicativity, so perhaps there's hope for this in the future. Explicit type application makes it possible (in principle, modulo #11352) for the user to write something like #foo @T @(((forall a . a -> a) -> Bool) -> Bool) t (\ k -> k k True) although they might not want to! But the ramifications haven't been fully thought through, e.g. we'd need to be able to solve the constraint HasField T "foo" (((forall a . a -> a) -> Bool) -> Bool) even though it has a polytype as an argument. Sorry to be the bearer of bad news, Adam -- Adam Gundry, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/
participants (4)
-
Adam Gundry
-
AntC
-
Bertram Felgenhauer
-
Jeremy