NateTheGreatt / bitECS

Flexible, minimal, data-oriented ECS library for Typescript
Mozilla Public License 2.0
944 stars 84 forks source link

Parameters not being passed down pipe #36

Closed marcusround closed 3 years ago

marcusround commented 3 years ago
import * as bitecs from 'bitecs'

const world = bitecs.createWorld()

const systemA = bitecs.defineSystem((world, parameter) => {

  console.log(parameter)

})

const systemB = bitecs.defineSystem((world, parameter) => {

  console.log(parameter)

})

const pipe = bitecs.pipe(systemA, systemB)

pipe(world, "Hello")

Expected output:

Hello
Hello

Received output:

Hello
undefined

Not sure if this is the intended way to use pipes (I understand that to get around this I could add a property to world instead) but the fact that the parameter is passed through successfully to the first system, but not any thereafter, is confusing. This led to quite a tricky bug for me (I was passing deltaTime down a pipe and was bewildered when refactoring caused it to break)

NateTheGreatt commented 3 years ago

pipe is actually implemented like any other pipe from other functional libraries, and is supposed to function like the upcoming ES6 pipe operator |>. functions are connected such that each function's output is "piped" to the next function's input. because functions can only have a single return value, i opted to have systems implicitly return world, and so this is always what is piped to the next system.

however, i have recognized the useful nature of being able to have all initial arguments passed down the pipe. i could introduce another composition function, maybe call it sequence or something, where all initial input parameters are passed to each function in the composition as you are expecting (instead piping each output to the next input).

however, there are alternative solutions which allow unique function signatures per system.

you can construct pipelines manually:

const pipeline = (...args) => {
  systamA(...args)
  systamB(...args)
}

const args = [world, dependencyA, dependencyB]
pipeline(...args)

or here is another strategy using pipe:

pipe(
  world => systemA(world, ...args),
  world => systemB(world, dependencyB),
)

lmk if you have any thoughts on this

marcusround commented 3 years ago

I see, I wasn't familiar with pipe as a concept and misunderstood the intent. I was treating a pipe as just a neat 'package' of systems that I could import at once. Your first code solution looks like it covers my intent and is easy to construct, not sure there's any need to add anything to the library itself.

NateTheGreatt commented 3 years ago

cool! i'll close this then 🍻