5

I'm trying to convert an old api that uses a lot of dot notation chaining which needs to be kept ie:

[1,2,3,4].newSlice(1,2).add(1) // [3]

I'd like to add the functional style of composition in this example Ramda but lodash or others would be fine:

const sliceAddOne = R.compose(add(1), newSlice(1,2)) 
sliceAddOne([1,2,3,4])// [3]

My question is how can I do both chaining and composition in my function newSlice what would this function look like?

I have a little jsBin example.

cmdv
  • 1,693
  • 3
  • 15
  • 23

1 Answers1

3

EDIT

I think I initially misunderstood you. You want a function f that you can call as

f(...args)(someObj) ===  someObj.f(...args)

I would do that like this

// infix
Array.prototype.newSlice = function(x,y) { return this.slice(x,y) }

// prefix 
const newSlice = (x,y) => F => F.newSlice(x,y)

This is a good design because you can implement newSlice on any Object you wish to have newSlice capability on and the prefix function will just work. This also allows you to have a unique implementation of newSlice on each object type (Array, String, Other...) as it's likely that the underlying data we are slicing will be different – You get all of this without having to do silly conditional this checks within the body of your function.

// newSlice :: [a] -> (Integer,Integer) -> [a]
Array.prototype.newSlice = function(x,y) {
  return this.slice(x,y)
}

// newSlice :: String -> (Integer,Integer) -> String
String.prototype.newSlice = function(x,y) {
  return this.substring(x,y)
}

// even on a custom object
class LottoTicket {
  constructor(...nums) { this.nums = nums }
  newSlice(x,y) { return this.nums.slice(x,y) }
}

// newSlice :: (Array, String) a => (Integer,Integer) -> a -> a
const newSlice = (x,y) => F => F.newSlice(x,y)

// use it in prefix position
console.log(newSlice(1,2)([1,2,3,4]))              // [2]
console.log(newSlice(1,2)("abcd"))                 // 'b'
console.log(newSlice(1,2)(new LottoTicket(9,8,7))) // [8]

// use it infix position
console.log([1,2,3,4].newSlice(1,2))                // [2]
console.log("abcd".newSlice(1,2))                   // 'b'
console.log((new LottoTicket(9,8,7)).newSlice(1,2)) // [8]
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • thanks for the pointer, it's why I tend to use ramda as all function are curried :) – cmdv Aug 24 '16 at 09:38
  • @cmdv I appear to have mistook your initial intentions. I've reworked my answer with a new understanding. – Mulan Aug 24 '16 at 10:19
  • thank you, very thorough and plenty for me to take away – cmdv Aug 24 '16 at 10:43
  • with your answer i would have to add new prototypes to Array + String what would be a way to keep this inline in the function it self? I have 100 odd functions that I need to convert like this and worry with name clashing. – cmdv Aug 24 '16 at 10:57
  • @cmdv aren't they all defined on the prototypes now? – Mulan Aug 24 '16 at 11:04
  • They are all on a global, Global.prototype.slice, but wanted to ES6 export and isolate the functions rather than being dependent on parent child. I'm going to go with your solution http://jsbin.com/gihepevaxe/1/edit?js,console – cmdv Aug 24 '16 at 12:43
  • Do you mean `Object.prototype.slice` ? – Mulan Aug 24 '16 at 17:37
  • well right now it looks like this: `$Glob.prototype['splice'] = function (index, count)..` hence they need a lot of cleaning up :P – cmdv Aug 25 '16 at 08:34