 public func equal<T: Equatable>(_ expectedValue: T?) -> Predicate<T> {
     return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
         let actualValue = try actualExpression.evaluate()
-        let matches = actualValue == expectedValue && expectedValue != nil
-        if expectedValue == nil || actualValue == nil {
-            if expectedValue == nil && actualValue != nil {
-                return PredicateResult(
-                    status: .fail,
-                    message: msg.appendedBeNilHint()
-                )
-            }
+        switch (expectedValue, actualValue) {
+        case (nil, _?):
+            return PredicateResult(status: .fail, message: msg.appendedBeNilHint())
+        case (nil, nil), (_, nil):
             return PredicateResult(status: .fail, message: msg)
-        }
-        return PredicateResult(status: PredicateStatus(bool: matches), message: msg)
+        case (let expected?, let actual?):
+            let matches = expected == actual
+            return PredicateResult(bool: matches, message: msg)
+        }
     }
 }
 

        var errorMessage: ExpectationMessage =
            .expectedActualValueTo("equal <\(stringify(expectedValue))>")

        if let expectedValue = expectedValue {
            if let actualValue = try actualExpression.evaluate() {
                errorMessage = .expectedCustomValueTo(
                    "equal <\(stringify(expectedValue))>",
                    "<\(stringify(actualValue))>"
                )

                if expectedValue == actualValue {
                    return PredicateResult(
                        status: .matches,
                        message: errorMessage
                    )
                }

                let missing = expectedValue.subtracting(actualValue)
                if missing.count > 0 {
                    errorMessage = errorMessage.appended(message: ", missing <\(stringify(missing))>")
                }

                let extra = actualValue.subtracting(expectedValue)
                if extra.count > 0 {
                    errorMessage = errorMessage.appended(message: ", extra <\(stringify(extra))>")
                }
                return  PredicateResult(
                    status: .doesNotMatch,
                    message: errorMessage
                )
            }
            return PredicateResult(
                status: .fail,
                message: errorMessage.appendedBeNilHint()
            )
        } else {
            return PredicateResult(
                status: .fail,
                message: errorMessage.appendedBeNilHint()
            )
        }
    }
}


public func equal<T: Equatable>(_ expectedValue: [T]?) -> Predicate<[T]> {
    return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
        let actualValue = try actualExpression.evaluate()
        switch (expectedValue, actualValue) {
        case (nil, _?):
            return PredicateResult(status: .fail, message: msg.appendedBeNilHint())
        case (nil, nil), (_, nil):
            return PredicateResult(status: .fail, message: msg)
        case (let expected?, let actual?):
            let matches = expected == actual
            return PredicateResult(bool: matches, message: msg)
        }
    }
}

/// A Nimble matcher allowing comparison of collection with optional type
public func equal<T: Equatable>(_ expectedValue: [T?]) -> Predicate<[T?]> {
    return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in
        guard let actualValue = try actualExpression.evaluate() else {
            return PredicateResult(
                status: .fail,
                message: msg.appendedBeNilHint()
            )
        }

        let doesNotMatch = PredicateResult(
            status: .doesNotMatch,
            message: msg
        )

        if expectedValue.count != actualValue.count {
            return doesNotMatch
        }

        for (index, item) in actualValue.enumerated() {
            let otherItem = expectedValue[index]
            if item == nil && otherItem == nil {
                continue
            } else if item == nil && otherItem != nil {
                return doesNotMatch
            } else if item != nil && otherItem == nil {
                return doesNotMatch
            } else if item! != otherItem! {
                return doesNotMatch
            }
        }

        return PredicateResult(
            status: .matches,
            message: msg
        )
    }
}

