Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sequence functions for various monads #296

Merged
merged 2 commits into from
Apr 15, 2016
Merged

Add sequence functions for various monads #296

merged 2 commits into from
Apr 15, 2016

Conversation

ryanbooker
Copy link
Contributor

@CodaFi Is this what you were after (re: sequence conversation on Twitter https://twitter.com/CodaFi_/status/707423595601891328)?

sequence added for:

  • Array
  • Either
  • Identity
  • List
  • Optional
  • Proxy
  • State
  • String
  • Stream
  • Writer

I haven't been able to get Reader to type check.

@CodaFi
Copy link
Member

CodaFi commented Mar 13, 2016

Looks great! Can you throw in the bare minimum SwiftCheck properties for all of these (basically this)?

@ryanbooker
Copy link
Contributor Author

Yep no problem. I'll get to that in the next day or so.

@mpurland
Copy link
Member

The Reader issue seems to be an issue related to associated types.

screen shot 2016-03-16 at 2 28 32 pm

@CodaFi
Copy link
Member

CodaFi commented Mar 16, 2016

Oh god, that looks like Swift thinks we need rank-2 polymorphism.

@ryanbooker
Copy link
Contributor Author

@CodaFi I have some questions with the tests.

While looking at your SwiftCheck test for sequence on Gen, I noticed (at least it looks like) the order is being reversed by sequence. Is that intended?

Is the shape of a test for a give type M supposed to look like the following?

property("Gen.sequence occurs in order") <- forAll { (xs : [String]) in
    return forAllNoShrink(sequence(xs.map(M.pure))) { ss in
        return ss == xs
    }
}

If so, it seems that forAllNoShrink etc need to be implemented for all the types being sequenced, in SwiftCheck… Is that correct or have I missed something?

@CodaFi
Copy link
Member

CodaFi commented Mar 17, 2016

The file I linked to is on a branch where I fixed the order issue and committed that test. Use it instead of master.

@CodaFi
Copy link
Member

CodaFi commented Apr 11, 2016

@ryanbooker If you want me to write the tests for this, I can merge this and do it.

@mpurland
Copy link
Member

Do we need to write the sequence function for Reader? I'm worried this may be a language/compiler limitation.

@CodaFi
Copy link
Member

CodaFi commented Apr 11, 2016

I can take a crack at it with the tests.

@ryanbooker
Copy link
Contributor Author

Sorry. I was totally side tracked by Easter and work. I'm happy to write the tests, but I think I need some pointers/direction. I'm not particularly familiar with QuickCheck style testing. My first question:

Is the shape of a test for a give type M supposed to look like the following?

property("sequence occurs in order") <- forAll { (xs : [String]) in
    return forAllNoShrink(sequence(xs.map(M.pure))) { ss in
        return ss == xs
    }
}

If so, it seems that forAllNoShrink etc need to be implemented for all the types being sequenced, in SwiftCheck… Is that correct or have I misunderstood what is happening?

@ryanbooker
Copy link
Contributor Author

A Haskeller friend also pointed out that sequence . fmap (what I actually needed sequence for) is traverse, should I/we look into that?

@CodaFi
Copy link
Member

CodaFi commented Apr 12, 2016

Traversable isn't typesafe in this language and doesn't generalize from Foldable at all. However, MonoTraversable will work great. As for the tests, I've implemented either Arbitrary or the WitnessedArbitrary shim for each one.

@ryanbooker
Copy link
Contributor Author

To check my understanding, is this the right track?

property("sequence occurs in order") <- forAll { (xs : [String]) in
    return forAllNoShrink(Gen.pure(sequence(xs.map(Optional.pure)))) { ss in
        return ss! == xs
    }
}

xs.map(M.pure): transform the list into [M x]
sequence: sequence that list to get an M [x]
Gen.pure: create a generator for the M [x]
Then compare the [x] with xs

@CodaFi
Copy link
Member

CodaFi commented Apr 13, 2016

Yep! It's mostly for the order invariant.

@ryanbooker
Copy link
Contributor Author

Tests added. Stream test is commented out. It causes the test suite to grind to a halt. I also added a special case to sequence for Arrays. If there's a better way, please let me know. But it was needed to ensure sequence([]) returns [], rather than [[]].

@CodaFi
Copy link
Member

CodaFi commented Apr 14, 2016

Yeah, that's to be expected. What happens if you use take on arbitrary subsequences to lower the law to the array ones?

@ryanbooker
Copy link
Contributor Author

I'm not sure I understand what you mean. Do you mean basically remove sequence for Stream? So the test would be something like (or not even needed):

property("sequence occurs in order") <- forAll { (xs : [String]) in
    let seq = sequence(xs.map({ Stream.pure($0).take(1) }))
    return forAllNoShrink(Gen.pure(seq)) { ss in
        return ss.first ?? ss == xs
    }
}

@CodaFi
Copy link
Member

CodaFi commented Apr 15, 2016

No, leave it in. But equality on streams is not a thing, but equality on lists is so you see how in the Stream Spec I lower all the equality calls with take to equalities on streams of arbitrary stream heads? That's what I mean.

@ryanbooker
Copy link
Contributor Author

It seems to be the actual sequence call that can take forever… I only compare the head of the stream against the original list for equality, and changing that to use take doesn't help. I assume I've just made a mistake that I can't see due to an incomplete understanding of Stream.

@CodaFi
Copy link
Member

CodaFi commented Apr 15, 2016

Yeah, so rather than invoke sequence on the Stream, just take an arbitrary number of elements and sequence the resulting Array instead.

@ryanbooker
Copy link
Contributor Author

Yeah, ok. That's what I did above. But that's not really testing sequence on Stream then.

@CodaFi
Copy link
Member

CodaFi commented Apr 15, 2016

Not like we can, though. The laws require an equality that necessitates forcing the stream.

}

property("sequence occurs in order") <- forAll { (xs : [String]) in
let seq = sequence(xs.map({ x in Stream.pure(x).take(1) }))
Copy link
Member

Choose a reason for hiding this comment

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

Can this take an arbitrary head of the sequence (an extra forAll)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It slows down to the point of not completing in any reasonable time. Assuming you mean wrapping it like other uses of take.

    return forAll { (n : UInt) in
    ...
    }

Copy link
Member

Choose a reason for hiding this comment

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

What does limiting the size of generated lists do (either with custom arguments or Gen.fromElementsIn(1...10), etc.)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't make a difference, even with 1...2.

Copy link
Member

Choose a reason for hiding this comment

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

Damn 😕

@CodaFi
Copy link
Member

CodaFi commented Apr 15, 2016

Welp, given the issues we discussed seem hard limits, and in the interest of honoring the great work you did here, I feel it's only appropriate to merge this.

Thanks for putting up with me through review ❤️ and for the patch ✨

@CodaFi CodaFi merged commit dd9b46c into typelift:master Apr 15, 2016
@ryanbooker
Copy link
Contributor Author

Thanks. No drama. It was a good learning process. Seems only fair seeing I use the project. :)

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

Successfully merging this pull request may close these issues.

None yet

3 participants