-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wrap the result of all things in hub #45
Comments
Transformation form lazy pipelines and that is consistent regardless of the data structures those pipelines originate from. That's done intentionally for consistency. That being said, there is very simple mechanism to share transformation which is Also keep in mind that share by default would be a lot more painful since now you do have a way to share transformations, but if you invert default you'll loose laziness and won't be able to opt-out from sharing. |
@Gozala the thing is I call I'm fine with "multiple calls to fold means multiple consumptions from input" I find the fact that the implementation detail of a transformation (i.e. does it fork and merge) can cause multiple consumptions from input |
I'm sure it's not the issues with But yes I understand that it's different and can be confusing if not used to laziness. |
So it turns out that I didn't want something wrapped in hub and that I needed the lazy-ness shortly afterwards. I think it is hard to get used to, but it should not be the default |
In general, I don't think you can implement monadic data structures without laziness, at least I can't see how you would.
I don't think you can make hub default for reasons already stated in previous comments. I don't think you can implement monadic data structures without laziness. I could be wrong but I have no idea how to make what you're asking for. Problem has to trigger a pipe in our case it's Also note that Now all that being said, story could be greatly improved by documenting all of this. I've clearly have done poor job at this, but hopefully over time I'll manage to do that. Strategy I'm trying to use is in my code is to avoid multiple folds of the same source, but I don't have a silver bullet how to do that. Here is a recent work I did with Gordon, which I think maybe little similar to what I think you're using at work, may help to inspire you: |
@Raynos I'd also welcome you to try what you have in mind in the fork and see if that works. If there is a solution that's great, if not it'll definitely help to understand why things are the way they are. |
@Gozala documenting lazyness and the way inputs get consumed multiple times is a massive plus. Well I avoid multiple folds too. But even then if the transformation contains merge(filter(x, b), filter(x, a)) it still consumes twice even with a single fold. I'll mess with rocket-bar at some point. I feel like i'm reinventing parts of reflex at work :P |
Maybe we can address that specific case with another functions though. Although it's not clear why filter can be or(a, b) kind of thing. Maybe Also I think elm does this differently: |
One thing that comes to mind is to have a |
Alternatively could be var forks = fork(input, {
foo: isFoo,
bar: isBar
})
print(forks.foo)
print(forks.bar) Where |
Actually!! fork could delay reduction of |
@Raynos will that address your problems ? |
@Gozala the problem with print is that its a fold. your folding twice of course it breaks. what you want is var forks = fork(input, {
foo: isFoo,
bar: isBar
})
fold(join(introspect(forks.foo), introspect(forks.bar))) |
@Gozala it doesn't quite handle my issue because I want to fork the input later as well in a seperate function. That later fork will again cause an extra reduction of input even though fold is called once |
I think we should just look concretely at my example. |
I understand, although you could do: var forks = fork(input, { a: True, b: True }) And than pass I don't see how you can implicitly abstract time without being lazy, as a matter of fact that sounds like a foot gun. Since you won't have a clue what or when that would happen. BTW you could always wrap the In other words I see following options:
I consider 3rd no go as it makes no behavior guarantees. 1st is a current case, but allows you to switch to 3rd in specific areas using Maybe 2nd can be somehow made default to the reducers level, but I'm not sure it's a good idea, since it will cause whole new set of issues. For example if one of the transformation is never read nothing will happen at all. It also maybe an option to share everything but delay all the reads from input by a tick hoping that by then all the readers are in place and if not, well too bad. That adds ambiguity and sounds too magical so I don't think it will make a best default. Maybe you wanna explore any of this and see if it turns out to be better in practice. |
@Gozala atm I just do |
Actually no. I don't hub it! I fixed my input to be weird >_< |
The problem with 2. is that I pull from input asynchronously when some other value comes in. So that's a race condition. What I currently do is input is this function createInput() {
var current = {}
startPolling()
return reducible(function (next) {
sendCurrent(next)
sendNewData(next)
})
} The problem is that the input starts polling the async source before it's being folded. but this does mean that I can reduce the input as many times as I want without side effects. The above example is like hub EXCEPT when you reduce it sends you a snapshot of the current state and then all changes. So maybe I should hub the polling events and then have input be: function createInput() {
var input = PollerGuy()
return reductions(hub(input), function () {
// accumulate all the state stuff???
}, {})
} But then I only want to send you a snapshot of current state once you start reducing and then send deltas. I don't want to send you a stream of snapshots all the time. What I really want is function createInput() {
var poller = createPoller()
return merge([currentState(poller), hub(poller)])
} i.e. the result I want to return is whatever the current state is when you reduce it followed by only the current events. It's that |
Your description sounds like buggy version of buffer-reduce |
I think this is close to what we have being doing in reflex. Although if you remember I was suggesting to have Your suggestion sound too magical, sometimes dispatch state sometimes dispatch delta is awkward IMO. What's interesting though in reflex examples starting reductions with empty object was good enough. I wonder how is you're case different. |
@Gozala my approach is different actually. I'm representing state of app as a flat list of objects with Sure I could blast snapshots but that's a bit annoying. I actaully want to only blast deltas, except the first value is a list of all deltas needed to get to current state, maybe that's a bad idea though. |
That's a very definition of Hickeys complecting, but maybe it's not to bad in practice |
@Gozala it's similar to Property ( https://github.com/raimohanska/bacon.js?utm_source=javascriptweekly&utm_medium=email#property ). i.e. there's both a way to get current state (snapshot) and get updates to the thing. I feel this notion of reducible which is event but also has a snapshot of current state is missing. |
No need to over think things:
This is actually what |
@Gozala but then I have this fugly api map(thing, function (blob) {
var current = blob.state
var update = blob.update
// do shit
}) instead of map(merge([state(thing), thing]), function (update) {
// do shit
}) |
I like term fugly :D |
Anyway that's why I reflex did: map(states, function(snapshot) {
// do thing with a snapshot
}) If you need both state and delta you have: reductions(states, function(current, previous) {
var delta = diff(previous, current)
}) Maybe not ideal but worked fine in most cases I've tried. I also considered this (but then thought it was ugly): map(state, function(snapshot) {
// if you need only delta then
var delta = diff(snapshot)
}) Also it's fact that your map dependents on state snapshot at first is worrying |
@Gozala reductions is a pain in the ass because I want two things. I want to reduct and accumulate state between transformations and I want a result. The result of my transformation is different from the state I want to reduct. I've just been storing state in closures for this and using expand to return some values sometimes and reduct state between transformations. I think the real question is do I want to do transformations on streams of snapshots or streams of changes. |
I think you're missing a point and there for having all this issues. Library is intentionally designed to make local state hard. It's fine to capture bindings from closures, but it's not to mutate them. Intention is that all the stateful code (updating any references or doing mutations) will go into source implementation or into consumer implementation. All the transformation logic should be state free and order independent since non of that is guaranteed. I think you'll have a lot better time if you don't try to workaround these limitations as they are guardrails. As of And in ES6 even use destructuring. At the moment you do bunch of tradeoffs just for a typing convenience. |
Here is yet another example how stateful transformation can be done when that is really necessary: var LOCAL = 0
var ACCUMULATED = 1
function lift(input, f, start) {
return reducible(function(next, initial) {
return reduce(input, function transform(value, state) {
var data = [].concat(f(value, state[LOCAL]))
var local = data.pop()
var result = state[ACCUMULATED]
var count = data.length
var index = 0
while (index < count) {
result = next(data[index], result)
if (isReduced(result)) return result
index = index + 1
}
return [local, result]
}, [start, initial])
})
}
// Transform any text stream into stream of lines
var lines = lift(text, function(chunk, prefix) {
return prefix.concat(chunk).split("\n")
}, "") The reason I hesitate to include this by default is that it's easy to include a new state in the end of the returned array. |
It's weird to have my filter being called multiple times because there are two forks after it.
Especially when I merge those two forks later.
advantages
disadvantages
The text was updated successfully, but these errors were encountered: