Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

protoduck

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

protoduck - npm Package Compare versions

Comparing version 3.0.0 to 3.0.1

14

index.js

@@ -56,8 +56,10 @@ 'use strict'

'No ' + (proto.name || 'protocol') + ' impl for `' +
(target ? typeName(thisArg) + '#' : '') +
name +
'` found for arguments of types: (' +
[].map.call(args, typeName).join(', ') + ')'
if (target) {
msg += ' and `this` type ' + typeName(thisArg)
}
'(' +
[].map.call(args, typeName).join(', ') + ')`'
msg += '\n\n'
msg += 'You must implement '
msg += (proto.name || 'the protocol `' + name + '` belongs to')
msg += ' in order to call `' + name + '` with these arguments.\n'
var err = new Error(msg)

@@ -116,3 +118,3 @@ err.protocol = proto

var methodTypes = calculateMethodTypes(name, proto, types)
if (target && !target[name]) {
if (target != null && !{}.hasOwnProperty.call(target, name)) {
target[name] = proto._metaobject

@@ -119,0 +121,0 @@ ? Protocol.meta.createGenfun(proto._metaobject, proto, target, name)

{
"name": "protoduck",
"version": "3.0.0",
"version": "3.0.1",
"description": "Fancy duck typing for the most serious of ducks.",

@@ -11,3 +11,5 @@ "main": "index.js",

"preversion": "npm t",
"test": "standard && nyc -- mocha --reporter spec"
"postversion": "npm publish && git push --follow-tags",
"pretest": "standard",
"test": "nyc -- mocha --reporter spec"
},

@@ -43,3 +45,3 @@ "repository": {

"dependencies": {
"genfun": "^3.0.0"
"genfun": "^3.0.1"
},

@@ -46,0 +48,0 @@ "devDependencies": {

@@ -1,2 +0,2 @@

# Protoduck [![Travis](https://img.shields.io/travis/zkat/protoduck.svg)](https://travis-ci.org/zkat/protoduck) [![npm version](https://img.shields.io/npm/v/@zkat/protoduck.svg)](https://npm.im/@zkat/protoduck) [![license](https://img.shields.io/npm/l/@zkat/protoduck.svg)](https://npm.im/@zkat/protoduck)
# Protoduck [![Travis](https://img.shields.io/travis/zkat/protoduck.svg)](https://travis-ci.org/zkat/protoduck) [![npm version](https://img.shields.io/npm/v/protoduck.svg)](https://npm.im/protoduck) [![license](https://img.shields.io/npm/l/protoduck.svg)](https://npm.im/protoduck)

@@ -29,5 +29,12 @@ [`protoduck`](https://github.com/zkat/protoduck) is a JavaScript library is a

* [Example](#example)
* [Features](#features)
* [Guide](#guide)
* [Introduction](#introduction)
* [Defining protocols](#defining-protocols)
* [Simple impls](#simple-impls)
* [Multiple dispatch](#multiple-dispatch)
* [Static impls](#static-impls)
* [API](#api)
* [`protocol()`](#protocol)
* [`implementation`](#impl)
* [`impls`](#impl)

@@ -43,3 +50,3 @@ ### Example

talk: [],
isADuck: []
isADuck: [() => true] // default implementation -- it's optional!
})

@@ -58,26 +65,204 @@

// elsewhere in the project...
class Person () {}
Quackable(Person, {
walk() { return "my knees go the wrong way but I'm doing my best" }
talk() { return "uhhh... do I have to? oh... 'Quack' 😒"}
isADuck() { return true /* lol I'm totally lying */ }
})
// and another place...
class Duck () {}
Quackable(Duck, {
// Implement the protocol on the Duck class.
Quackable(Duck, [], {
walk() { return "*hobble hobble*" }
talk() { return "QUACK QUACK" }
isADuck() { return true }
})
// main.js
doStuffToDucks(new Person()) // works
doStuffToDucks(new Duck()) // works
doStuffToDucks({ walk() { return 'meh' } }) // => error
doStuffToDucks(new Duck()) // works!
```
### Features
* Clear, concise protocol definitions and implementations
* Verifies implementations in case methods are missing
* "Static" implementations ("class methods")
* Optional default method implementations
* Fresh JavaScript Feel™ -- methods work just like native methods when called
* Methods can dispatch on arguments, not just `this` ([multimethods](npm.im/genfun))
* Fast, cached multiple dispatch
### Guide
#### Introduction
JavaScript comes with its own method definition mechanism: You simply add
regular `function`s as properties to regular objects, and when you do
`obj.method()`, it calls the right code! ES6/ES2015 further extended this by
adding a `class` syntax that allowed this same system to work with more familiar
syntax sugar: `class Foo { method() { ... } }`.
`protoduck` is a similar *language extension*: it adds something called
"protocols" to JavaScript.
The purpose of protocols is to have a more explicit definitions of what methods
"go together". That is, if you have a type of task, you can group every method
that things definitely need to have under a protocol, and then write your code
using the methods defined there. The assumption is that anything that defines
that group of methods will work with the rest of your code.
And then you can export the protocol itself, and tell your users "if you
implement this protocol for your own objects, they'll work with my code."
Duck typing is a common term for this: If it walks like a duck, and it talks
like a duck, then it may as well be a duck, as far as any of our code is
concerned.
#### Defining Protocols
The first step to using `protoduck` is to define a protocol. Protocol
definitions look like this:
```javascript
// import the library first!
import protocol from "protoduck"
// `Ducklike` is the name of our protocol. It defines what it means for
// something to be "like a duck", as far as our code is concerned.
const Ducklike = protocol([], {
walk: [], // This says that the protocol requires a "walk" method.
talk: [] // and ducks also need to talk
peck: [] // and they can even be pretty scary
})
```
Protocols by themselves don't really *do* anything, they simply define what
methods are included in the protocol, and thus what will need to be implemented.
#### Simple impls
The simplest type of definitions for protocols are as regular methods. In this
style, protocols end up working exactly like normal JavaScript methods: they're
added as properties of the target type/object, and we call them using the
`foo.method()` syntax. `this` is accessible inside the methods, as usual.
Implementation syntax is very similar to protocol definitions, but it calls the
protocol itself, instead of `protocol`. It also refers to the type that you want
to implement it *on*:
```javascript
class Dog {}
// Implementing `Ducklike` for `Dog`s
Ducklike(Dog, [], {
walk() { return '*pads on all fours*' }
talk() { return 'woof woof. I mean "quack" >_>' }
peck(victim) { return 'Can I just bite ' + victim + ' instead?...' }
})
```
So now, our `Dog` class has two extra methods: `walk`, and `talk`, and we can
just call them:
```javascript
const pupper = new Dog()
pupper.walk() // *pads on all fours*
pupper.talk() // woof woof. I mean "quack" >_>
pupper.peck('this string') // Can I just bite this string instead?...
```
#### Multiple Dispatch
You may have noticed before that we have these `[]` in various places that don't
seem to have any obvious purpose.
These arrays allow protocols to be implemented not just for a single value of
`this`, but across *all arguments*. That is, you can have methods in these
protocols that use both `this`, and the first argument (or any other arguments)
in order to determine what code to actually execute.
This type of method is called a multimethod, and isn't a core JavaScript
feature. It's something that sets `protoduck` apart, and it can be really
useful!
The way to use it is simple: in the protocol *definitions*, you put matching
strings in different spots where those empty arrays were, and when you
*implement* the protocol, you give the definition the actual types/objects you
want to implement it on, and it takes care of mapping types to the strings you
defined, and making sure the right code is run:
```javascript
const Playable = protocol(['friend'], {
playWith: ['friend']
})
class Cat {}
class Human {}
class Dog {}
// The first protocol is for Cat/Human combination
Playable(Cat, [Human], {
playWith(human) {
return '*headbutt* *purr* *cuddle* omg ilu, ' + human.name
}
})
// And we define it *again* for a different combination
Playable(Cat, [Dog], {
playWith(dog) {
return '*scratches* *hisses* omg i h8 u, ' + dog.name
}
})
// depending on what you call it with, it runs different methods:
const cat = new Cat()
const human = new Human()
const dog = new Dog()
cat.playWith(human) // *headbutt* *purr* *cuddle* omg ilu, Sam
cat.playWith(dog) // *scratches* *hisses* omg i h8 u, Pupper
```
In general, most implementations you write won't care what types their arguments
are, but when you do? This may end up saving you a lot of trouble and allowing
some tricks you might realize you can do that weren't possible before!
#### Static impls
Finally, there's a type of protocol impl that doesn't involve a `this` value at
all: static impls. Some languages might call them "class methods" as well. In
the case of `protoduck`, though, these static methods exist on the protocol
object itself, rather than a regular JavaScript class.
Static methods can be really useful when you want to call them as plain old
functions without having to worry about the `this` value. And because
`protoduck` supports multiple dispatch, it means you can get full method
functionality, but with regular functions that don't need `this`: they just
operate on their standard arguments.
Static impls are easy to make: simply omit the first object type and use the
arguments array to define what the methods will be implemented on:
```javascript
const Eq = protocol(['a', 'b'], {
equals: ['a', 'b']
})
const equals = Eq.equals // This isn't necessary, but it shows that we
// don't need a `.` to call them, at all!
Eq([Number, Number], {
equals(x, y) {
return x === y
}
})
Eq([Cat, Cat], {
equals(kitty, cat) {
return kitty.name === cat.name
}
})
equals(1, 1) // true
equals(1, 2) // false
equals(snookums, reika) // false
equals(walter, walter) // true
equals(1, snookums) // Error! No protocol impl!
```
### API

@@ -93,3 +278,2 @@

The types in `<spec>` must map, by string name, to the type names specified in

@@ -96,0 +280,0 @@ `<types>`, or be an empty array if `<types>` is omitted. The types in `<spec>`

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc