To recap, there was a function, withResource, that handled creation/acquisition and disposal of resources, but if you needed to access the resource directly in the tests, you had to store the resource in an IORef (or similar) as part of the initialization routine.
At the time it seemed acceptable, but later I discovered that when the number of resources was bigger than one or two, or even not known in advance (when tests are generated rather than just written down), this was inconvenient enough to start looking for a different solution.
One of the major problems with tests receiving the resource value directly, as inwithResource :: IO a -> (a -> IO ()) -> (a -> TestTree) -> TestTree
… was that the resource could be used not only in the tests themselves, but to construct the tests, which is bad/wrong for a number of reasons. For instance, we don’t want to create the resources when we’re not running tests, but we still want to know which tests we have.
The solution I found is to pass not the value of the resource, but an IO action yielding the resource.withResource :: IO a -> (a -> IO ()) -> (IO a -> TestTree) -> TestTree
Even though it’s an IO action, it doesn’t acquire the resource, because such a resource wouldn’t be shared across multiple tests, which is the semantics we’re after. Instead, it returns the resource which has been acquired (think: reads from an IORef or MVar). But thanks to it being an IO action, it can only be used inside a test, and not to construct or alter tests based on the resource value.
Here’s a modified example from the last article which works with this new API:import Test.Tasty import Test.Tasty.HUnit -- assumed defintions data Foo acquire :: IO Foo release :: Foo -> IO () testWithFoo :: Foo -> Assertion (acquire, release, testWithFoo) = undefined main = do defaultMain $ withResource acquire release tests tests :: IO Foo -> TestTree tests getResource = testGroup "Tests" [ testCase "x" $ getResource >>= testWithFoo ]
(這是 Day 24 – Advent Ventures 的中譯，作者是 Larry Wall。)
我們到了嗎？</article> <article lang="zh-Hant">
這個月是 Perl 降臨以來的第 26 年，
（包括 13 歲的天才小妹）
我們的小家庭一次用 24 個故事，
埋葬<hruby><rb annotation="naysayers">酸民</rb>和<rb annotation="yaysayers">鹼民</hruby>的屍骨，
歡迎來到這個家族，<hruby><rb annotation="quantum superposition">量子疊加</rb></hruby>出無數歡樂、悲傷和憧憬。
嗯……最好<hruby><rb annotation="do some fact checking">看看新聞小幫手</rb></hruby>……稍等一下……<hruby><rb annotation="tum tiddly tum">等等等等</rb></hruby>……
嘿，我知道，我只要用 Perl 6 測試套件就行了。
（大聲）<hruby><rb annotation="sanity test">健全測試</rb></hruby> #1 合格了嗎？結果如何？
【TimToady 得到賜福，開始指揮 Perl 朝聖者的合唱。】
Microsoft's Joe Duffy and team have been (quietly) working on a new programming language, based on C# (for productivity, safety), but leveraging C++ features (for performance). I think it's fair to say - and agree with Joe - that a nirvana for a modern general purpose language would be one that satisfies high productivity (ease of use, intuitive, high level) AND guaranteed (type)safety AND high execution performance. As Joe outlines in his blog post (not video!):
At a high level, I classify the language features into six primary categories:
1) Lifetime understanding. C++ has RAII, deterministic destruction, and efficient allocation of objects. C# and Java both coax developers into relying too heavily on the GC heap, and offers only “loose” support for deterministic destruction via IDisposable. Part of what my team does is regularly convert C# programs to this new language, and it’s not uncommon for us to encounter 30-50% time spent in GC. For servers, this kills throughput; for clients, it degrades the experience, by injecting latency into the interaction. We’ve stolen a page from C++ — in areas like rvalue references, move semantics, destruction, references / borrowing — and yet retained the necessary elements of safety, and merged them with ideas from functional languages. This allows us to aggressively stack allocate objects, deterministically destruct, and more.
2) Side-effects understanding. This is the evolution of what we published in OOPSLA 2012, giving you elements of C++ const (but again with safety), along with first class immutability and isolation.
3) Async programming at scale. The community has been ’round and ’round on this one, namely whether to use continuation-passing or lightweight blocking coroutines. This includes C# but also pretty much every other language on the planet. The key innovation here is a composable type-system that is agnostic to the execution model, and can map efficiently to either one. It would be arrogant to claim we’ve got the one right way to expose this stuff, but having experience with many other approaches, I love where we landed.
4) Type-safe systems programming. It’s commonly claimed that with type-safety comes an inherent loss of performance. It is true that bounds checking is non-negotiable, and that we prefer overflow checking by default. It’s surprising what a good optimizing compiler can do here, versus JIT compiling. (And one only needs to casually audit some recent security bulletins to see why these features have merit.) Other areas include allowing you to do more without allocating. Like having lambda-based APIs that can be called with zero allocations (rather than the usual two: one for the delegate, one for the display). And being able to easily carve out sub-arrays and sub-strings without allocating.
5) Modern error model. This is another one that the community disagrees about. We have picked what I believe to be the sweet spot: contracts everywhere (preconditions, postconditions, invariants, assertions, etc), fail-fast as the default policy, exceptions for the rare dynamic failure (parsing, I/O, etc), and typed exceptions only when you absolutely need rich exceptions. All integrated into the type system in a 1st class way, so that you get all the proper subtyping behavior necessary to make it safe and sound.
6) Modern frameworks. This is a catch-all bucket that covers things like async LINQ, improved enumerator support that competes with C++ iterators in performance and doesn’t demand double-interface dispatch to extract elements, etc. To be entirely honest, this is the area we have the biggest list of “designed but not yet implemented features”, spanning things like void-as-a-1st-class-type, non-null types, traits, 1st class effect typing, and more. I expect us to have a few of these in our mid-2014 checkpoint, but not all of them.
What do you think?