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
Tutorial Playground #91
Conversation
//: * Functional Programming | ||
//: * What that dumb 'M' word is/does/means | ||
|
||
//: # Introduction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rnapier I know you stressed pre-reqs in the last pull request. Is there anything you would add/remove from this list? Any better links than wikipedia would be helpful too.
I'm trying to get SwiftCheck working with DateTime (https://github.com/zbeckman/DateTimeKit). I get that I need to create an Seeing a tutorial on how to add support for NSDate would probably be a great example (and it would be a nice addition to SwiftCheck). |
NSDate cannot be used directly (dumbass Self requirement restrictions on protocol instances). You have to wrap it. |
There, I included it in the tutorial. It's a pretty great way to introduce modifiers. |
Excellent. That's another major real world problem we run into all the time. (Also the fact that |
//: about some glue? | ||
|
||
// Concatenates 5 strings together in order. | ||
func glue5(l : String) -> String -> String -> String -> String -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should work too:
func glue5(l: String)(m: String)(m2: String)(m3: String)(r: String) {
return l + m + m2 + m3 + r
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely. I'll use that.
//: `ap` comes from [Applicative Functors](http://staff.city.ac.uk/~ross/papers/Applicative.html) and is | ||
//: used to "zip together" `Gen`erators of functions with `Gen`erators of of values, applying each function | ||
//: during the zipping phase. That definition is a little hand-wavey and technical, so for now we'll say that | ||
//: `ap` works like "glue" that sticks special kinds of generators togethers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a dumb and lazy and inaccurate simplification. I think I'll use a diagram.
//: individual passing cases in a few scattershot unit tests, but declare and enforce immutable | ||
//: properties that better describe the intent and invariants of our programs. If you would like | ||
//: further reading, see the files `Arbitrary.swift`, `Test.swift`, `Modifiers.swift`, and | ||
//: `Property.swift`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like a list of external links here too
I like where this is going, but I'm struggling to use it in my own code. You have a lot of nice examples like The specific case I was trying to implement was "array of |
I can see 2 ways of going about your specific example:
let arrayGen = UInt8.arbitrary.proliferateSized(8)
property("...") <- forAll(arrayGen) { x in
/// ...
}
property("...") <- forAll { (x : [UInt8]) in
return (x.count == 8) ==> {
/// ...
}
} The former is more conducive to explanation than the latter. |
assert(!xs.isEmpty, "Gen.fromElementsOf used with empty sequence") | ||
|
||
return choose((xs.startIndex, xs.endIndex.predecessor())).fmap { i in | ||
public static func fromElementsOf<S : Indexable where S.Index : protocol<Comparable, RandomType>>(xs : S) -> Gen<S._Element> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This evil is only temporary.
Does the filtering (implication) process at up my 100 tests? I mean, if I pick 100 arrays and only 2 of them are the right length, do I run just those 2, or do I keep implicating until get I get 100 things to try? I didn't realize the |
//: Tests are local, atomic entities that ideally only use the data given to them to match a | ||
//: user-defined specification for the behavior of a program or algorithm. While this may | ||
//: seem draconian, the upshot of following these [unwritten] rules is that the produced tests become | ||
//: "law-like", as in a [Mathematical or Scientific Law](https://en.wikipedia.org/wiki/Laws_of_science). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zbeckman Does this further your understanding of why your use-cases are a little strange w.r.t. this library?
Hm. I see your points. However – let's say you wanted to write a test that ensures a given list of data is populated with at least 100 (or more?) "valid" sets of data. We could say a valid piece of data is a "positive Int" or a "well formed email" or a "password/salt pair." Doesn't really matter – one thing I run into in the real world is a two phase test plan:
The above example was a bit contrived but that's what it was getting at... Validate the properties, and THEN verify the program. |
Then you would make part 1 a A spec is not the place to be generating data, a spec is a place where you validate your assumptions against your model. The testing block works best as a function from filtered inputs to truthy and falsy outcomes. |
One thing to understand about how tools like SwiftCheck get applied... and I'm saying this from about 30 years in SQA, from the Defense Department to little startups... it gets applied in a very "practical" way. Teams that are striving (and under the gun) to deliver working software end up applying it to solve their immediate need. That includes things like:
I realize some of these fall outside the scope of what SwiftCheck/QuickCheck was really designed to do, but when you give a programmer a tool and a problem... ;-) |
I know it's a very strange thing to be told to throw out that kind of experience for this newfangled abstract nonsense, but there are very serious implications to this whole thing. When you get down to properties, I mean really get down to properties, you only have 2 steps:
Step 1 lets you break down your problem into properties, your assumptions become inputs to functions. |
(And yes, making a custom shrinker makes sense – however, I can pretty much guarantee that 80% of the people that use this library will be utterly confused when they write an email generator that explicitly does NOT include spaces... and then they see failure cases that do – that is the only reason I'm suggesting you go down this path – to avoid further confusion). |
Utterly, no. I catch your meaning tho. I've carved out room for another example under the modifier types one. I will solve your problem there. |
There, that should suffice. |
Looking back at our conversation, I have a question: How would you write a generator that would create a list of 100 things that you know meet a certain program specification? (Perhaps email addresses that you know pass the
|
It's more efficient to be correct by construction than to be correct by filtering out the totality of strings SwiftCheck can generate. I wouldn't make a generator of strings with a let g = Gen.sized >>- { n in
return myGen.proliferateSized(n)
} >>- Gen.fromElementsOf |
How would I go about having one test run a larger set of property tests (say 1000 rather than 100)? |
That is accomplished in QuickCheck by modifying the stdArgs options. I haven't explored a satisfactory way of doing the same in Swift (the last version's syntax couldn't allow for multiple arguments in a subscript). If you'd like to try, all the machinery is there. |
What about adding a few props to Testable (maxRun, maxFail, things like that)? |
(I can give it a try but I'm no expert on this syntax). |
No, don't do that! There's an internal struct called |
With that, I'm done. I will absolutely accept modifications to this in the form of pull requests, but any future questions about any part of the framework need to be separate issues from here on out. Thank you all for your help with this 💕 ⛵ |
Awesome! This is a big gain the project. Question: Can you generate an HTML page from the tutorial, stick it into the project, and link to it from the readme.md so it's on the wiki? That would be great if it could be done. By the way... my comment on adding props to Testable where motivated by ScalaCheck, where I can do this (and which I really like): property("Unicode email addresses validate correctly") <- forAll(Generators.emailAddress) { (e : String) in
f.text = e
return f.isValid
}.minSize(200).maxDiscard(50) Anyhow, food for future thought... |
Why HTML? The playground comes bundled with the project now, so you have access to it even if you build with carthage. The tutorial is designed to be interactive. As for the other stuff, like I said, it's all there, it just needs a public interface. |
So that it's on the wiki. Our team doesn't get the Carthage checkout since frameworks are handled by me and then committed in git. Nothing like good docs on the web. |
Do you know of any tools to extract that kind of information from a playground? |
No afraid not... will keep my eyes open for something though. |
No description provided.