I completely agree with Austin here. Automatic derivation (perhaps
through the same mechanisms Joe is talking about) would be a nice
enhancement, but I find it refreshing and advantageous for simple value
types to have very little automatic behavior.
Cheers! Zachary Waldowski zach@waldowski.me
I would prefer Equatable and Hashable to remain opt-in, and for us to
add better support for automatic deriving of implementation.
For something like printing the representation of an object to a
string, there exists a "not wrong" mapping of every possible value to
a string. That is, if my FooStruct doesn't provide a custom
description, having the runtime convert it to something like
"(FooStruct instance)" is still a valid mapping. It might not be
useful, but it's not wrong.
I don't think the same applies for equatability. The universal default
behavior for equating two objects is either correct or incorrect, and
it's not possible to know beforehand which is which. One of the
wonderful things about the current Swift system is that (modulo some
exceptional cases) only things explicitly meant to be equatable with
each other are comparable. We avoid the object-oriented pitfall in
which 'equality' means two different things - equality of value if you
implemented an override properly; a default 'equality of instance'
otherwise (which might be right or wrong). Of course, the same pitfall
wouldn't necessarily apply in our case, but the problem of having a
'default' == impl that allows a developer to falsely assume their type
is being properly compared (or not think about it at all) would still
be present.
Best, Austin
Definitely a +1 on the basics. When you get inheritance involved,
does that complicates things a little bit?
Let's say I have a subclass instance that has corresponding fields
with a superclass instance. Is it equal to said super-class instance
using just member-wise comparisons? Would that be problematic? In
Scala you'd often use a reference to an "equality contract" object
type in order to get "transitive" equality between subclasses and
superclasses, which definitely feels like a step backwards from the
current protocol-driven approach.
(starting a new thread by DaveA's request)
There's a definition of equality that makes sense as a default for
nearly every type in our system:
- Basic types like IntNN, FloatNN, String, etc. have domain-
defined equality,
- Structs and tuples can be considered equal if their corresponding
fields are equal,
- Enums can be considered equal if they carry the same, equal payload,
- Class references can be considered equal if they refer to the
same instance,
- Metatypes can be considered equal if they represent the same type, and
- Existentials can be considered equal if they carry equal values of the
same dynamic type.
and similarly, reasonable hash code implementations could be synthesized
by applying a standard hash combine operation over the components, and a
default ordering could be assigned to values of every type. I think
it's worth considering whether Equatable, Hashable, and/or Comparable,
instead of being explicit protocols, should become universal behavior
like 'print', with customization points to override the default
behavior. If Equatable and Hashable behavior were universal, that would
solve many of the common problems people currently have trying to work
with heterogeneous containers. In object-oriented frameworks, including
Cocoa, Java, and .NET, it is common for the root (NS)Object class to
provide default equality and hashing operations. There are of course
some tradeoffs:
- Universal behavior would require us to either generate code for '==',
'hashValue', and/or '<' for every type, or provide sufficient
reflection info for a common runtime implementation to do it. The reflection-
based approach may be reasonable for print(), since dumping reflection
info only reduces the quality of the default logging behavior, but
'==' and 'hashValue' are more essential to proper behavior, so relying
on reflection might be too slow, and would be brittle when we
introduce the ability to drop reflection info.
- Type safety with '==' is important to prevent accidental '1 == "1"'
type comparsions, and a fully generic 'func ==<T>(x: T, y: T) -> Bool'
could potentially allow those sorts of mixed-type comparisons by
accident. Language rules that constrained when generic parameters can
be resolved to supertypes might help here.
- Function types in Swift do not provide a ready equality operation.
We could provide a default implementation that always returns
'false', perhaps.
- A Comparable ordering can be dreamt up for many types, but it's not
always a stable ordering, or a desired one. Many people have
complained that 'nil < .Some(1)' works for optionals, for instance,
ordering 'nil' below Some values. We could use pointer identity to
order class instances and types, but this wouldn't be a stable
ordering across process runs. That might be good enough for ordered
collections like search trees, but is weaker than what many people
expect '<' to do.
It's my feeling that Equatable and Hashable would make a lot of sense as
universal operations; I'm not so sure about Comparable.
-Joe
¡¡¡
On Tue, Mar 8, 2016, at 05:15 PM, Austin Zheng via swift-evolution wrote:
On Tue, Mar 8, 2016 at 2:02 PM, Brian Pratt via swift-evolution <swift- > evolution@swift.org> wrote:
On Tue, Mar 8, 2016 at 2:54 PM, Joe Groff via swift-evolution <swift- >> evolution@swift.org> wrote:
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution