busterjs / referee-combinators

Combinators goes assertion
0 stars 1 forks source link

Terminology (RFC) #11

Open meisl opened 11 years ago

meisl commented 11 years ago

I'd like to propose some definitions so we can discuss things in a more concise way (and hopefully get things clear faster). I've felt a need for this quite for a time and now that I started to tackle #5 finally found myself unable to go without.

Plz note that this is a proposal, a request for comments. We can change anything anytime (but not too often, plz).

_meta_ assertion

That's what I'm aiming to make in #5; most and before all others: fails. I'm calling them "meta" because they are assertions about assertions.

Ex: consider the "⇔" vs the "↔" in

x | 2 ↔ y | 2 ⇔ either both or none of x and y are even

"⇔" is a meta symbol while "↔" is not. Yet both read - and mean - "if and only if". The difference is that ↔ is about predicates on numbers while ⇔ is about propositions on predicates on numbers. Other meta assertions might be passes, throws/yieldsError, failsWithMessage, whatever. I'm going for fails first.

_higher-order_ assertion

Most general: any assertion that takes other assertions as argument (and possibly other things) and/or produces an assertion, very much like "higher-order functions".

So any meta assertion is also higher-order but not necessarily vice versa. Ex.: or, and, some, all are higher-order BUT NOT meta whereas fails is higher-order AND meta.

_object-level_ assertion (OLA/ola)

The assertion that a meta assertion is about; standard variable name for such is ola.

Ex: assert.fails(assert.equals, [2, 3]) would read "assert that assert.equals(2, 3) fails" and have object-level assertion equals & meta assertion fails. An OLA can be higher-order, as in assert.fails(assert.all(numbers, assert.isEven)), where assert.all, assert.isEven and the result of assert.all(numbers, assert.isEven) are OLAs but not assert.fails. assert.isEven is the only one that's strictly non-higher-order and assert.fails is the only meta assertion.

assertion _definition_ (isXyzDef, ie suffix Def)

The thing in referee.add(name, thing).

Ex:

referee.add('isEven',
    {  // *assertion definition*
        assert: function() {...},
        assertMessage: "Expected ${0} to be even"
        //, ...
    }
);

assertion _implementation_

The assert function of an assertion definition.

Ex: the fn in referee.add(name, { assert: fn, ... ), or:

referee.add('isEven',
    {
        assert: function() {...},  // *assertion implementation*
        assertMessage: "Expected ${0} to be even"
        //, ...
    }
);

function under test (FUT, fut)

Should be clear; fut as variable name.

You might have wondered why there is no ma convention for "meta assertion" variables. Well, I just haven't come across a need for it yet. Consider: the meta assertions themselves need to be tested, of course. But how would that look like? Maybe this (which should pass) refute.fails(assert.fails(assert.equals, [2,3]))? Well, that's a bit circular. We cannot (reasonably) use fails to test itself. So more reasonable is assert.isTrue(failsDef.assert(assert.equals, [2,3])), where failsDef.assert is NOT an assertion, just a function. Hence...

meisl commented 11 years ago

There are some more that need to be defined but which I don't need for #5. Also, I can't give substantial suggestions for them yet. I'll name them later, with comments about what they ought to refer to and partly some first rationale.

meisl commented 11 years ago

Hmm, maybe put each definition in a separate comment? This way they could be referred to from elsewhere and a link be provided, s.t. you could click on eg " _OLA_ " and be taken to " _object-level assertion_ ". ?

johlrogge commented 11 years ago

Good initiative. I haven't had time to give your suggestions the thought they deserve so I refrain from commenting on them now. I did set up an wiki page here where we can collect the definitions. For now I think definitions that are not disputed are definitions until they are disputed. Basically, we can consider your suggestion a good starting state.

I have been using combinators quite sloppily. I think that there are both combinators and combineable asserts. ( higher level asserts that can combine other combineable asserts), all combinators are combineable but all combineables are not combinators.

In grammars we talk about terminals and non terminals which in are equivalent to what I mean with combineable and combinator.

I will have to read up on parser combinator terminology. I'm thinking that for combinable asserts it should be a good starting point.

About meta asserts - with the changes I'm working on they would pretty much be assertions about promises. What I think is the interesting part is test case generation for asserts in the test-helper ways. I have always seen them as utilities for expressing facts about asserts, not as asserts (each fact will be represented as a test and your work takes away a lot of grunt work as well as gives a reasonable template for describing the asserts.

That's it for now. I hope to be able to get my branch a bit further towards integrating the promise based raw asserts into referee.

meisl commented 11 years ago

Thanks, the wiki is a good idea, just the right place! (btw, the link you gave has a surplus "issues/" in it, hence not working)

I have been using combinators quite sloppily. I think that there are both 'combinators' and 'combineable' asserts. ( higher level asserts that can combine other combineable asserts), all combinators are combineable but all combineables are not combinators.

Yes, that should be roughly the notion of my equally sloppily using "'real' combinators" vs "not real/not-necessarily 'real' combinators". However, for a workable definition we still have to tweak it a bit. For example, "all x are y but all y are not x" isn't really fortunate, IMHO. As a matter of fact, it leaves me in doubt whether you meant "...but not every y need be an x" or rather "...but no y is an x (ever!)" ;) But anyways, that's not urgent. Btw: we haven't even defined "assertion" in the first place...(!) Yet again: just to say. No problem for me so far :)

In grammars we talk about terminals and non terminals which in are equivalent to what I mean with combineable and combinator.

I like this analogy! Namely for it promotes the basic idea: atoms (~> terminals), ie something you cannot view as made up from (smaller) parts vs compound things (~> non-terminals). "...you cannot view as..." is of course a matter of perspective that we can (and must, and will) decide on. Then, what is/are the operation/s to build up compound things? Answering these questions for our domain will lead to a thorough definition (and most probably more). So that's why it's a good analogy, it triggers the right questions.

I will have to read up on parser combinator terminology. I'm thinking that for combinable asserts it should be a good starting point.

Definitely, it's been a source of lots of inspiration already. As long as we don't identify parsers with assertions (of whatever kind) it's all fine. The relation is again analogical. That is, on the surface - I'm almost sure that there is a more profound concept (ie one concept) underlying both. That's why I'm always bringing up monads ...without understanding enough :( Nevertheless, analogies are a Good ThingTM! For me, personally, "analogical thinking" is actually where the cool ideas are coming from. Must be balanced / interleaved with "analytical thinking" of course. So getting this balance right is what makes the art, IMHO.

About meta asserts - with the changes I'm working on they would pretty much be assertions about promises. [...] I have always seen them as utilities for expressing facts about asserts, not as asserts [...] your work takes away a lot of grunt work [...]

Well, I'm basically trying to raise the level of abstraction (and see how far I can push it). So particularly this:

...as utilities for expressing facts about asserts, not as asserts

...I am taking as a challenge, saying "why not express facts about asserts - by asserts!?" After all, asserts are there for expressing facts about something... However, as for any abstract construct - and if possible at all - it must be built from lower-level building blocks[1]. Raw asserts is one. My hack re intercepting buster's fail is another one, albeit ugly, hacky, and "not bearing much weight". But note: not every thinkable building block necessarily needs to be used!

_Promise based_ raw asserts, in comparison, almost seem like a _silver-bullet building block_ to me, so :D ...

-- [1] In the end it's all nothing but a dumb (finite! deterministic!) Turing machine. Or λ. Or CL ;)

johlrogge commented 11 years ago

How about primitive asserts for terminals and assert combinator for nonterminals. It is in line with how they describe parsers here

meisl commented 11 years ago

+1 for primitive assert vs combinator!

+2 for the paper by Hutton + Meijer :) Haven't read through it yet but seems to include - and be way more comprehensive than - the 1992 one (4 yrs earlier) by Hutton alone, which I had mentioned elsewhere. Right?

meisl commented 11 years ago

A condensed version of 8 pages from 1998, by the same two authors: Monadic Parsing in Haskell

This paper is a tutorial on defi ning recursive descent parsers in Haskell. In the spirit of one-stop shopping, the paper combines material from three areas into a single source. [...] The paper is targeted at the level of a good undergraduate student who is familiar with Haskell, and has completed a grammars and parsing course. Some knowledge of functional parsers would be useful, but no experience with monads is assumed.

johlrogge commented 11 years ago

looking good