proposal-first-class-protocols icon indicating copy to clipboard operation
proposal-first-class-protocols copied to clipboard

Why not use object

Open 2A5F opened this issue 4 years ago • 0 comments

protocol ToString {
  tag;

  toString() {
    return `[object ${this[ToString].tag}]`;
  }
}
Protocol.implement(Object, ToString);
Object.prototype[ToString] = { tag: 'Object' };
// or
Object.prototype[ToString].tag = 'Object';

in #34 syntax

implement ToString for Object {
  tag = 'Object'
}

protocol Functor {
    map;
}
implement Functor for Promise {
    map(f) {
        return m => m.then(f);
    }
}
function map(m, f) {
    return m[Functor].map(f)(m);
}

in ts

protocol Functor<F extends for<_>> {
    map<T, R>(f: (v: T) => R): F<T> => F<R>;
}
implement Functor<Promise> for Promise {
    map<T, R>(f: (v: T) => R): Promise<T> => Promise<R> {
        return m => m.then(f);
    }
}
function map<M extends for<_> { [Functor]: Functor<M> }, T, R>(m: M<T>, f: (v: T) => R): M<R> {
    return m[Functor].map(f)(m);
}

Another proposal

protocol <name> [extends <protocol_extends>] {
  <slots>;
  <fields> = <expr>;
  <methods>() { };
}

implement <protocol> for <constructor> [as <impl_alias>] [extends <impl_extends>] { <object body> }
implement <protocol> of <object> [as <impl_alias>]  [extends <impl_extends>] { <object body> }
implement <protocol> [as <impl_alias>]  [extends <impl_extends>] { <object body> }

<value> instanceof <protocol>

<name> is symbol

1. SomeObject = { 
        prototype: { },
    }
2. call Protocol.implement(SomeProtocol, [SomeObject.prototype]);
3. SomeObject = { 
        prototype: { 
            [SomeProtocol]: {
                <slots>: undefined, // has key but value is undefined
                <fields> = 1,
                __proto__: {
                    <methods>() { },
                }
            }
        },
    }
4. SomeObject[SomeProtocol].<slots> = 1

2.2 call Protocol.implement(SomeProtocol, [SomeObject.prototype], { 
        <slots>: 1,
        <fields> = 2
    });

2.3 call Protocol.implement(SomeProtocol, [Foo, Bar]); // impl for multi
2.4 call Protocol.implement(SomeProtocol, [Foo, Bar], { }); // impl for multi with object

polyfill

const ProtocolMap = new Map

Protocol.define = (name, def, opt) => {
    const symbol = Symbol(name)
    ProtocolMap.set(symbol, {  def, opt  })
    return symbol
}

Protocol.implement = (protocol, objs, impl) => {
    const { def, opt } = ProtocolMap.get(protocol)
    let base = null;
    if (opt != null && typeof opt === 'object' && 'extends' in opt && ProtocolMap.has(opt.extends)) {
        const needsuper = objs.filter(o => !(protocol in o))
        if (needsuper.length != 0) {
            base = Protocol.implement(opt.extends, needsuper, impl)
        } 
    }
    const proto = Object.fromEntries(Object.entries(def).filter(([k, v]) => typeof v === 'function'))
    const insta = Object.fromEntries(Object.entries(def).filter(([k, v]) => typeof v !== 'function'))
    const obj = Object.assign(Object.assign(Object.create(Object.assign(Object.create(base), proto)), insta), impl)
    for (o of objs) {
       o[protocol] = obj
    }
    return obj
}

Protocol.instanceof = (obj, protocol) => protocol in o

// define
protocol Functor {
    map;
}
var Functor = Protocol.define('Functor ', {
    map: void 0;
})

// impl
implement Functor for Promise {
    map(f) {
        return m => m.then(f);
    }
}
Protocol.implement(Functor, [Promise.prototype], {
    map(f) {
        return m => m.then(f);
    }
})

// impl for object
implement Functor of Promise.prototype {
    map(f) {
        return m => m.then(f);
    }
}

// impl for nothing
implement Functor as NullableFunctor {
    map(f) {
        return m => m == null ? null : f(m);
    }
}
var NullableFunctor = Protocol.implement(Functor, [], {
    map(f) {
        return m => m == null ? null : f(m);
    }
})

// protocol extends
protocol Monad extends Functor {
    bind;
    return;
}
implement Monad for Promise {
    bind(f) {
        return m => m.then(f);
    },
    return(v) {
        return Promise.resolve(v);
    }
}
var Monad = Protocol.define('Monad ', {
    bind: void 0;
    return: void0;
}, { extends: Functor  })
Protocol.implement(Monad, [Promise.prototype], {
    bind(f) {
        return m => m.then(f);
    },
    return(v) {
        return Promise.resolve(v);
    }
})

// impl extends
implement Monad as NullableMonad extends NullableFunctor {
    bind(f) {
        return m => m == null ? null : f(m);
    },
    return(v) {
        return v;
    }
}
var NullableMonad = Protocol.implement(Monad, [], Object.assign(Object.create(NullableFunctor), {
    bind(f) {
        return m => m == null ? null : f(m);
    },
    return(v) {
        return v;
    }
}))

2A5F avatar Apr 29 '21 13:04 2A5F