macabeus/js-proposal-algebraic-effects

Testing

brucou opened this issue · 1 comments

Hi there,

this is very nice work. The try/catch syntax seems elegant and not too counter-intuitive (hopefully it does not interfere with regular exception handling?)

There is one important thing though that I came to think while reviewing the proposal. What is great about algebraic effects (as understood from the freer monad) is that you can interpret those effects in any which way you want. That means in particular swapping the interpreter as you need. This in turn means the possibility of having one interpreter to run the program, and one to test the program. So say in the catch instead of only running the effects, you also log them and their results, After an effectful functions is ran, you can get back the logs and check it against expectations. This gives data structures which can be tested against with property-based testing.

The issue here is that you cannot swap a catch block (running effects) for another one (running + logging effects). Is there a syntax that would facilitate that? I can think of decoupling the catch block from the try block:

const fn = params => ${
  // effectful code
  ...
  perform this
  ...
  perform that
  ...
}

export {fn}

So you can have an exect script and a test script by using different interpreters:

// exec 
// someDataStructure  encodes the effects
const someDataStructure = fnUnderTest(some params);
const value = run (someDataStructure, execInterpreter);
... continue program
// test
const someDataStructure = fnUnderTest(some params);
const actual = run (someDataStructure, testInterpreter);
assert.deepEqual(actual, expected, `ok`);
// or with PBT
// assert.ok(property(actual, expected), `ok`);
// or with metamorphic testing
// const someDataStructure = fnUnderTest(some params);
// const anotherDataStructure = fnUnderTest(another params);
// assert.ok(property(some params, someDataStructure, another params, anotherDataStructure), `ok`);

The try/catch syntax seems elegant and not too counter-intuitive (hopefully it does not interfere with regular exception handling?)

Yeah. It's true. Because it's just a new feature with a new syntax currently not supported, so it don't break anything.
There is an open issue about the syntax (#14) but IMO the current syntax works fine.

The issue here is that you cannot swap a catch block (running effects) for another one (running + logging effects). Is there a syntax that would facilitate that?

At this moment, there is no idea in mind about that. On a past and long issue, we understand that is better to write a simpler proposal, with less new features, instead of a more complex.

Despite there is no current ideal to facilitate to swap the handle block, you could swap the try block:

const tryBlock = () => {
  // ...
}

// when running the real application
try {
  tryBlock()
} handle (effects) {
  // effects handle to real application
}

// when running the tests
try {
  tryBlock()
} handle (effects) {
  // effects handle to tests
}

It's desired, because is good to have a symmetry with exceptions.