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

Documentarian #90

Merged
merged 18 commits into from Sep 5, 2015
Merged

Documentarian #90

merged 18 commits into from Sep 5, 2015

Conversation

CodaFi
Copy link
Member

@CodaFi CodaFi commented Sep 3, 2015

I'm using this pull request as an opportunity to try and quash a lot more of the ambiguity around using this framework in a meaningful way by providing a large amount of documentation. As such, I'm giving @zbeckman the authority to green light this.

I'd appreciate if you would use this issue to comment on any existing documentation and provide feedback about any new stuff that was provided. As a new user your experience with this library so far is probably the most valuable thing I could have at my disposal for this pull request.

@buildreactive
Copy link

How possible is it to turn the SwiftDoc into HTML doc in the wiki? (I've never done it – though it seems there should be a way...)

I think the biggest issue is really the lack of a comprehensive introductory tutorial, which I realize is hard to write. For example, I didn't even know there was a .noShrinking option because I did not happen to browse that section of the source code...

@buildreactive
Copy link

Footnote: This is made worse since a carthage framework does not provide quick link documentation in Xcode. So it's up to the user to open up a separate SwiftCheck project and read source... I usually start with test cases hoping to find interesting tidbits I can learn from, then fall back to actually reading source.

@CodaFi
Copy link
Member Author

CodaFi commented Sep 4, 2015

There is a tutorial playground, but I have not been maintaining it of late.

@buildreactive
Copy link

Ok – trying to put together a "list of really nice to have's" for the documentation.

Here's one that I'm stuck on: How would I write a generator that takes a random element from an Array? Been reading the code... haven't figured it out yet.

/// the testability of program properties - A statement or invariant that can be proven to hold when
/// fed any number of arguments of a particular kind. It is akin to Fuzz Testing but is made
/// significantly more power by the primitives in this framework.
///
Copy link

Choose a reason for hiding this comment

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

At this point, I'm really interested. You've caught my attention, even if I've never heard of QuickCheck before. You're assuming your reader is familiar with TDD, but that's not a terrible assumption (and a motivated reader will have a good intuition for what you mean at this point anyway).

@rnapier
Copy link

rnapier commented Sep 4, 2015

It's important to know who your audience is. In my comments I've assumed the following profile:

  • No background with functional programming
  • Certainly no background with QuickCheck or Haskell generally
  • Some background with TDD or at least good unit test practices
  • Fairly good grasp of Swift, and enough experience to have had some testing problems
  • Motivated to build good testing solutions, and willing to learn new things to get there, but focused on practical matters of shipping product, not proving correctness.

To teach this kind of reader, you need to focus on problems they encounter in their work, and show how taking some time to learn a new system will help solve those problems. It's possible that you need to first teach them what problems they face. They may not even realize the limitations of their current system. So the first step is "here's the problem."

For an example of what I mean, see Functional Wish Fulfillment and its followups. This may be way too much information for the header (it took me 3 blog posts just to build up to flatMap). That's fine. You may need to point readers to some wiki page with the lessons and say "when you get why you want SwiftCheck, come back and we'll show you how to use it."

If SwiftCheck can really do for Swift what QuickCheck does for Haskell, I think it can be an incredibly useful tool to many developers. It's worth selling it well, and especially to developers who have not embraced lambda-all-the-things. It needs to work for the kinds of software most iOS devs write today. By its nature, I would expect it to nudge devs towards writing better software, but it's crappy software that most needs SwiftCheck....

@rnapier
Copy link

rnapier commented Sep 4, 2015

And total nitpick because of how I read it. Using /** **/ for big block comment would make much easier to read on iPhone :D

@buildreactive
Copy link

It's worth selling it well, and especially to developers who have not embraced lambda-all-the-things.

I think it's important to keep this in mind. I'm a Scala programmer, but with about 25 years of object oriented programming (started with Smalltalk, then Objective C). I'm familiar with the concepts but I still tend to avoid "complex" or obscure syntax, and to me that includes memorizing 20 different math-like operators. I think it makes my code harder to read.

I'd rather write foo.fmap(something) than, say, foo <^> something. Especially since I jump back and forth between languages, and some operators are not the same...

@buildreactive
Copy link

My suggestions for some additions to the README.md that would make SwiftCheck much, much easier to adopt. I think it's important that these appear in the README since code-level documentation is harder to access.

  • More tutorial:
  • Right now the tutorial kind of "jumps into the middle" and assumes some basics about functional syntax and familiarity with QuickCheck. That's not going to be the case – a pure Objective-C / Swift programmer will have no idea about any of this.
  • Using generators for basics, like creating a string, a string of random length, a random series of dates within a range, and a composed operation (like an email address).
  • Using a generator to take a random value from an Array (I'm still trying to figure out how to do this).
  • A few more examples, organized to build from simple to more complex:
  • Explain key functions like generate and others that I haven't discovered yet.
  • Introduce not only forAll but its alternatives.
  • Explain how shrinking can be disabled and why it might be a good idea.
  • Composition:
  • Composition is hard for non-functional types. Seeing things like bind is hard enough, and seeing >>- is a total puzzle. Explain how it works with a couple of examples: 1) A simple generator using the generate method, 2) with forElementsIn(), 3) compose a generator that randomly sets length and binds that to a generator that builds a string. This would be a great tutorial and really take a newbie a long ways.
  • Explain a few key functors (>>-, <^>, <*>) and link to Swiftz for more information.
  • Show alternatives to the functional syntax, so a non-functional programmer understands what it means. There are things like this, which non-functionalists won't be able to figure out:
let emailGen = wrap3 <^> localEmail <*> Gen.pure("@") <*> hostname <*> Gen.pure(".") <*> tld
  • References:
  • A better table of contents with links to the test cases and specifically to key test cases with interesting examples. For example, ComplexSpec is a good example... but the reference to it is buried in the README, so many people won't stumble on it.

/// `SetOf` is called a "Modifier Type". To learn more about them see `Modifiers.swift`---+
/// |
/// v
/// property("Shrunken sets of integers don't always contain [] or [0]") <- forAll { (s : SetOf<Int>) in

Choose a reason for hiding this comment

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

So I read Modifiers.swift but it leaves more questions than I have answers (lack of documentation in that file).

I get the general impression that I can operate on an ArrayOf, SetOf. But it's still kind of vague. Why are we not just passing in an Array or a Set?

I also saw quite a few things that I didn't understand, such as shrinkOne and removes, and a few things that I personally understand from Scala, but a Swift programmer won't (such as take and drop). But I still have no clue how to apply these in SwiftCheck.

ArrowOf perplexes me. Not sure what that might be – I only vaguely understand arrows.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added documentation for that in this pull request. Read that.

Choose a reason for hiding this comment

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

Added a few more comments up there... but it still leaves a lot of questions – such as "what is shrinkOne and removes and take and drop and when would I use these? Keep in mind, if you are dealing with Swift programmers... they've probably never heard of this. They wouldn't know what List[0,1,2].take(1) would mean.

Also I'd really like to see how to use withFail and whenComplete but I can't find any examples. Would be very handy to see them in action. (I wanted to log something to output in whenComplete for instance...)

By the way, showing expectFail was very nice. Recommend you put that into the README.md as well. That's a big one.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a few more comments up there... but it still leaves a lot of questions – such as "what is shrinkOne and removes and take and drop and when would I use these?

You wouldn't. They're all private 😄

Also I'd really like to see how to use withFail and whenComplete but I can't find any examples.

Yes, that is certainly lacking I'll focus there next.

@rnapier
Copy link

rnapier commented Sep 5, 2015

Definitely agree with @zbeckman's suggestions. The emailGen example confuses me, too. I'm particularly confused by wrap3 just looking at it. If anything, I'd think it'd be wrap5. But I'd really expect some function that took a variadic list of component generators and glued them together rather than a bunch of chained operators. (My experience with the Swift compiler is that a bunch of chained operators can seriously blow up compile times, even with built-in ops, so I tend to avoid it anyway.)

Similarly, every time I see pure, I really don't get what's going on. I'd expect just Gen("@") or Gen(literal: "@") or GenOnly("@") or GenOne("@") or something like that. Something closer to the spirit of Swift's GeneratorOfOne. This feels like a factory method, which is not very Swift-like, and "pure" feels like a misnomer here.

@CodaFi
Copy link
Member Author

CodaFi commented Sep 5, 2015

What did RAC do to appease that kind of criticism with +[RACSignal return:]?

@CodaFi
Copy link
Member Author

CodaFi commented Sep 5, 2015

@rnapier The trouble with providing such a method in the variadic case would be statically checking that the length of the given list (n) is always mirrored in the length of the given arrow (n + 1). In the remaining case, to go that route would necessitate either providing a monomorphic list of Generators or would entail a million overloads to support each individual case, something I've been trying to avoid/limit even with stuff like forAll.

Email is not a great example to have in here because of its complexity.

@buildreactive
Copy link

The latest ReactiveCocoa (3.0) has adopted very Swifty syntax, getting rid of "RAC" (so it's just Signal) and eliminating pipe right/left (the |> and <| operators) in favor of dot-notation (like .map, .next, etc).

@CodaFi
Copy link
Member Author

CodaFi commented Sep 5, 2015

I don't mean prefixing so much as RAC's equivalent to .pure was always +[RACSignal return:] rather than +[RACSignal signalWithBlah...:]

In that same spirit, I don't see .pure as a factory method so much as a combinator just like any other in that same file used for creating a Generator.

/// For an example of the latter see the `ArrowOf` modifier. Because Swift's type system treats
/// arrows (`->`) as an opaque entity that you can't interact with or extend, SwiftCheck provides
/// `ArrowOf` to enable the generation of functions between 2 types. That's right, we can generate
/// arbitrary functions!

Choose a reason for hiding this comment

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

I think I need more of an explanation of how I would actually go about using this...!

Copy link
Member Author

Choose a reason for hiding this comment

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

Given!

@buildreactive
Copy link

Oh, @rnapier, we have to use /// with Xcode 7 because of the way they changed the docgen. It kinda sucks. Unless you have fully left-justified your comments. I've really been thrilled about going through all our code and changing /**..*/ to ///...

@buildreactive
Copy link

@CodaFi ... Actually I think working up to, and presenting, a good email generator would be a fantastic case for a couple of reasons:

  • It's something that sooooo many developers struggle with (and do wrong!)
  • It's a common problem and it is "real world"
  • It will show how SwiftCheck delivers on "real world" problems
  • It will demonstrate how to assemble SwiftCheck's components in a complex case... which means yes, have to build up to it, but then it's like "Wow, ok, that's really awesome."

@CodaFi
Copy link
Member Author

CodaFi commented Sep 5, 2015

Then I will save it for the Tutorial playground (which I'm thinking now I'll do in a different pull request).

@CodaFi
Copy link
Member Author

CodaFi commented Sep 5, 2015

OK, I'm going to go ahead and merge this, but this discussion is far from over and will continue in #91.

CodaFi added a commit that referenced this pull request Sep 5, 2015
@CodaFi CodaFi merged commit 20cfac2 into typelift:swift-develop Sep 5, 2015
@CodaFi CodaFi deleted the documentarian branch September 5, 2015 18:51
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