Implement an Equatable Void (None) Type

Implement an Equatable Void (None) type

Trying again in Swift 2.0, it seems that Void can be initialized as Void():

public enum Result<T, Error: ErrorType> {
case Success(T)
case Failure(Error)

var value: T? {
switch self {
case .Success(let v):
return v
case .Failure(_):
return nil
}
}

/// Constructs a success wrapping a `value`.
public init(value: T) {
self = .Success(value)
}

/// Constructs a failure wrapping an `error`.
public init(error: Error) {
self = .Failure(error)
}
}

enum MyError: ErrorType {
case AnError
}

let result = Result<Void, MyError>(value: Void())

let success = result.value != nil // returns true
let error = result.value == nil // returns false

Equatable alternatives for Void in Swift

I get an error during compilation: FooBarBaz does not conform to protocol Equatable

This half-answer focuses on explaining why the approach you've tried yourself will not (yet) work.

There is a limitation, currently, with conditional conformances, that will limit you from using this particular technique to achieve your goal. Citing SE-0143: Conditional conformances, implemented in Swift 4.1:

Multiple conformances


Swift already bans programs that attempt to make the same type conform
to the same protocol twice, e.g.:

...

This existing ban on multiple conformances is extended to conditional
conformances, including attempts to conform to the same protocol in
two different ways.

...

The section overlapping conformances describes some of the
complexities introduced by multiple conformances, to justify their
exclusion from this proposal. A follow-on proposal could introduce
support for multiple conformances, but should likely also cover
related features such as private conformances that are orthogonal to
conditional conformances.

Which wont allow us to construct multiple conditional conformances as e.g.:

struct Box<T> { }

extension Box: Equatable where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

// error: redundant conformance of 'Box<T>' to protocol 'Equatable'
extension Box: Equatable where T: Equatable {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

On the other hand, if we look at your own example:

struct Box<T> { }

extension Box: Equatable where T: Equatable {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

extension Box where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

// error: type 'Void' does not conform to protocol 'Equatable'
func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>())

Swift accurately identifies that Box<Void> is not Equatable: the extension Box where T == Void will not mean that Box<Void> conforms to Equatable, as it does not leverage a conditional conformance of Box to Equatable when T is Void (it just provides a == method in case T is Void.

Conditional conformances express the notion that a generic type will
conform to a particular protocol only when its type arguments meet
certain requirements.


As a side note, the following example yields expected results:

struct Box<T> { }

extension Box: Equatable where T == () {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>()) // Box<()> is Equatable

whereas, peculiarly, replacing the conditional conformance of Box to Equatable if T == () with the typedef of (), namely Void, crashes the compiler:

struct Box<T> { }

extension Box: Equatable where T == Void {
static func ==(lhs: Box<T>, rhs: Box<T>) -> Bool {
return true
}
}

func foo<T: Equatable>(_ _: T) { print(T.self, "is Equatable") }
foo(Box<Void>()) // compiler crash

Assertion failed: (isActuallyCanonicalOrNull() && "Forming a CanType
out of a non-canonical type!"), function CanType,

file /Users/buildnode/jenkins/workspace/oss-swift-4.1-package-osx/swift/include/swift/AST/Type.h,
line 393.

...

Edit: apparently is a (now resolved) bug:

  • SR-7101: Compiler crash when implementing a protocol for an enum using generics

How do I define equality for a generic ResultT type?

I don't think that is possible at present. Void is the type of the
empty tuple (), and tuples cannot adopt protocols (a discussion about
that topic starts at [swift-evolution] Synthesizing Equatable, Hashable, and Comparable for tuple types).

A possible workaround (as suggested by @Hamish above) is to use a custom
type instead of Void:

struct Unit: Equatable {
static var unit = Unit()
public static func ==(lhs: Unit, rhs: Unit) -> Bool {
return true
}
}

let res = Result.success(Unit.unit)

I initially though that once SE-0143 Conditional conformances is implemented then one could define

public enum Result<T> {
case success(T)
case error
}

public extension Result: Equatable where T: Equatable {
public static func ==(lhs: Result, rhs: Result) -> Bool {
// ...
}
}

public extension Result: Equatable where T == Void {
public static func ==(lhs: Result, rhs: Result) -> Bool {
return true
}
}

without the need the make Void itself Equatable.

However (again attribution goes to @Hamish) this won't work
because multiple conformances won't be permitted.

Swift Struct doesn't conform to protocol Equatable?

OK, after lots of searching, it's working...

struct MyStruct {
var id: Int
var value: String

init(id: Int, value: String) {
self.id = id
self.value = value
}

var description: String {
return "blablabla"
}

}

extension MyStruct: Equatable {}

func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
let areEqual = lhs.id == rhs.id &&
lhs.value == rhs.value

return areEqual
}

My Struct was in a class, so it didn't work.. I moved this Struct out of my class and now it's good :)

Flutter: Equatable props getter with optional parameter

The base Equatable.props getter is declared to return a List<Object?>, not List<Object>. Fixing your override to match would allow you to store null.

using Equatable class with flutter_bloc

For comparison of data, we required Equatable. it overrides == and hashCode internally, which saves a lot of boilerplate code. In Bloc, we have to extend Equatable to States and Events classes to use this functionality.

 abstract class LoginStates extends Equatable{}

So, that means LoginStates will not make duplicate calls and will not going to rebuild the widget if the same state occurs.

Define State:

class LoginInitialState extends LoginStates {}

Define State with props:

props declared when we want State to be compared against the values which declared inside props List

class LoginData extends LoginStates {
final bool status;
final String userName;
const LoginData({this.status, this.userName});
@override
List<Object> get props => [this.status, this.userName];
}

If we remove the username from the list and keep a list like [this.status], then State will only consider the status field, avoiding the username field. That is why we used props for handling State changes.

Bloc Stream Usage:

As we extending State with Equatable that makes a comparison of old state data with new state data. As an example let's look at the below example here LoginData will build a widget only once, which will avoid the second call as it is duplicated.

@override
Stream<LoginStates> mapEventToState(MyEvent event) async* {
yield LoginData(true, 'Hello User');
yield LoginData(true, 'Hello User'); // This will be avoided
}

Detail Blog: https://medium.com/flutterworld/flutter-equatable-its-use-inside-bloc-7d14f3b5479b

Java: How to workaround the lack of Equatable interface?

Consider wrapping your foreign class inside your own instead.

public class Foreign {
// undesired equals() and hashCode() implementation
}

public class ForeignWrapper {
private Foreign foreign;

public ForeignWrapper(Foreign foreign) {
this.foreign = foreign;
}

public void equals() {
// your equals implementation, using fields from foreign
}

public int hashCode() {
// your hashCode implementation, using fields from foreign
}

}

Then add new ForeignWrapper(foreign) to the standard HashSet / HashMap. Not applicable in all situations, but maybe in yours.

Why Swift Tuples can be compared only when the number of elements is less than or equal to 6?

Background: Tuples aren't Equatable

Swift's tuples aren't Equatable, and they actually can't be (for now). It's impossible to write something like:

extension (T1, T2): Equatable { // Invalid
// ...
}

This is because Swift's tuples are structural types: Their identity is derived from their structure. Your (Int, String) is the same as my (Int, String).

You can contrast this from nominal types, whose identity is solely based off their name (well, and the name of the module that defines them), and whose structure is irrelevant. An enum E1 { case a, b } is different from an enum E2 { case a, b }, despite being structurally equivalent.

In Swift, only nominal types can conform to protocols (like Equatble), which precludes tuples from being able to participate.

...but == operators exist

Despite this, == operators for comparing tuples are provided by the standard library. (But remember, since there is still no conformance to Equatable, you can't pass a tuple to a function where an Equatable type is expected, e.g. func f<T: Equatable>(input: T).)

One == operator has to be manually be defined for every tuple arity, like:

public func == <A: Equatable, B: Equatable,                                                       >(lhs: (A,B        ), rhs: (A,B        )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, >(lhs: (A,B,C ), rhs: (A,B,C )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, >(lhs: (A,B,C,D ), rhs: (A,B,C,D )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, >(lhs: (A,B,C,D,E ), rhs: (A,B,C,D,E )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool { ... }

Of course, this would be really tedious to write-out by hand. Instead, it's written using GYB ("Generate your Boilerplate"), a light-weight Python templating tool. It allows the library authors to implement == using just:

% for arity in range(2,7):
% typeParams = [chr(ord("A") + i) for i in range(arity)]
% tupleT = "({})".format(",".join(typeParams))
% equatableTypeParams = ", ".join(["{}: Equatable".format(c) for c in typeParams])

// ...

@inlinable // trivial-implementation
public func == <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
${", ".join("lhs.{}".format(i) for i in range(1, arity))}
) == (
${", ".join("rhs.{}".format(i) for i in range(1, arity))}
)
}

Which then gets expanded out by GYB to:

@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable>(lhs: (A,B), rhs: (A,B)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1
) == (
rhs.1
)
}

@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2
) == (
rhs.1, rhs.2
)
}

@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable>(lhs: (A,B,C,D), rhs: (A,B,C,D)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3
) == (
rhs.1, rhs.2, rhs.3
)
}

@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable>(lhs: (A,B,C,D,E), rhs: (A,B,C,D,E)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4
) == (
rhs.1, rhs.2, rhs.3, rhs.4
)
}

@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4, lhs.5
) == (
rhs.1, rhs.2, rhs.3, rhs.4, rhs.5
)
}

Even though they automated this boilerplate and could theoretically change for arity in range(2,7): to for arity in range(2,999):, there is still a cost: All of these implementations have to be compiled and produce machine code that ends up bloating the standard library. Thus, there's still a need for a cutoff. The library authors chose 6, though I don't know how they settled on that number in particular.

Future

There's two ways this might improve in the future:

  1. There is a Swift Evolution pitch (not yet implemented, so there's no official proposal yet) to introduce Variadic generics, which explicitly mentions this as one of the motivating examples:

    Finally, tuples have always held a special place in the Swift language, but working with arbitrary tuples remains a challenge today. In particular, there is no way to extend tuples, and so clients like the Swift Standard Library must take a similarly boilerplate-heavy approach and define special overloads at each arity for the comparison operators. There, the Standard Library chooses to artificially limit its overload set to tuples of length between 2 and 7, with each additional overload placing ever more strain on the type checker. Of particular note: This proposal lays the ground work for non-nominal conformances, but syntax for such conformances are out of scope.

    This proposed language feature would allow one to write:

    public func == <T...>(lhs: T..., rhs: T...) where T: Equatable -> Bool { 
    for (l, r) in zip(lhs, rhs) {
    guard l == r else { return false }
    }
    return true
    }

    Which would be a general-purpose == operator that can handle tuples or any arity.

  2. There is also interest in potentially supporting non-nominal conformances, allowing structural types like Tuples to conform to protocols (like Equatable).

    That would allow one to something like:

    extension<T...> (T...): Equatable where T: Equatable {
    public static func == (lhs: Self, rhs: Self) -> Bool {
    for (l, r) in zip(lhs, rhs) {
    guard l == r else { return false }
    }
    return true
    }
    }

Filtering array of functions Swift

Non-Nominal Types cannot be filtered. This is because they are not equatable:

i.e. Here are some Non-Nominal Types

  • Any
  • Tuples - soon to be equatable
  • Void aka () aka Empty Tuple
  • Functions aka () -> ()

What you could do is create a struct like so:

struct Method: Hashable, Equatable {
var name: String
var method: () -> () // or whatever type you want
init(_ n: String,_ m: @escaping () -> ()) {
name = n
method = m
}

// Equatable
static func == (lhs: Method, rhs: Method) -> Bool {
return lhs.name == rhs.name
}

// Hashable
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}

So that the name is what checks if methods are equal. But so much for structs.

If you wanted, you could make a similar class instead to keep track of the reference.


Example code:

func function1() {print("foo")}
func function2() {print("bar")}

var array: [Method] = []

array.append(Method("One", function1))
array.append(Method("Two", function2))

let function: Method
function = Method("One", function1)
var filteredArray: [Method] = []
filteredArray = array.filter { $0 != function}

print(filteredArray.count) // 1

filteredArray.first?.method() // prints "bar"


Related Topics



Leave a reply



Submit