5 ms·
It looks sane and solidly specified. I never trusted DisambiguateRecordFields, so I'll finally ditch a lot of ugly prefixes I stuck in there. The whitespace re
by ben509 6y ago
It looks sane and solidly specified. I never trusted DisambiguateRecordFields, so I'll finally ditch a lot of ugly prefixes I stuck in there.
The whitespace requirements, though, are going to lead to some confusing error messages for newbies who want to type myRecord . someField
- sanxiyn 6y agoWhitespace requirement is unfortunate but probably necessary. Changing meaning of . from function composition to anything else will break approximately 100% of existing Haskell libraries.
- IncRnd 6y agoHow would this break 100% of existing libraries? Is there a breaking case outside of where an existing composed function and record name are identical? Haskell is a strongly typed language, where that doesn't happen.
- runeks 6y agoI am under the impression that it’s undesirable to have the syntactical meaning change based on the type of the expressions involved.
- tikhonj 6y agoHaskell record fields get turned into top-level functions that act as field accessors. That is, if we have a record: data Student = Student { name :: Text, school :: School } we end up with two functions 'name' and 'school' in scope that extract the values of their respective fields: name :: Student -> Text school :: Student -> School This means it is common for field names to be composed with functions. I write code like this all the time: schools = map (schoolId . school) students This is compounded by the fact that a lot of newtypes use record syntax to define functions in this style that are even named like functions: newtype Reader r a = Reader { runReader :: r -> a } Importing runReader from a module, you might not even know that it's implemented as a record field! This is pretty handy since you can change the implementation of runReader from being a record field to a normal function without breaking most of your callers. (In fact, I think this might have happened with runReader being implemented in terms of runReaderT.) This kind of pattern means that people write lots of code that uses record fields as functions, sometimes without even realizing that they are using a record field! The practical upshot is that myRecord . someField is used with . as function composition all the time, so any proposal that cares about backwards compatibility can't change the meaning of . in that context.
- IncRnd 6y agoI think you misread my post. Identical names doesn't just mean the same type. The change appears to just be syntactic sugar and so would not conflate anything.
- tikhonj 6y agoRight, I think I misunderstood your question. Rereading now, it seems you're asking how interpreting a . b as record access only if b is a record field in scope would break existing code. The new record system is implemented on top of a HasField typeclass under the hood. Thanks to the way Haskell's typeclasses work, this means that the instances that define what fields a record has will be in scope when you import the module containing that record—directly or indirectly—even if you don't import the record itself. This means that even if we do not have a record with a field b in scope, we might still have the instance for some other record in scope—leading to ambiguity in previously unambiguous code. Moreover, even if a . b is unambiguous right now, a record with a field called b might be added somewhere deep in the codebase and cause unexpected ambiguities in the future. I suppose you could say that a . b is composition whenever a function b is in scope, even if there is also a record with a field called b. That rule would not break existing code, but it also seems inconsistent and confusing—a.b and a . b are sometimes the same and sometimes different, depending entirely on whether b is in scope. Making the meaning of . purely lexical rather than depending on what's in scope seems a lot more consistent and easier to follow. It's already the case with Haskell anyway, since . can mean function composition or a qualified name depending on the spaces around it. This isn't an ideal situation, but I don't think we can aim for "ideal" in a living language that's 30 years old.
- ben509 6y agoThe breakage problem is interesting. Right now, a.b parses just fine, so existing code will break when this is turned on. And that is an issue as people often turn extensions on per project, but it's also something a code formatter may be able to fix. (Hopefully britanny will get an update to handle this correctly!) They could, In Theory, rename the composition operator selectively when this language extension is enabled. That'd mean Prelude has to change what operators it exports depending on what extensions are enabled, which would be horribly magical.
- platz 6y agosince basically no other language with dot syntax lets you do myRecord . someField with a space in between, it doesn't seem like something that will actually trip newbies up
- sanxiyn 6y agoHuh? It works in C (I just tried) and I bet in most other languages.
- agency 6y agoWorks in JS as well. Though I have never seen anyone put whitespace around field accesses like that, in any language.
- taeric 6y agoNewlines are pretty common. Especially in chained cases. No?
- platz 6y agoi guess i forgot about the newlines case, and just had never seen anyone put spaces in like that without newlines, so figured it didn't exist. strange that it's even allowed.
- codeflo 6y agoThat’s because C-like languages don’t distinguish newlines from other whitespace. If you want foo() .filter(...) .map(...) then you have to allow foo() .filter(...) .map(...) as well, as a matter of principle.
- eru 6y agoYes. Though even Python, which does treat newlines different from other whitespace, allows `object . someMethod` with spaces on either side.