3 ms·
So here's some TypeScript code I just made up, with a lot of lambdas. It's somewhat typical of code I write all the time. books .join(authors, book
by feoren 5y ago
So here's some TypeScript code I just made up, with a lot of lambdas. It's somewhat typical of code I write all the time.
books
.join(authors, book => book.author, author => author.id)
.filter(([book, author]) => author.lastName === searchText)
.map((book, author) => `The Book ${book.title}, by ${author.fullName()}, has ${book.chapters.count()} chapters, totaling ${book.chapters.sum(chapter => chapter.pages.count())} pages.`);
There are 5 lambda functions in there. Can you tell what the code is doing? Is it correct? Yes, and yes. This is the kind of code that, in my opinion, doesn't need tests at all, nor comments. You can look at it and understand what it's doing and know that it is doing it correctly, as long as it compiles. If it's mission critical, you should test that the join really should be on book.author and author.id, but you need to know the correct answer to write the test, so why not just look at the code and verify it's correct? If your answer is "because another change might break it": no, it won't! Given the preconditions, that code is correct, and no other code can effectively break (and still have your code compile) it without lying to the Type-checker. If someone breaks that code, it's because they're intentionally changing it to do something else, so they'd redline any tests you wrote for it anyway.
It sounds like you're suggesting this code should be more like:
/// <verboseDocs purpose="Verbosity">
/// <purpose> Gets the author of a book
/// <returns> The author of the given book
/// <inputs>
/// <input param="book">The book to get the author of
/// </inputs>
/// </verboseDocs>
function getAuthorOfBook(book: Book) {
return book.author;
}
@testMethod()
function canGetAuthorOfBook() {
const mockBook: Book = {
author: 'Test Author',
title: 'Verbosity',
...
};
Assert.areEqual('Test Author', getAuthorOfBook(mockBook);
}
...
/// <verboseDocs>
/// <purpose> Takes a last name and returns a function that returns true if the given (Book, Author) tuple contains an author whose last name matches the given string of the outer function.
/// ...
function getBookAuthorPairPredicateFromAuthorLastName(lastName: string) {
return function(pair: [Book, Author])
return pair[1].lastName === lastName;
}
}
And on and on and on, still needing the original code, but just a lot more obfuscated:
books
.join(authors, getAuthorOfBook, author => getIdOfAuthor)
.filter(getBookAuthorPairPredicateFromAuthorLastName (searchText))
.map(describeBookAuthorPair)
Now I have no idea what the hell this code is doing and whether it's correct or not. Note that to avoid lambdas in the filter (filtering by a value in the closure) we need to write a function that returns the predicate we want to test on, instead of just sticking the right thing in the right place to start with. All to avoid a single "=>".
What circle of hell are we in!?
- christophilus 5y agoThis is the truth. Peace be with you.
- robertlagrant 5y agoI appreciate the large amount of effort you went to to get your point across; that helps with actual discussion, which is awesome. The context I was talking in mentioned Python not having lambdas. It does have lamdbas (there's even a keyword) so I assumed it meant larger, multi-line lambdas. The code you wrote (glueing together a few mini-lambdas) I would not expect to test independently. I'd feel bad that you can't use SQLAlchemy's even nicer syntax, but I certainly wouldn't expect a test for each of those little things.