paldepind / flyd

The minimalistic but powerful, modular, functional reactive programming library in JavaScript.
MIT License
1.56k stars 84 forks source link

Can flyd streams be made lazy ? #198

Closed sourcevault closed 5 years ago

sourcevault commented 5 years ago

when you have nodejs style API, you have this:

var fs = requie('fs');

var flyd = requie('flyd');

var read_hello = flyd.stream();

fs.readfile('./hello.txt',read_hello); // <- this runs immediately :(

read_hello.map(function(){/...../});

Is there some magic I can do to turn the stream upside down to make it lazy ?

var fs = requie('fs');

var flyd = requie('flyd');

var read_hello = flyd.stream();

read_hello = flyd.lazy(function(stream){

  fs.readfile('./hello.txt',stream);

});

read_hello.map(function(){/...../}).run(); // <- runs here !

Cheers !

nordfjord commented 5 years ago

you could maybe do something like:

const start = flyd.stream();
const readFile = (file)=> {
  const s = flyd.stream();
  fs.readFile(file, s);
}
const read_hello = start.chain(()=> readFile('./hello.txt'));

read_hello.map(fileContents => console.log(fileContents));

start(true);
StreetStrider commented 5 years ago

Afaik, flyd is push by it's nature. Check for pull-stream, Highland.js (they're oriented to pull strategy) and most.js (starting cold).

sourcevault commented 5 years ago

@StreetStrider it should be possible to express a push stream as a pull stream given that their operations are similar. I do not know what the theoretical equivalence law is.

@nordfjord yep - that seems like a cool workaround :D using chains !

Thank you both !

sourcevault commented 4 years ago

Hi @nordfjord !

const read_hello = start.chain(()=> readFile('./hello.txt'));

readFile would return undefined not a flyd stream.

I can think of creating a wrapper and create .run method but there might be a clever trick.

sourcevault commented 4 years ago
  var flyd = require('flyd')

  var fs = require('fs')

  readfile = function(name){

    var s = flyd.stream()

    var F = fs.readFile(name, function(err, data){return s(data.toString())})

    setTimeout(F, 0);

    return s;
  };

  stream = readfile('file1.txt');

  stream.map(function(data){

    console.log(data);

  });

Using setTimeout as a hack.

nordfjord commented 4 years ago

Hey @sourcevault ! Glad you found a solution!

I think you might be able to make a generic nodeback -> Stream function.

function fromNode(fn) {
  return (...args)=> {
    const s = stream()
    fn(...args, (err, data)=> s(data))
    return s
  }
}

Then you could use that as:

const readFile = fromNode(fs.readFile)

readFile('file1.txt')
  .map(x => x.toString())
  .map(console.log)
sourcevault commented 4 years ago

Hallo @nordfjord !! 😀

const readFile = fromNode(fs.readFile)

readFile('file1.txt') // <-- 1) This still runs immediately 
  .map(x => x.toString()) // <-- 2) This runs after [1]
  .map(console.log)

I don't think there is a way without using setTimeout to delay [1].

@nordfjord are you familiar with mostjs ?

https://github.com/cujojs/most

It runs / delays all side effects till all the .maps etc are defined, its said to be lazy.

flyd is more 'active', but I am trying to find a way to make it lazy, I hope I am making some sense.

nordfjord commented 4 years ago

don't worry, I understand what you're trying to accomplish.

I'm curious about your use case though, what use case do you have that requires lazy streams?

sourcevault commented 4 years ago

It is more of a intellectual curiosity when you corner me like that @nordfjord ☺️.

I have no immediate usecase, most streams work fine on node events like fs.readFile.

But then things like fs.watch flyd is a better choice.

I was hoping if there was a way to make flyd work for all most.js usecases.

I have looked at some libuv internals and using setTimeout 0 with flyd should be fine.

libuv runs everything with setTimeout 0 in the next tick, making it lazy so to speak.