Following Tab's comments on the a Promise monad, I prototyped a Future library 
based on DOM Future using TypeScript. Since TS has no concept of a union type, 
I'm using TS overloads to approximate the Ref<T> union type example. It has 
roughly the following API:

```ts
class FutureResolver<T> {
  accept(value: T): void;
  resolve(value: Future<T>): void;
  resolve(value: T): void;
  reject(value: any): void;
}

class Future<T> {
  constructor(init: (resolver: FutureResolver<T>) => void);

  // unconditional lift
  static accept<TResult>(value: TResult): Future<TResult>;

  // autolift
  static resolve<TResult>(value: TResult): Future<TResult>;
  static resolve<TResult>(value: Future<TResult>): Future<TResult>;

  static reject<TResult>(value: any): Future<TResult>;

  // assimilation of thenables, similar to `Q()`. Assimilation stops at the 
first `Future`
  static from<TResult>(value: any): Future<TResult>;

  // autolifting then.
  // for `p.map` like operation, `resolve` should return `Future.accept(u)`
  // for `p.flatMap` like operation, `resolve` can return u or 
`Future.resolve(u)`, but no error is thrown
  then<TResult>(resolve: (value: T) => Future<TResult>, reject: (value: any) => 
Future<TResult>): Future<TResult>;
  then<TResult>(resolve: (value: T) => TResult, reject: (value: any) => 
Future<TResult>): Future<TResult>;
  then<TResult>(resolve: (value: T) => Future<TResult>, reject: (value: any) => 
TResult): Future<TResult>;
  then<TResult>(resolve: (value: T) => TResult, reject: (value: any) => 
TResult): Future<TResult>;

  catch<TResult>(reject: (value: any) => Future<TResult>): Future<TResult>;
  catch<TResult>(reject: (value: any) => TResult): Future<TResult>;

  done(resolve: (value: T) => void, reject: (value: any) => void);
}
```

In the AsyncTable example you would write:

```ts
class AsyncTable<T, U> {
  private m = new Map<T, U>();

  set(keyP: Future<T>, val: U): void {
    keyP.done(key => { this.m.set(key, val); });
  }

  get(keyP: Future<T>): Future<U> {
    return keyP.then(key => Future.accept(this.m.get(key))); // accept causes 
an unconditional lift.
  }
}
```

In Mark's second example, using the union type, you might instead have:

```ts
class AsyncTable<T, U> {
  private m = new Map<T, Future<U>>();

  set(keyP: Future<T>, val: Future<U>): void;
  set(keyP: Future<T>, val: U): void;
  set(keyP: Future<T>, val: any): void {
    keyP.done(key => { this.m.set(key, Future.resolve(val)); }); // autolift 
`Ref<U>`-like union to `Future<U>`
  }

  get(keyP: Future<T>): Future<U> {
    return keyP.then(key => this.m.get(key)); // no need for unconditional 
lift, `then` will merge the already auto-lifted `Future<U>`
 }
}
```

And Mark's third example might be:

```ts
class AsyncTable<T, U> {
  private m = new Map<T, Future<U>>();

  // gah! TS needs union types...
  set(keyP: Future<T>, val: Future<U>): void;
  set(keyP: T, val: Future<U>): void;
  set(keyP: Future<T>, val: U): void;
  set(keyP: T, val: U): void;
  set(keyP: any, val: any): void {
    Future.resolve(keyP).done(key => { this.m.set(key, Future.resolve(val)); 
});  // autolift key and val
  }

  get(keyP: Future<T>): Future<U>;
  get(keyP: T): Future<U>;
  get(keyP: any): Future<U> {
    return Future.resolve(keyP).then(key => this.m.get(key)); // autolift key, 
val is already a `Future<U>`
  }
}
```

Ron

From: [email protected] [mailto:[email protected]] On 
Behalf Of Claude Pache
Sent: Friday, May 10, 2013 11:25 AM
To: Mark S.Miller
Cc: [email protected]; es-discuss
Subject: Re: The Paradox of Partial Parametricity


Le 10 mai 2013 à 14:55, Mark S. Miller 
<[email protected]<mailto:[email protected]>> a écrit :


[+es-discuss]

I didn't realize that I composed this in reply to a message only on 
public-script-coord. Further discussion should occur only on es-discuss. Sorry 
for the confusion.

On Fri, May 10, 2013 at 5:52 AM, Mark S. Miller 
<[email protected]<mailto:[email protected]>> wrote:

I think the key exchange in these threads was

On Fri, May 3, 2013 at 4:17 PM, Jonas Sicking 
<[email protected]<mailto:[email protected]>> wrote:
[...]
I.e. permitting nested promises creates a more complex model and with
that you always get more confusion and more questions.

 On Sat, May 4, 2013 at 1:48 AM, Claus Reinke 
<[email protected]<mailto:[email protected]>> wrote:
[...]
>From the perspective of a-promise-is-just-like-other-wrapper-classes,
auto-flattening promises creates a more complex model


Together with each of their explanations about what they meant. They are both 
right. Lifting and auto-lifting do not mix. Q Promises give us autolifting with 
no lifting. Monadic promises would give us lifting but no autolifting. Having 
both in one system creates a mess which will lead programmers into the 
particular pattern of bugs Jonas warns about in his message.

For clarity I define the following APIs so I can define the three architectural 
choices in terms of the subset of these APIs they contain. Obviously, I do not 
intend to start a bikeshed yet on the particular names chosen for these 
operations. Let's stay focused on semantics, not terminology.

An upper case type variable, e.g. T, is fully parametric. It may be a promise 
or non-promise.
A lower case type variable, e.g. t, is constrained to be a non-promise. If you 
wish to think in conventional type terms, consider Any the top type immediately 
split into Promise and non-promise. Thus type parameter t is implicitly 
constrained to be a subtype of non-promise.




Ref<T> is the union type of T and Promise<T>.

Q.fulfill(T) -> Promise<T> // the unconditional lift operator

Q(Ref<t>) -> Promise<t> // the autolift operator

p.map: Promise<T> -> (T -> U) -> Promise<U>

p.flatMap: Promise<T> -> (T -> Promise<U>) -> Promise<U>
// If the onSuccess function returns a non-promise, this would throw an Error,
// so this type description remains accurate for the cases which succeed.

p.then: Promise<T> -> (T -> Ref<u>) -> Promise<u>
// Ignoring the onFailure callback



* A Monadic promise system would have Q.fulfill, p.map, and p.flatMap.

* A Q-like promise system would have Q, and p.then

* The dominant non-Q-like proposal being debated in these threads has 
Q.fulfill, Q, and p.then.



Hello,

With this explanation, from my perspective (as someone who has never used 
promises, but rejoices in advance to use them), the Q-like model of promise 
seems far superior to me:

* lifting (for non-promises) + no-op (for promises) is advantageously replaced 
by one method, autolifting (working with both);
* `flatMap` (for promises) + `map` (for non-promises) is advantageously 
replaced by one method, `then` (working with both);

It follows that:
* it is impossible to have a promise for a promise, reducing the probability of 
bugs;
* it makes it easier to write generic algorithms that work for both 
non-promises and promises;
* in many situations, there is no need to ask oneself if one should provide a 
promise or a non-promise, reducing the burden of thinking to programmers and 
therefore the probability of bugs.

Not wanting to ask oneself «Should I provide a promise or a value?» is not 
sloppiness, but it is because promises are just uninteresting wrappers. I never 
ask myself: «Should I provide a string or a String object to the `substring` 
method?», because it doesn't matter, and the String object is just an 
uninteresting wrapper. And to push the comparison further: it is dubious, and 
hopefully impossible, to wrap a String object in another String object, just 
like it is dubious to have a promise for a promise.





<snip>

The main failure mode of standards bodies is to resolve a conflict by adding 
the union of the advocated features. Here, this works even worse than it 
usually does. The coherence of lifting depends on the absence of autolifting, 
and vice versa. We need to make a choice.

Indeed, the advantages I have mentioned vanished if we add unconditional 
lifting to autolifting + `then`, because its bare use forces the programmer to 
think what type of object he should provide (a promise or a non-promise) in 
some situations. If you allow me to do a somewhat shaky comparison, the 
usefulness of ASI is greatly reduced by the few situations where ASI does not 
work; therefore most style guides recommend to not use ASI at all.


-Claude




--
    Cheers,
    --MarkM



_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to