Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Add PredicateSet. #48

Merged
merged 13 commits into from
Feb 16, 2015
Merged

Add PredicateSet. #48

merged 13 commits into from
Feb 16, 2015

Conversation

amackworth
Copy link
Contributor

A stab at addressing #30.

😸

@robrix
Copy link
Owner

robrix commented Feb 15, 2015

Nice, looks good! I’ll do a more thorough review later on, but for now, could you please document all public functionality? Copying the docs from Multiset is fine, we just want to have something show up when you option-click a symbol.

public let predicate: Element -> Bool

public init() {
self.predicate = { (element: Element) in false }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest { _ in false }

@robrix robrix self-assigned this Feb 15, 2015
@robrix
Copy link
Owner

robrix commented Feb 15, 2015

Because I forgot to note it: review complete ✨

@amackworth
Copy link
Contributor Author

Alright, I think this is now ready to go! 🎉

self.predicate = { element in
return predicate(element) != nil
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we’ve removed Q and family, I think we can probably remove this as well. I’m primarily concerned about it being confusing because:

  • every type T is promotable to Optional<T>, and
  • the returned PredicateSet bears no relationship to T whatsoever.

I.e. by my understanding of Swift’s rules, you could write PredicateSet<String>(count) right now and be very confused as to why it contains "".

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the penalty for removing this is that consumers have to do an explicit nil comparison which they ought to be doing anyway for clarity’s sake.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@robrix
Copy link
Owner

robrix commented Feb 15, 2015

A few small notes. This is looking really great!

For full set comprehensions (with SequenceType conformance and all), we’ll want something with some sort of monoid structure, but this is really nice.

@amackworth
Copy link
Contributor Author

@robrix Ooh, interesting! Could you elaborate more on how that would look?

@robrix
Copy link
Owner

robrix commented Feb 16, 2015

:shipit:

robrix added a commit that referenced this pull request Feb 16, 2015
@robrix robrix merged commit 957b83b into robrix:master Feb 16, 2015
@amackworth
Copy link
Contributor Author

🎉 🎊

@robrix
Copy link
Owner

robrix commented Feb 16, 2015

@amackworth Upon reflection, monoids might not actually suffice. I was thinking of structural induction, a la the typical inductive definition of .

Fortunately, that would be more complicated than we require: I think we’d just want (potentially infinite) SequenceTypes as inputs, and a closure as an output. (I believe this to be the approach taken by Python’s list comprehensions.)

That would give us e.g.:

{ 2𝑥 + 1 | 𝑥 ∈ ℕ, 𝑥 < 5 } = { 1, 3, 5, 7, 9 }

In Swift’s notation, we might instead say:

lazy(0..<Int.max)
    .filter { $0 < 5 }
    .map { 2 * $0 + 1 }

But we want a couple of extra things from this:

  1. It should stop trying to produce new elements once $0 < 5 fails, so that you can enumerate it eagerly with for/in.

    That’s sort of like a -takeUntilBlock: in RAC 2.0. In our case it implies that the system can reason on 0..<Int.max sufficiently to infer that it’s monotonic, and what that implies for <.

  2. It should be more concise, e.g.:

    ComprehensionSet(0..<Int.max, <, 5) { 2 * $0 + 1 }

I have some ideas about how we can achieve both of those but they’re a bit nebulous and hand-wavy. Specifically, I’m passing < and 5 in separately instead of as a predicate closure because we can construct predicates like so:

init<T>(HalfOpenInterval<T>, (T -> PredicateTerm<T>), T, T -> Element)

public enum PredicateTerm<T: Comparable> {
    case LessThan(Box<T>)func apply(x: T) -> Bool {
        switch self {
        case let LessThan(v):
            return x < v.value
        
        }
    }
}

public prefix func < <T: Comparable>(x: T) -> PredicateTerm<T> {
    return PredicateTerm.LessThan(Box(x))
}

and thus retain the information we need about both the input sequence and the predicate to ensure the comprehension is finite.

@robrix
Copy link
Owner

robrix commented Feb 16, 2015

There’s a lot of inspiration to be found in LINQ and Parallel LINQ, too.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants