|
There's a proposal at the moment to add support for TDNR to Haskell
- to leverage "the power of the dot" (e.g. for intellisense).
http://hackage.haskell.org/trac/haskell-prime/wiki/TypeDirectedNameResolution I approve of the goal, but I'd like to suggest a different approach. My basic idea is stolen from Bertrand Meyer (Object-Oriented Software Construction, second edition). Basically, a class *is* both a module and a type. Quote... Classes as modules Object orientation is primarily an architectural technique: its major effect is on the modular structure of software systems. The key role here is again played by classes. A class describes not just a type of objects but also a modular unit. In a pure object-oriented approach: Classes should be the only modules. By the logic of equivalence relations, we can conclude that a type *is* a module. Only I'd adapt that a little. In C++, the following operators can all be used to access the "module" for some type or value...
We already have member pointers - the functions that map an instance to the field value. It would make some sense if these could be placed in a module associated with the type (not the instance). When an instance is created of a type, that can effectively (without run-time overhead) create a new module associated with the new instance. This will contain the same field-access functions, but with the instance parameter already curried in. So there's no real need for any new meaning of the . operator - it's just access to names within a module. And there's no need for a new mechanism for accessing fields - only for a way to place them in that module scope, and a little sugar that gives us the same field-access function but with the instance parameter already curried in. Once we have these modules containing compiler-generated field-access functions, though, it makes some sense to allow additional functions (and perhaps types) to be added within that types module explicitly by the programmer. It may also make sense to allow functions to be explicitly defined which will be added to the instance-modules and support the prefix-instance-parameter sugar. Finally, as with C++, when dealing with IORef and similar, it make make sense to have a separate -> operator (spelled differently, of course). Or it could use the standard dot. C++ and D disagree in this (in C++, the smart pointer has its own module separate from the pointed-at instance - in D, there is no -> or equivalent). As an aside, Ada has already gone through a related transition. The original Ada 83 had variant records, but no "true classes". In Ada 95, "tagged types" were added which were like variant records, but which supported inheritance and run-time dispatch. The discriminant is replaced by a "tag" which is presumably implemented as a virtual table pointer. However, functions and procedures weren't members. The typical call of a "method" would be... packagename.procedure_name ( instance_arg, other_args ); Ada 2005 added some workarounds to allow conventional OOP call notation. See section 1.3 of the Ada 2005 rationale for details. However, it all feels a bit kludgy. In particular, the procedures and functions still aren't members - there are just some special rules for when they can be used as if they were. I've not actually used Ada 2005, but I'd bet some confusion can result from that. Personally, I think Meyer was at least partly right - if types (and instances) are modules, the kludge-factor is much lower. C++ actually doesn't get this quite right IMO (you can access static class members through the instance objects, for example, not just through the classes), but C++ classes *do* act mostly like modules and that is a very useful trait - particularly within the declarative sublanguage (templates etc). _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Steve Horne <sh006d3592 <at> blueyonder.co.uk> writes:
> > There's a proposal at the moment to add support for TDNR to Haskell > - to leverage "the power of the dot" (e.g. for intellisense).http://hackage.haskell.org/trac/haskell- prime/wiki/TypeDirectedNameResolution > I approve of the goal, ... Steve, I think that proposal has been rather superseeded by http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields, which draws on TDNR. But SORF is best seen as an evolving design space, with precise details yet to be clarified/agreed. I've put my own variation into the ring: http://www.haskell.org/pipermail/glasgow-haskell-users/2011- December/021298.html -- which seems to have fallen into a black hole :-( One of the aspects of TDNR that wasn't so popular was that its type-directed resolution was very similar to instance resolution, but subtly and confusingly different. I guess we have to be very careful about the dot. It seems to be in a very 'crowded' syntax space, so if we implement the wrong way, we could end up shutting the door with the keys left inside. SPJ's observations about how the dot works in other languages are all good points. He's arguing that the dot should behave in a familiar way. I'm most used to it in SQL as table.column, but I guess for most programmers it's object.method. Haskell is already encumbered by Module.name, and g . f (function composition with spaces round the dot). I like the part in OverloadedRecordFields (and TDNR) re user-defined 'virtual' fields. (fullName being a concatenation of the datatype fields firstName and lastName, area being a calculation over a Shape datatype.) But the point about those being virtual is that they're not first-class fields: you can't update through them. SPJ got 'stuck' at that point. My proposal was that restricting the dot to field selection wasted too much of the design space. Instead dot should be merely syntactic sugar for reverse function application. That is: whatever.funcmethod ==> (funcmethod whatever) (Note no spaces around the dot. This is syntactically distinct from qualified names because the name to the left of the dot begins lower-case.) Then funcmethod can be a 'real' field selector, or a virtual field or a class method or some other function completely. So to get to name resolution: since dot is (reverse) function application, we can use all the usual Haskell type inference/instance selection 'for free'. Either/both `whatever' and `funcmethod' could be arguments passed through from a distant call, which turned out to be a record type and field selector (not recognisable as such from its name). So we'd get polymorphic record and field selection 'for free'. I'd also like to be able to mix the dot with qualified names: A.b.(C.D.e.f).G.h ==> (G.h ((f C.D.e) A.b)) The syntax rule is: an upper-case name to the left of the dot means this is a qualified name, and binds most tightly. lower-case to the left means reverse- function applic. Of course you can use parentheses to group differently. (Re a one-sided dot I have no intuitions. TDNR includes some options for partial application/sections, SORF some more. They seem to me what Wirth would call 'rococo'. If dot is to be merely function application, it hardly seems worth worrying about.) How do we get field names to be suitable funcmethods for dot applying to records? And how do we support field update? ==> Subjects for a different post. There's also an elephant in the room I haven't talked about: TDNR started with what happens inside an IDE when you type `x.' and all the possible methods (or fields) for x pop up. This follows the philosophy in OO of focus on the object -> look for the action. (Same thinking as right-click in GUI's. Contrast old-style 'green screen' applications where you went down a menu tree first (action), then looked for your object.) If the dot is merely function application, then what follows the dot could be 'anything' (including very generic functions like show or return). I plain don't know if IDE's can be smart enough to spot that what's to the left of the dot is a datatype and offer its fields, or get from its type to its instances to their methods. (Actually, under my proposal, datatype to fields is exactly datatype to Has instance.) (How) could it tell what are more-specific or more- generic methods? > My basic idea is stolen from Bertrand Meyer (Object-Oriented > Software Construction, second edition). Basically, a class *is* both > a module and a type. ... 1) Are you sure that C++ classes/instances/methods are comparable enough to Haskell's? This is a very confusing area of terminology for object-oriented cp. functional languages. 2) Have you looked at GHC 7.4.1 innovations around classes-as-types and Constraint kinds? _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
AntC> Steve, I think that proposal has been rather superseeded by AntC> http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields, which AntC> draws on TDNR. But SORF is best seen as an evolving design space, with precise AntC> details yet to be clarified/agreed. I've put my own variation into the ring: AntC> http://www.haskell.org/pipermail/glasgow-haskell-users/2011- AntC> December/021298.html -- which seems to have fallen into a black hole :-( AntC> One of the aspects of TDNR that wasn't so popular was that its type-directed AntC> resolution was very similar to instance resolution, but subtly and confusingly AntC> different. AntC> I guess we have to be very careful about the dot. It seems to be in a AntC> very 'crowded' syntax space, so if we implement the wrong way, we could end up AntC> shutting the door with the keys left inside. AntC> (...) All this dot syntax magic frankly frightens me. Haskell, as a pure functionnal language, requires (and allows !) a programming style that just does not mix well with object oriented practices. Stretching the syntax to have the dot feel a-bit-but-not-really like object oriented programming, mainly to have IDE autocompletion on some cases, does not make much sens. If the editor matters - and it probably does -, we could rather take a more ambitious path, and work on a real semantic editor, as opposed to a plain left-to-right text editor, with hacked semantic goodies to alleviate the pain. Indeed, very often in haskell, we just don't think code left to right, or top to bottom. Emacs ability to move point quickly certainly helps, but I'd surely welcome a drastic switch, to something allowing us to work directly on type-checked syntax trees. -- Paul _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On 28/01/2012 13:00, Paul R wrote:
> AntC> Steve, I think that proposal has been rather superseeded by > AntC> http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields, which > AntC> draws on TDNR. But SORF is best seen as an evolving design space, with precise > AntC> details yet to be clarified/agreed. I've put my own variation into the ring: > AntC> http://www.haskell.org/pipermail/glasgow-haskell-users/2011- > AntC> December/021298.html -- which seems to have fallen into a black hole :-( > > AntC> One of the aspects of TDNR that wasn't so popular was that its type-directed > AntC> resolution was very similar to instance resolution, but subtly and confusingly > AntC> different. > > AntC> I guess we have to be very careful about the dot. It seems to be in a > AntC> very 'crowded' syntax space, so if we implement the wrong way, we could end up > AntC> shutting the door with the keys left inside. > > AntC> (...) > > All this dot syntax magic frankly frightens me. Haskell, as a pure > functionnal language, requires (and allows !) a programming style that > just does not mix well with object oriented practices. Stretching the > syntax to have the dot feel a-bit-but-not-really like object oriented > programming, mainly to have IDE autocompletion on some cases, does not > make much sens. before OOP became popular - OOP stole the dot from modular programming! If a record is a module, that only means that one thing can be both a module and a type (or value) at the same time. It takes little from OOP that OOP didn't already take from the more fundamental modular programming - and Haskell already has modules. > If the editor matters - and it probably does -, we could rather take > a more ambitious path, and work on a real semantic editor, as opposed to > a plain left-to-right text editor, with hacked semantic goodies to > alleviate the pain. Every programmer has their own favorite editor, usually using the same one to work in many different languages. For the moment, you'd have a hard job separating me from Notepad++. If you really want a "semantic editor", I'd argue a rich visual language with a file format that isn't intended to be read directly. Something more like writing in Word than writing in TeX. But I don't think most programmers are ready for this, for various reasons. Version control tools and readable differences get a place near the top of that list. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On 30/01/2012 04:23, Steve Horne wrote:
> On 28/01/2012 13:00, Paul R wrote: >> AntC> Steve, I think that proposal has been rather superseeded by >> AntC> >> http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields, >> which >> AntC> draws on TDNR. But SORF is best seen as an evolving design >> space, with precise >> AntC> details yet to be clarified/agreed. I've put my own variation >> into the ring: >> AntC> http://www.haskell.org/pipermail/glasgow-haskell-users/2011- >> AntC> December/021298.html -- which seems to have fallen into a >> black hole :-( >> >> AntC> One of the aspects of TDNR that wasn't so popular was that its >> type-directed >> AntC> resolution was very similar to instance resolution, but subtly >> and confusingly >> AntC> different. >> >> AntC> I guess we have to be very careful about the dot. It seems to >> be in a >> AntC> very 'crowded' syntax space, so if we implement the wrong way, >> we could end up >> AntC> shutting the door with the keys left inside. >> >> AntC> (...) >> >> All this dot syntax magic frankly frightens me. Haskell, as a pure >> functionnal language, requires (and allows !) a programming style that >> just does not mix well with object oriented practices. Stretching the >> syntax to have the dot feel a-bit-but-not-really like object oriented >> programming, mainly to have IDE autocompletion on some cases, does not >> make much sens. > That's a benefit of my idea. Modular programming used the dot long > before OOP became popular - OOP stole the dot from modular > programming! If a record is a module, that only means that one thing > can be both a module and a type (or value) at the same time. It takes > little from OOP that OOP didn't already take from the more fundamental > modular programming - and Haskell already has modules. > I'm basically asserting that a record in standard Pascal (without any of that OOP Turbo Pascal 5.5+/Delphi stuff) is a module. It doesn't matter that the only names that can be held in that module are field names - it's still a container of named items and therefore a special case of a module. In the Pascal case (like C structs), the content of the module doesn't include functions or methods or whatever, it only includes fields. And the module is only accessible via the record instances, not via the record type (there's nothing like C++ member pointers). Converting this to Haskell - well, we already use field-access functions, so why not move those to the record-instance module instead of having them pollute some existing namespace? Since naming the same thing twice (once to identify the module, once to specify the instance parameter) would be annoying, why not auto-curry that parameter? The result is still a function living in a module. And rather than lose the original function, why not move that to another scope - a module that's associated with the record type rather than the record instance? If you don't specify an instance, you can't curry that parameter - it still makes sense. There's no inheritance here, no virtual functions, no OOP features at all - just Pascal-like records adapted for immutability by supplying a field access function rather than e.g. a field offset. The function placed in the record-type module would be the exact same function we get now, just in a different scope. However, once you have the idea that a record is a module, maybe it makes sense to put some other functions in there too? As a minimal solution no, but it's nice to know there's room for future expansion. There's nothing OOP about this at all - it's really just adapting and extending what standard Pascal does. You could extend it to include OOP if you really wanted to, but the minimal solution just moves the existing Haskell access functions to another scope, and adds a pre-curried version in a further scope, associating those scopes with the record type and record instances respectively. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Steve Horne
On 28/01/2012 13:00, Paul R wrote:
... > All this dot syntax magic frankly frightens me. Haskell, as a pure > functionnal language, requires (and allows !) a programming style that > just does not mix well with object oriented practices. Stretching the > syntax to have the dot feel a-bit-but-not-really like object oriented > programming, mainly to have IDE autocompletion on some cases, does not > make much sens. In the glasgow-haskell-users discussion, it has been pointed out (to little apparent effect) that the current notation for access by field name, `field record', is naturally functional and is easier to read for a functionally trained eye than a postfix `record.field' alternative. It isn't so much of an issue for OO programmers because the languages are also procedural and the expressions tend to be simpler. In a language like Haskell, an expression could switch back and forth several times between pre-fix (functional) and post-fix (dot) notation. Like, `yolk (separate (crack (largeEnd egg)))' becomes `(separate (crack (egg.smallEnd))).yolk' That elementary example doesn't give me much trouble, but it sure doesn't seem to be much of an improvement in notational elegance. See how natural the transformation with function composition - yolk (separate (crack (largeEnd egg))) yolk ((separate . crack . largeEnd) egg) yolk (f egg) where f = separate . crack . largeEnd ... compared to the re-shuffing necessary with post-fix dot notation (assuming for the sake of discussion a functional dot notation .field = \ r -> r.field) (separate (crack (egg.smallEnd))).yolk ((separate . crack . .smallEnd) egg).yolk (f egg).yolk where f = separate . crack . .smallEnd Donn _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On 30/01/2012 07:09, Donn Cave wrote:
> ((separate . crack . .smallEnd) egg).yolk > (f egg).yolk where f = separate . crack . .smallEnd Scary - that ".smallEnd" worries me. It's like a field is being referenced with some magical context from nowhere. Obviously I need to read that full proposal. Sorry for going on about it, but this wouldn't happen in my idea. If field access functions are just items in a module, the . separates the module identification from the item name, same as always. The only difference is how you identify the module. There's no special interactions with function composition, or whatever it is that's happening here. If you want to composite the function with some other function without knowing in advance which record value you're dealing with, just get the access function from the record-type module. If I'm correctly guessing what your code means, that reads as... (f egg).yolk where f = separate . crack . (eggModule.eggType.smallEnd) OK, in a sense specifying "eggModule.eggType" there is a bit redundant, but in general that isn't true - define f separately and it needs some clue for the type inference to decide where to look for "smallEnd", and "eggtype" provides it. I'd prefer a notation that allows me to reference the field without needing type inference to determine which record type it relates to. But then again, I'm probably just not understanding the reason behind that wierdness - perhaps it wouldn't seem so wierd if I did. Or maybe it's just a familiarity issue. First thought - I've not addressed access from within a polymorphic function with type class constraints. The situation there would (without extra features) be the same as it is now, with no TDNR support. Field access functions would have to be provided as explicit operations within the type class. That said, it shouldn't be hard to handle. For example, a type class can explicitly state which field names it is interested in, and an instance can provide functions to access those fields. Alternatively, the instance might support using arbitrary functions (of the right type). This might allow some wierdness (fields that aren't really fields), but it may allow some useful flexibility (this particular type provides the field "daddy", that type provides "mummy", a third type has no named fields but has a function that works by pattern matching that can provide the positionally-defined field - either way, this type class will refer to "parent") so that polymorphic functions can use the dot notation, but the relationship between fields in the type class and fields in the instance type are flexible. It's really just syntactic sugar for what type classes have to do now - providing a dot notation, but still using vtable references to field access functions to do the work. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Steve Horne
Steve> Every programmer has their own favorite editor, usually using the same
Steve> one to work in many different languages. For the moment, you'd have Steve> a hard job separating me from Notepad++. Main editors have very advanced customization features (though incredibly hacky most of the time). A type-directed (this word is what I like most in the proposal ;]) Haskell editing mode for them could be a good first step. Steve> If you really want a "semantic editor", I'd argue a rich visual Steve> language with a file format that isn't intended to be read directly. Steve> Something more like writing in Word than writing in TeX. But I don't Steve> think most programmers are ready for this, for various reasons. Steve> Version control tools and readable differences get a place near the Steve> top of that list. Well, in the long term I don't know ... maybe plain text won't be a good representation of a program anymore. But in the short term that's not an option. However, I see no problem in just constructing this textual representation through a strictly type-directed (yeah!) syntax tree editor. Emacs, Vim, and a lot of others have "snippet" support. The workflow could be something like : - action to create a new top-level function - PROMPT : name (eg. map) - PROMPT : signature (eg. (a -> b) -> [a] -> [b]) - PROMPT : parameters matching (eg. f (x:xs)) - a stub is inserted with name, signature, and "undefined" definition map :: (a -> b) -> [a] -> [b] map f (x:xs) = undefined map f [] = undefined - now enters definition construction. You would start by adding to a 'pool' the bindings you want to combine. Some could be added to the pool automatically (function parameters, top-level definitions in the module, explicitly imported functions ...). Then you would pick one of them, and the type-directed system would offer type-correct completion. For example : - The pool state is { f :: a -> b , x :: a, xs :: [a], map :: (a -> b) -> [a] -> [b] } (the type variables scope over the entire pool) - The definition type must be [b] - I ask to use 'f'. It isn't [b] so I can't use it alone. The wonderful system would then reduce the pool to things that I can combine f with in order to keep the target [b] type. The result is { map f xs :: [b] }. I say ok. - The sub is now : map :: (a -> b) -> [a] -> [b] map f (x:xs) = map f xs map f [] = undefined - Now I ask to use (:) :: c -> [c] -> [c] . They are plenty of places where it could be used in the definition, so let's narrow the choice by associating the 'c' type to something in our expression : c == b. So (:) :: b -> [b] -> [b] - we have no expression typing as 'b' in the definition, but we have a single expression that types as [b], and it is 'map f xs'. So the system can safely offer : map :: (a -> b) -> [a] -> [b] map f (x:xs) = undefined : map f xs map f [] = undefined - now let's define the first 'undefined'. Its type is b. We ask this time to use the 'x' binding (x :: a). But we are looking for a 'b'. We have f :: a -> b so the system can offer 'f x'. map :: (a -> b) -> [a] -> [b] map f (x:xs) = f x : map f xs map f [] = undefined - The last undefined is trivial. The user interface would certainly matter much, to have a fast and pleasant experience. But the point is that as a pure language, haskell very looks well suited for this kind of incremental syntax-tree editing, with type-directed assistance. I wonder, even, if some rules could be defined to construct automatically a definition that typechecks and use all bindings in the pool :) Back to the point of the thread, it looks like we certainly can target type-directed editing with current haskell function notation, which has the advantage of being beautiful and consistent. -- Paul _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Steve Horne
Quoth Steve Horne <[hidden email]>,
> On 30/01/2012 07:09, Donn Cave wrote: >> ((separate . crack . .smallEnd) egg).yolk >> (f egg).yolk where f = separate . crack . .smallEnd > > Scary - that ".smallEnd" worries me. It's like a field is being > referenced with some magical context from nowhere. > > Obviously I need to read that full proposal. As I said: > (assuming for the sake of discussion a functional dot notation > .field = \ r -> r.field) By that, I meant to say that I just made that up. I am sure that various proposals have made some notational provision for `\ r -> r.field', but it may or may not be `.field', I don't recall. But that's all the magic there is to it. Either you have a notational shorthand for it, or you're obliged to write out `\ r -> r.field' Donn _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Donn Cave-4
Donn Cave <donn <at> avvanta.com> writes:
> > On 28/01/2012 13:00, Paul R wrote: > ... > > All this dot syntax magic frankly frightens me. Haskell, as a pure > > functionnal language, requires (and allows !) a programming style that > > just does not mix well with object oriented practices. > > In the glasgow-haskell-users discussion, it has been pointed out (to > little apparent effect) that the current notation for access by field > name, `field record', is naturally functional and is easier to read > for a functionally trained eye than a postfix `record.field' alternative. > [snip] > Donn > Just because OO does it like that, so what?" But if you read SPJ's discussion in the TDNR proposal, there's "a cultural connection to OO". My post at the head of this thread put it as "focus on the object -> look for the action". Of course it's easy to 'fake' postfix function application: (.$) = flip ($) But the trouble is that .$ binds weakly. What we want is for the dot to bind tighter even than function apply. So: crack egg.largeEnd ==> crack (largeEnd egg) (Where ==> means 'is syntactic sugar for'.) We're already familiar with the tight-binding dot for qualified names. I suppose we're coping with the visual confusion with space-surrounded dot as function composition. But I can see that "one more petit bonbon" could tip confusion over the edge. To my eye, one-sided dot application is a bonbon too far. My proposal is that field selection functions be just ordinary functions, and dot notation be just function application(tight-binding). Then: object.fieldfuncmethod ==> fieldfuncmethod object (Subject to the tight binding for the dot.) And one-sided dot application is pointless (! errk I mean 'without purpose', no different to writing the bare object or bare fieldfuncmethod). Then you can write in your familiar style, and can use polymorphic field selectors as plain functions (same syntax as presently). Those under the influence of OO can write dot notation, until they discover the joys of pointless style. AntC _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Quoth AntC <[hidden email]>,
... > My proposal is that field selection functions be just ordinary functions, and > dot notation be just function application(tight-binding). Then: > object.fieldfuncmethod ==> fieldfuncmethod object > (Subject to the tight binding for the dot.) > And one-sided dot application is pointless (! errk I mean 'without purpose', > no different to writing the bare object or bare fieldfuncmethod). That's interesting! The wiki page on SORF (Simple Overloaded Record Fields, http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields) has some language that, as I interpreted it, meant that Has/Get syntactic sugar depended on the dot, so it's indispensable. Your proposal actually has some similar language but, I see you don't mean it that way. That's great news for anyone who's really dying to get somewhere with records, if it means that the functionality could in principle be introduced independently of changes to the interpretation of "." that would break a lot of code. Donn _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Donn Cave <donn <at> avvanta.com> writes:
> > Quoth AntC <anthony_clayden <at> clear.net.nz>, > ... > > My proposal is that field selection functions be just ordinary functions, and > > dot notation be just function application(tight-binding). Then: > > object.fieldfuncmethod ==> fieldfuncmethod object > > (Subject to the tight binding for the dot.) > > And one-sided dot application is pointless (! errk I mean 'without purpose', > > no different to writing the bare object or bare fieldfuncmethod). > > That's interesting! The wiki page on SORF (Simple Overloaded Record Fields, > http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields) > has some language that, as I interpreted it, meant that Has/Get syntactic > sugar depended on the dot, so it's indispensable. Yes it does, and that's one of the things I didn't like - hence my counter- proposal. In particular in SORF, the dot notation got tied into 'virtual record selectors'. Now 'virtual record selectors' is a good idea, but SORF tied it to the field selection approach, so had to go via a Has instance, which introduced a `set' method as well as the get, which didn't make sense, so SPJ ran into trouble. Actually the TDNR proposal was better re the "power of the dot": "works with any function". As soon as you decide to make 'virtual record selectors' just ordinary functions (so they select but not update), then you can see that field names are also just ordinary functions (for selection purposes). So the semantics for field 'selection' (whether or not you use dot notation) is just function application. So Type-Directed Name resolution is just instance resolution. So it all gets much easier. > Your proposal actually > has some similar language but, I see you don't mean it that way. That's > great news for anyone who's really dying to get somewhere with records, > if it means that the functionality could in principle be introduced > independently of ... Yes. Actually, (IMHO) the biggest block to making some progress with the 'cottage industry' for records (and there are heaps of ideas out there) is that currently the field name appearing in data decls grabs so much of the namespace real estate. It creates a global name (selector function) that can't be overloaded. You'll see in my other posts last night (NZ time) that the first thing I think should happen is to switch off auto-creation of field selection functions. (This should have come along as an option with DisambiguateRecordFields, I think. http://www.haskell.org/pipermail/glasgow-haskell-users/2012- January/021750.html) > ... changes to the interpretation of "." that would break > a lot of code. > Yes, in principle we could introduce the semantics for field-selectors-as- overloaded-functions without introducing any special syntax for field selection (dot notation or whatever). But the 'Records in Haskell' thread started with a Reddit/Yesod discussion about records, and the lack of dot notation being the last major wart in Haskell. "A sentiment open to doubt" in the words of the poet. It stung SPJ enough to open up the discussion (and I guess now is timely as 7.4.1 gets put to bed). For me, the record/field namespacing is the major wart, polymorphism only slightly less, and the notation is a side-issue. But I don't want to lose the initiative that's built up, so I'm trying to address both at the same time. AntC _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On 1/02/2012, at 11:38 AM, AntC wrote: > As soon as you decide to make 'virtual record selectors' just ordinary > functions (so they select but not update), then you can see that field names > are also just ordinary functions (for selection purposes). So the semantics > for field 'selection' (whether or not you use dot notation) is just function > application. So Type-Directed Name resolution is just instance resolution. So > it all gets much easier. I'm reminded of Pop-2, where f(x) and x.f meant exactly the same thing. Overloading was a (dynamic) property of f, not a property of dot. Ada had two reasons for adding dot syntax, and much as I admire Ada, I'm not sure that I agree with either of them. One was to be more familiar to programmers from other languages, but since there remain interesting differences between x.f in Ada and x.f in other languages, it's not clear to me how much of a kindness that really is. The other is that x.f means basically what f(x) would have, *had f(x) been legal*; the aim was to be able to use methods without having to important everything from a module. Now that might have some relevance to Haskell. Making f x and x.f the same is pretty appealing, but it is imaginable that the former might require importing the name of f from a module and the latter might not. That is to say, it lets f and .f have completely different meanings. Oh the joy! Oh the improved readability! -- on some other planet, maybe. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by Steve Horne
> On 1/02/2012, at 11:38 AM, AntC wrote: > > As soon as you decide to make 'virtual record selectors' > > just ordinary functions (so they select but not update) > > , then you can see that field names are also just > ordinary functions (for selection purposes). So the > > semantics for field 'selection' (whether or not you use > > dot notation) is just function application. So > Type-Directed Name resolution is just instance resolution. > > So it all gets much easier. > > > Richard O'Keefe wrote: > ... Making f x > and x.f the same is pretty appealing, but it is imaginable > that the former might require importing the name of f from > a module and the latter might not. That is to say, it lets > f and .f have completely different meanings. Oh the joy! > Oh the improved readability! -- on some other planet, > maybe. > I'm proposing x.f is _exactly_ f x. That is, the x.f gets desugared at an early phase in compilation. If the one needs importing some name from a module, than so does the other. A 'one-sided dot doesn't mean anything. (Also, I feel vaguely nauseous even seeing it written down.) Under my proposal, the only thing .f could mean is: \z -> z.f which desugars to \z -> f z which means (by eta-reduction) f And to complete the story: the only thing (x.) could mean is: \g -> x.g So a use like: (x.) f -- or z f, where z = (x.) would desugar to f x which is the same as x.f A use like (x.)f (no spaces around the parens) would amount to the same thing. This is all so weird I'm inclined to say that one-sided dot is probably a syntax error, and reject it. It's too dangerously ambiguous between the syntax for 'proper' dot notation and function composition. Or is there something I'm not understanding? [Good to see another NZ'er on the list, by the way.] AntC _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
On Tue, 31 Jan 2012 23:10:34 -0700, Anthony Clayden <[hidden email]> wrote: > I'm proposing x.f is _exactly_ f x. That is, the x.f gets > desugared at an early phase in compilation. Anthony, I think part of the concern people are expressing here is that the above would imply the ability to use point-free style. But this orthogonality is disavowed by your exception: > A 'one-sided dot doesn't mean anything. I haven't read the underlying proposals, so I apologize if the following is covered, but my understanding of the discussion is that the x.f notation is intended to disambiguate f to be a field name of the type of x and therefore be advantageous over "f x" notation where f is presently in the global namespace. With your exception, I still cannot disambiguate the following: data Rec = { foo :: String } foo :: Rec -> String foo = show rs :: [Rec] rs = [ ... ] bar = map foo rs If the exception doesn't exist, then I could write one of the following to clarify my intent: bar = map foo rs baz = map .foo rs -- -KQ _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Kevin Quick <quick <at> sparq.org> writes:
> > > On Tue, 31 Jan 2012 23:10:34 -0700, Anthony Clayden > <anthony_clayden <at> clear.net.nz> wrote: > > I'm proposing x.f is _exactly_ f x. That is, the x.f gets > > desugared at an early phase in compilation. > > Anthony, > > I think part of the concern people are expressing here is that the above > would imply the ability to use point-free style. But this orthogonality > is disavowed by your exception: > > > A 'one-sided dot doesn't mean anything. > Kevin, thank you for helping me clarify my descriptions. I admit my 'proposal' is probably a bit hard to follow at the moment, because it lives in a series of emails, rather than all in a coherent wiki page. It's also possibly confusing because there are three differing proposals in play, and they all use dot notation for field selection, but they use it somewhat differently. But every proposal supports dot-as-function-composition, providing the dot appears with space on both sides. The discussion with Donn Cave has clarified that under my proposal (but not TDNR or SORF), the dot notation is not necessary. Donn is concerned that older code might be using dot for function composition in contexts that would be ambiguous with field-selection-as-reverse-application. http://www.haskell.org/pipermail/haskell-cafe/2012-January/099008.html So we could make the dot notation a compiler option: - you either keep with H98 syntax, so field selection must be by usual function syntax f x - or use dot notation so that x.f desugars to f x (of course you could still use f x: nothing forces you to use the dot) Let me give some examples to clarify what I mean by 'one-sided' dot: M.f -- no spaces, upper case to left, is qualified name x.f -- no spaces, lower case to left, desugars to f x x . f -- spaces both side of dot, is function composition x. f -- space on one side only, what does that mean? x .f -- space on one side only, what does that mean? In my view, those last two (which I'm calling 'one-sided' dot) are too confusing (for the eye, at least). I would reject them as invalid syntax. H98 might treat them as function composition. (I'm not sure, I wouldn't code like that.) Donn is saying that he doesn't want to break extant code that uses 'one-sided' dot. Fair enough. Under my proposal we could make it a compiler option to stick with H98 syntax, an which case x.f is function composition (I believe), not field selection. I know Wadler's rule about the disproportionate time spent on lexical syntax. SPJ was trying (inter alia) to introduce dot notation to support more OO-type thinking. I'm more familiar with dot-as-field-selector from relational databases, so I'm keen to introduce it. But frankly it's a side-show compared to addressing the namespace issues around records. > I haven't read the underlying proposals, ... No, clearly you haven't from what follows. Pay me (and the other contributors) the respect of doing so before wasting my time. I'm a busy person. I appreciate the feedback on this forum when it's informed. I appreciate that people give their time voluntarily (which is what I'm doing). _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
Fair deuce. With all due respect now included, my same concern still seems to
apply although I believe I poorly stated it originally. Allow me to retry: By declaring partial application an invalid parse, it introduces an exception to point-free style that is at odds with the normal intuition of the uses of "f x". SPJ's SOPR raises it as an issue and indicates he's inclined to disallow it; my concern above would still apply. As I surely mis-understand it (referencing your proposal as RHCT since I haven't seen another reference): SOPR: map (\r -> f r) recs SOPR: map (get f) recs SOPR/alt: qfmap (undefined :: "f") id recs RHCT: map (\r -> f r) recs RHCT: map (\r -> r.$rev_ f) recs RHCT: map ((.$)f) recs If partial application is allowed (against SPJ's inclination and explicitly disallowed in your scheme), I could have: map .f recs in either SOPR or your proposal, which (to me) is an intuitive coordination of the two concepts (point-free/partial application and f.x desugaring). I don't think this is currently a valid parse, so I don't think it breaks existing, but that's not a very well informed opinion either. My concern is a triviality however; my intent was to attempt to assist in trying to clarify a what I perceived as a conceptual gap in the discussion. I am most grateful for the significant time and effort contributed by yourself, SPJ, and all other parties, and I fear I've mostly wasted people's time on syntactic trivialities already well discussed and dismissed. Please do carry on, it's all good stuff. -KQ Quoting AntC <[hidden email]>: > Kevin Quick <quick <at> sparq.org> writes: > > > > > > > On Tue, 31 Jan 2012 23:10:34 -0700, Anthony Clayden > > <anthony_clayden <at> clear.net.nz> wrote: > > > I'm proposing x.f is _exactly_ f x. That is, the x.f gets > > > desugared at an early phase in compilation. > > > > Anthony, > > > > I think part of the concern people are expressing here is that the above > > would imply the ability to use point-free style. But this orthogonality > > is disavowed by your exception: > > > > > A 'one-sided dot doesn't mean anything. > > > > Kevin, thank you for helping me clarify my descriptions. I admit my > 'proposal' > is probably a bit hard to follow at the moment, because it lives in a series > > of emails, rather than all in a coherent wiki page. > > It's also possibly confusing because there are three differing proposals in > play, and they all use dot notation for field selection, but they use it > somewhat differently. > > But every proposal supports dot-as-function-composition, providing the dot > appears with space on both sides. > > The discussion with Donn Cave has clarified that under my proposal (but not > TDNR or SORF), the dot notation is not necessary. Donn is concerned that > older > code might be using dot for function composition in contexts that would be > ambiguous with field-selection-as-reverse-application. > http://www.haskell.org/pipermail/haskell-cafe/2012-January/099008.html > > So we could make the dot notation a compiler option: > - you either keep with H98 syntax, > so field selection must be by usual function syntax f x > - or use dot notation so that x.f desugars to f x > (of course you could still use f x: nothing forces you to use the dot) > > Let me give some examples to clarify what I mean by 'one-sided' dot: > M.f -- no spaces, upper case to left, is qualified name > x.f -- no spaces, lower case to left, desugars to f x > x . f -- spaces both side of dot, is function composition > x. f -- space on one side only, what does that mean? > x .f -- space on one side only, what does that mean? > > In my view, those last two (which I'm calling 'one-sided' dot) are too > confusing (for the eye, at least). I would reject them as invalid syntax. H98 > > might treat them as function composition. (I'm not sure, I wouldn't code like > > that.) > > Donn is saying that he doesn't want to break extant code that uses > 'one-sided' > dot. Fair enough. Under my proposal we could make it a compiler option to > stick with H98 syntax, an which case x.f is function composition (I believe), > > not field selection. > > I know Wadler's rule about the disproportionate time spent on lexical syntax. > > SPJ was trying (inter alia) to introduce dot notation to support more OO-type > > thinking. I'm more familiar with dot-as-field-selector from relational > databases, so I'm keen to introduce it. > > But frankly it's a side-show compared to addressing the namespace issues > around records. > > > > I haven't read the underlying proposals, ... > > No, clearly you haven't from what follows. Pay me (and the other > contributors) > the respect of doing so before wasting my time. I'm a busy person. I > appreciate the feedback on this forum when it's informed. I appreciate that > people give their time voluntarily (which is what I'm doing). > > > > > _______________________________________________ > Haskell-Cafe mailing list > [hidden email] > http://www.haskell.org/mailman/listinfo/haskell-cafe > ------------------------------------------------- This mail sent through IMP: http://horde.org/imp/ _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by quick
> I haven't read the underlying proposals, so I apologize if the following is
> covered, but my understanding of the discussion is that the x.f notation is > intended to disambiguate f to be a field name of the type of x and therefore > be advantageous over "f x" notation where f is presently in the global > namespace. Here's another idea, I'm not sure if this one has come up before: f.x desugars as M.f x, where 'M' is the module that defines the type of 'x'. It's an error if 'x' is not monomorphic. You still can't have the same record name in two different records in the same module, but this way the record selector is monomorphic, and it's up to desugaring to find the defining module and if it's imported (I'd expect an error if not). However, I'd still want the prefix functional notation so it could be composed with other functions, and at that point, why have the postfix dot notation at all? Just say that '#x' requires a monomorphic argument, and desugars to 'M.x' where 'M' is the module that the type of its argument lives in, and combine as normal: (#y . #x) record. This way it's not even specific to records. _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by quick
<quick <at> sparq.org> writes:
> > Fair deuce. With all due respect now included, my same concern still seems to > apply although I believe I poorly stated it originally. Allow me to retry: OK, thank you. > > By declaring partial application an invalid parse, it introduces an exception > to point-free style that is at odds with the normal intuition of the uses of "f x". I'm not (and I don't think any of the other proposals are) trying to declare partial application as an invalid parse. I'm saying that if you want to part- apply function composition (in point-free style), you need to be careful with your syntax, because it's easily confused. A piece of background which has perhaps been implicit in the discussions up to now. Currently under H98: f.g -- (both lower case, no space around the dot) Is taken as function composition -- same as (f . g). f. g -- is taken as func composition (f . g) f .g -- is taken as func composition (f . g) I believe all three forms are deprecated these days, but as Donn points out there may be code still using it. Part of the reason for deprecating is the qualified name syntax, which _mustn't_ have dots. So: M.f -- is qualified f from module M M. f -- is dubious, did you mean M.f? -- or function composition (M . f)? -- with M being a data constructor M .f -- similarly dubious between M.f vs (M . f) The reason those are dubious is that it's relatively unusual to part-apply a data constructor in combination with function composition. More likely you've made a typo. Nevertheless, if that's what you want to do, be careful to code it as (M . f) All the proposals in play are going to change the meaning of f.g. Some of the proposals (not mine) are going to change the meaning of f. and /or .g -- as Donn points out, any/all of these changes may break code. I say it's better to be conservative: reject f. and .g as invalid syntax. (If existing code has f.g as function composition, changing the meaning to field extraction is going to give a type failure, so it'll be 'in your face'.) All proposals are saying that if you want to use dot as function composition you must always put the spaces round the dot (or at least between the dot and any name) -- even if you're part-applying. So: (f .) -- part-apply function composition on f (. g) -- part-apply function composition {- as an exercise for the reader: what does that second one mean? How is it different to the first one? Give an example of use with functions head, tail and a list. -} (f.) -- I say is ambiguous, did you mean (f .) -- or miss out something after the dot ? (.f) -- I say is ambiguous, did you mean (. f) -- or miss out something before the dot ? I'm saying that for both of the above, it's safer to treat them as an invalid parse, and require a space between the dot and the name. > > SPJ's SOPR raises it as an issue and indicates he's inclined to disallow it; my > concern above would still apply. "SOPR"? SPJ's current proposal is abbreviated as "SORF" (Simple Overloaded Record Fields). His older proposal as "TDNR" (Type-Directed Name Resolution). http://hackage.haskell.org/trac/ghc/wiki/Records I don't think either of those disallow partial application of function composition. I do think they discuss how the syntax could be confusing, so require you to be careful. Another piece of background which the discussion is probably not being explicit about (so thank you for forcing me to think through the explanation): under H98 record declarations data Customer = Customer { customer_id :: Int } You immediately get a function: customer_id :: Customer -> Int Then you can apply customer_id to a record, to extract the field. Because the type of customer_id is restricted to exactly one record type, this strengthens type inference. (Whatever customer_id is applied to must be type Customer, the result must be type Int.) For my proposal, I'm trying very hard to be consistent with the H98 style, except to say that field extractor function f can apply to any record type (providing it has field f). Specifically, if the f field is always a String, we can help type inference. The type of f is (approximately speaking): f :: (Has r Proxy_f String) => r -> String Or I prefer SPJ's suggested syntactic sugar: f :: r{ f :: String} => r -> String But type inference for r is now harder: we have to gather information about r from the type environment where f is applied to r, enough to figure out which record type it is; then look up the instance declaration (generated from the data decl) to know how to extract the f field. That much isn't too hard. The really difficult part is how to do that in such a way that we can also update f to produce a new r, and cope with all the possible things f might be - including if f is polymorphic or higher-ranked. (The "trying hard" is why my Record Update for Higher-ranked or Changing Types contained such ugly code.) So I'm trying to support mixing H98 record fields with new-style poly-record field extractors. If you see in code: f r (And you know already that r is a record and f is a field -- perhaps you're working in a database application). Then you know we're extracting a field from a record, whether it's a H98 record or a new-style record. Similarly: r.f -- desugars to f r, so you know just as much What's more, perhaps you've got new-style records in your module, but you're importing a H98 record definition from some other module. Then: customer_id customer -- extracts the customer_id from the record customer.customer_id -- means just the same Wow!! we've just used dot-notation on H98-style records, and we didn't need to change any code at all in the imported module. > > As I surely mis-understand it (referencing your proposal as RHCT since I haven't > seen another reference): You're right that there isn't a name for my proposal, and I definitely need one. (I take it "RHCT" comes from Record Update for Higher-ranked or Changing Types. Doesn't quite trip off the tongue, I'd say.) I'm thinking: "DORF" -- Declared Overloaded Record Fields -- The "ORF" part is similar to SPJ's SORF. -- The "Declared" means you have to declare a field, -- before using it in a data decl. -- Or the "D" might mean "Dictionary-based" as in data dictionary -- (not "dictionary" in the sense of "dictionary-passing") In these examples you're giving, I assume recs is a list of records(?). I don't understand what you're doing with the "SOPR" items, so I've cut them. > > ... In the "RHCT" examples, I assume r is a record, f is a field (selector function) -- or is it 'just some function'?, rev_ is a field selector for a higher-ranked function (to reverse lists of arbitrary type), .$ is the 'fake' I used to simulate dot-as-field-selector. Thank you for reading all that so closely. > RHCT: map (\r -> f r) recs is the same as: map f recs -- by eta reduction so map f takes a list of records, returns a list of the f field from each This also works under H98 record fields, with type enforcement that the records must be of the single type f comes from. > RHCT: map (\r -> r.$rev_ f) recs Beware that (.$) is an operator, so it binds less tightly than function application, so it's a poor 'fake' syntactically. Did you mean .$ to simulate dot-notation to extract field rev_ from r? Then put: map (\r -> (r.$rev_) f) recs This takes the Higher-Ranked reversing function from each record in recs, and (on the face of it) returns a list obtained by applying it to f. I've assumed above that f is a field selector function (or 'just some function'). So it's not a list. So you'll get a type error trying to apply (r.$rev_) to f. If you meant to apply (r.$rev_) to the f field in r, put: map (\r -> (r.$rev_) (r.$f)) recs For the type to work, this requires the f field to be a list. The map returns a list of reversed lists from the f field of each record. > RHCT: map ((.$)f) recs If you mean this to return a list of the f fields from recs, put: map f recs I don't know what else you could be trying to do. > > If partial application is allowed (against SPJ's inclination and explicitly > disallowed in your scheme), I could have: > > map .f recs If you mean this to return a list of the f fields from recs, put: DORF: map f recs -- are you beginning to see how easy this is? I'm saying the ".f" should be rejected as too confusing. (That is, under DORF aka RHCT. Under SORF or TDNR I'm not sure, which is why I don't like their proposals for dot notation, which is why I re-engineered it so that dot notation is tight-binding reverse function application **and nothing more**.) I don't know what else you could be trying to do, but if you're trying to use dot as function composition (part-applied), put: map (. f) recs But this won't extract an f field from recs (exercise for the reader). > ... my intent was to attempt to assist in trying > to clarify a what I perceived as a conceptual gap in the discussion. I am most > grateful for the significant time and effort contributed by yourself, SPJ, and > all other parties, and I fear I've mostly wasted people's time on syntactic > trivialities already well discussed and dismissed. Please do carry on, it's all > good stuff. > > -KQ > Thank you Kevin, we got there in the end. Your questions did help me clarify and explain what was implicit. I think in general that syntax is trivial, but for one thing we've got very complex syntax already in Haskell. Our 'syntax engineering' has got to be careful to 'fit in', and not use up too many of the options that are still available. What's special with dot syntax is it's well-established and with well- established (range of) meanings in other programming paradigms. If we introduce dot-notation into Haskell, we have to try to make it behave like those paradigms, but in a 'Haskelly' way. [To go a little off-topic/out of scope. My gold standard is polymorphic/anonymous records with concatenation, merge, projection, extension, everything you get in relational algebra. I don't want to use up all the design options just getting through the current namespace restrictions -- infuriating though they are.] AntC _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
|
In reply to this post by AntC
On 1/02/2012, at 7:10 PM, Anthony Clayden wrote: > >> On 1/02/2012, at 11:38 AM, AntC wrote: >>> As soon as you decide to make 'virtual record selectors' >>> just ordinary functions (so they select but not update) >>> , then you can see that field names are also just >> ordinary functions (for selection purposes). So the >>> semantics for field 'selection' (whether or not you use >>> dot notation) is just function application. So >> Type-Directed Name resolution is just instance resolution. >>> So it all gets much easier. >> >> >> Richard O'Keefe wrote: >> ... Making f x >> and x.f the same is pretty appealing, but it is imaginable >> that the former might require importing the name of f from >> a module and the latter might not. That is to say, it lets >> f and .f have completely different meanings. Oh the joy! >> Oh the improved readability! -- on some other planet, >> maybe. >> > Hi Richard, I'm not sure I understand what you're saying. I'm saying that if dot is to do anything at all that it does not do now, then f x and x.f being identical is sort of OK ( though it does rather clash with f . g), but any differences between them would be confusing. > > This is all so weird I'm inclined to say that one-sided dot > is probably a syntax error, and reject it. It wasn't a syntax error, it just wasn't intended to be Haskell code at all, just an ad hoc English text abbreviation for "f occurring after a dot". Of course (x.) = \f -> f x and (.f) = \x -> f x are precisely the kind of sections people will expect to be legitimate once they've seen (x.f)... Of course, if f x and x.f mean the same thing, we don't need x.f, do we? _______________________________________________ Haskell-Cafe mailing list [hidden email] http://www.haskell.org/mailman/listinfo/haskell-cafe |
| Powered by Nabble | Edit this page |
