Skip to content

Simple Reducer property behavior #437

Open
@justinbmeyer

Description

@justinbmeyer

I'd like something like the following to work:

DefineMap.extend({
  name: "string"
  nameChangeCount: {
	default: 0,
	reduce: {
		"name": (last, nameEvent)=> last + 1; 
	}
  }
})

Essentially, the reducer would specify events to bind to and a corresponding function that gets the last value, event emitted and returns the new value. Here's how fullName could be built without a getter (and #434 merged) :

DefineMap.extend({
  first: "string",
  last: "string",
  nameParts: {
	default: [],
	reduce: {
		"first": ( [first, last], {newValue} ) => [newValue, last],
 		"last": ( [first, last], {newValue} ) => [first, newValue],
	}
  },
  fullName: {
	default: "",
	reduce: {
		nameParts: (last, {newValue} )=> newValue.join(" ")
	}
  }
})

Notes / Questions

  • The functions should be called with the last value and all arguments used to dispatch the event. This makes it possible to have a funciton like (last, event, newValue, oldValue) => {}.

  • Should "deep" values be supported? For example reduce: { "task.length": ()=>{} }.

  • How should the set values be treated? Perhaps a reserved name?

     fullName: {
        default: "",
        reduce: {
           $lastSet: (last, newValue) => newValue,
           nameParts: (last, {newValue} )=> newValue.join(" ")
        }
     }
    
  • Could a get be used as a "final" stage and avoid intermediate steps?

    DefineMap.extend({
      first: "string",
      last: "string",
      fullName: {
        default: [],
        reduce: {
     	"first": ( [first, last], {newValue} ) => [newValue, last],
     	"last": ( [first, last], {newValue} ) => [first, newValue],
        },
        get: (reducedValue) => reducedValue.join(" ")
      }
    })
  • Can these "reducers" be shared and tested somehow? Or would we need can-reducer for this?

How to build this

We can add a reducer observable that would work like:

new ReducerObservable({
  "first": ( [first, last], {newValue} ) => [newValue, last],
   "last": ( [first, last], {newValue} ) => [first, newValue],
}, this, 0 )

This can be mixed in similar to how ResolverObservable is here:

var computeObj = make.computeObj(map, prop, new ResolverObservable(definition.value, map, defaultValue));

ReducerObservable would be a constructor function very similar in API to ResolverObservable, with the right symbols:

Reducer.prototype = {
	[getValue]()
	[setValue]()
	[onValue]() 
	[offValue]()
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions