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

Transducers in Redux #176

Closed
gaearon opened this Issue Jun 24, 2015 · 35 comments

Comments

@gaearon
Collaborator

gaearon commented Jun 24, 2015

Reusing Store functionality is something Flux has traditionally been very bad at, due to Stores not being composable. For example, I do silly things in Flux React Router Example just to reuse the pagination code in different Stores.

The reason for this being hard in Flux is because each Store is an event emitter, and composing event emitters is hard. But in Redux, “Stores” (reducers, really!) are pure functions, and pure functions are easy to compose!

This gist shows how reducers can be easily composed. But it's still manual composition. It's expressive, but there is some boilerplate for repetitive tasks. For example, you might want to implement things like optimistic updates, undo/redo or pagination scoped to specific parts of your state, but then you can't do it just once on the top—you need to repeat this logic in every relevant reducer.

Higher order functions to the rescue! In fact, we already have a higher-order reducer: composeStores (to be renamed composeReducers). It takes your app's reducers and returns a reducer that composes their state into a single object.

We can express more patterns with higher order reducers? Absolutely! In fact this may be the beginning of reusing logic in Flux because these higher order reducers don't need to know about specific actions in your app.

Consider this:

function whitelistActions(reducer, actionPredicate) {
  return (state, action) => actionPredicate(action) ? reducer(state, action) : state;
}

It's an example higher order reducer. Its purpose is a performance optimization by “cutting off” some expensive reducers from some unrelated actions.

const reducer = composeReducers({
  entities: composeReducers(entitiesReducers),

  // performance optimization!
  editor: whitelistActions( 
    composeReducers(editorReducers),
    action => action.type.startsWith('EDITOR_')
  )
});

This is just an example of what higher-order reducers can do. They're not tied to your particular app. You can pass particular action types as parameters to them so they know which precisely actions to handle.

You can user higher-order reducers to implement things like:

  • undo/redo
  • pagination
  • optimistic updates
  • performance optimization
  • throttling actions
  • etc

in a generic way, reusable across the Redux ecosystem.

And guess what? We didn't invent higher-order reducers! They are precisely the same thing as transducers in ClojureScript.

Anybody want to take a stab at writing some transducers for Redux?

@kevinrobinson

This comment has been minimized.

Show comment
Hide comment
@kevinrobinson

kevinrobinson Jun 24, 2015

Interesting! I prototyped this when looking at reducing over a whole log, since it seemed like a cool kind of optimization and I was generally excited about transducers. :)

In stepping back a bit, it seemed like two other optimizations might be more important than allocations. One was to keep snapshots of previous reductions (especially as the n for items in the log grows) and another was an optimizer that could memoizing hot calls to reducers. The problems I saw were how to make this work when composing calls, I found I wanted to graph of computation Clojure style and then sort of stopped and didn't work much further on it. So I'm excited to see you're thinking about this and super curious to hear about ideas other people have. :)

Another thought is that I think finding the right API for composition is even more important, and so grounding compute or memory optimizations in real numbers might help too. I don't have a sense of quite how bad the naive solutions are, so that might help inform what optimizations can have the most impact.

kevinrobinson commented Jun 24, 2015

Interesting! I prototyped this when looking at reducing over a whole log, since it seemed like a cool kind of optimization and I was generally excited about transducers. :)

In stepping back a bit, it seemed like two other optimizations might be more important than allocations. One was to keep snapshots of previous reductions (especially as the n for items in the log grows) and another was an optimizer that could memoizing hot calls to reducers. The problems I saw were how to make this work when composing calls, I found I wanted to graph of computation Clojure style and then sort of stopped and didn't work much further on it. So I'm excited to see you're thinking about this and super curious to hear about ideas other people have. :)

Another thought is that I think finding the right API for composition is even more important, and so grounding compute or memory optimizations in real numbers might help too. I don't have a sense of quite how bad the naive solutions are, so that might help inform what optimizations can have the most impact.

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jun 24, 2015

Wow!

They are precisely the same thing as transducers in ClojureScript.

Well not precisely, there is actually a spec for transducers in JavaScript https://github.com/cognitect-labs/transducers-js#the-transducer-protocol it mentions three functions instead of one etc.

This spec has number of compatible implementations:

Maybe it would be cool if Redux support actual spec, then we could use existing transducers implementations. Although it probably too crazy :)

rpominov commented Jun 24, 2015

Wow!

They are precisely the same thing as transducers in ClojureScript.

Well not precisely, there is actually a spec for transducers in JavaScript https://github.com/cognitect-labs/transducers-js#the-transducer-protocol it mentions three functions instead of one etc.

This spec has number of compatible implementations:

Maybe it would be cool if Redux support actual spec, then we could use existing transducers implementations. Although it probably too crazy :)

@funkenstein

This comment has been minimized.

Show comment
Hide comment
@funkenstein

funkenstein Jun 24, 2015

It's about time, yo! ;)

I'll mess around with this idea a bit today. I think the easiest way to do this is to transparently wrap each function passed to composeReducers in a transducer which only implements the step method. And @rpominov isn't crazy at all, sticking with the spec outlined by Cognitect is definitely the way to go here - it gives us access to all of the above libraries for free (and the spec is honestly trivial to implement).

funkenstein commented Jun 24, 2015

It's about time, yo! ;)

I'll mess around with this idea a bit today. I think the easiest way to do this is to transparently wrap each function passed to composeReducers in a transducer which only implements the step method. And @rpominov isn't crazy at all, sticking with the spec outlined by Cognitect is definitely the way to go here - it gives us access to all of the above libraries for free (and the spec is honestly trivial to implement).

@hugobessaa

This comment has been minimized.

Show comment
Hide comment
@hugobessaa

hugobessaa Jun 24, 2015

@rpominov following a spec is the whole purpose of transducers! That way our reducers can talk with actions and really any other communication system that follow the spec (collections, streams, channels, pipes, observables…).

Clojure's page about transducers has some insight. It also links this nice talk of Rich Hickey.

hugobessaa commented Jun 24, 2015

@rpominov following a spec is the whole purpose of transducers! That way our reducers can talk with actions and really any other communication system that follow the spec (collections, streams, channels, pipes, observables…).

Clojure's page about transducers has some insight. It also links this nice talk of Rich Hickey.

@alexeygolev

This comment has been minimized.

Show comment
Hide comment
@alexeygolev

alexeygolev Jun 25, 2015

@hugobessaa I agree... Using ramda with redux will be so comfortable this way

alexeygolev commented Jun 25, 2015

@hugobessaa I agree... Using ramda with redux will be so comfortable this way

@funkenstein

This comment has been minimized.

Show comment
Hide comment
@funkenstein

funkenstein Jun 25, 2015

While we're discussing - have we thought about using transducers for middleware? After all, middleware in Redux seems to be (correct me if I'm wrong) just a form of event stream processing. I think with enough thought these could really tie a lot of the functional concepts we already have together nicely. And with a default set of Redux-specific middleware transducers and a nice API for creating new ones, it would be very possible to do this while maintaining the "don't have to live in a cabin built out of functional programming books to understand this" philosophy of the library.

If you stretch the concept to its extreme, you could implement Flux as one gigantic transducer. Stores reduce over actions to accumulate their state, components reduce over store state to display the proper UI elements and actions reduce over input events on components to perform their duties. It's the circle of life, baby!

I'm not actually suggesting this (I've actually tried it - turns out transducers don't solve everything D:), but I think it's worth considering various uses for them while we're on the topic.

funkenstein commented Jun 25, 2015

While we're discussing - have we thought about using transducers for middleware? After all, middleware in Redux seems to be (correct me if I'm wrong) just a form of event stream processing. I think with enough thought these could really tie a lot of the functional concepts we already have together nicely. And with a default set of Redux-specific middleware transducers and a nice API for creating new ones, it would be very possible to do this while maintaining the "don't have to live in a cabin built out of functional programming books to understand this" philosophy of the library.

If you stretch the concept to its extreme, you could implement Flux as one gigantic transducer. Stores reduce over actions to accumulate their state, components reduce over store state to display the proper UI elements and actions reduce over input events on components to perform their duties. It's the circle of life, baby!

I'm not actually suggesting this (I've actually tried it - turns out transducers don't solve everything D:), but I think it's worth considering various uses for them while we're on the topic.

@hugobessaa

This comment has been minimized.

Show comment
Hide comment
@hugobessaa

hugobessaa Jun 25, 2015

@funkenstein this is pretty much the way Cycle.js works (just with different naming and events/views decoupling). I think Redux should do as much as possible to be just a lib that connect stuff that aren't redux specific.

hugobessaa commented Jun 25, 2015

@funkenstein this is pretty much the way Cycle.js works (just with different naming and events/views decoupling). I think Redux should do as much as possible to be just a lib that connect stuff that aren't redux specific.

@funkenstein

This comment has been minimized.

Show comment
Hide comment
@funkenstein

funkenstein Jun 25, 2015

@hugobessaa Ah, I believe I misunderstood the goal. We don't want transducer support in Redux, we just want transducers written for Redux. And upon using them, they will just return standard reducer functions which are already compatible with Redux (easy enough, any transducer library worth its salt has a toFn function for transducer transformers).

funkenstein commented Jun 25, 2015

@hugobessaa Ah, I believe I misunderstood the goal. We don't want transducer support in Redux, we just want transducers written for Redux. And upon using them, they will just return standard reducer functions which are already compatible with Redux (easy enough, any transducer library worth its salt has a toFn function for transducer transformers).

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jun 26, 2015

Collaborator

I'm dumb in this area so I'd be happy to see whatever you folks come up with 💃

Collaborator

gaearon commented Jun 26, 2015

I'm dumb in this area so I'd be happy to see whatever you folks come up with 💃

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 4, 2015

Collaborator

The most common transducer I think will be something like this, which simply executes multiple reducers in sequence https://github.com/acdlite/reduce-reducers. I'm not an expert on this topic either... I'm sure this operation already has an actual name in FP.

Collaborator

acdlite commented Jul 4, 2015

The most common transducer I think will be something like this, which simply executes multiple reducers in sequence https://github.com/acdlite/reduce-reducers. I'm not an expert on this topic either... I'm sure this operation already has an actual name in FP.

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 4, 2015

Collaborator

Okay I lied, that's not a transducer... A transducer is reducer => reducer' Still useful, though!

Collaborator

acdlite commented Jul 4, 2015

Okay I lied, that's not a transducer... A transducer is reducer => reducer' Still useful, though!

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

I made a library to create reducers from transducers:

https://github.com/acdlite/redux-transduce

It supports transducers that conform to the transducer protocol used by transducers.js and transducers-js. I did discover a caveat, though (quoting from the README):

Transducers typically operate on collections. It's possible to use transducers to transform asynchronous streams, but it requires the use of local state that persists over time. We can't do this, because Redux makes a hard assumption that the reducer is a pure function -- it must return the same result for a given state and action, every time.

For this reason, transduce() transforms actions one at a time. That means transducers like filter() and map() work fine, but take() and dedupe() do not.

I'm not sure if this limits its usefulness prohibitively. On the plus side, I finally understand transducers :D

Collaborator

acdlite commented Jul 5, 2015

I made a library to create reducers from transducers:

https://github.com/acdlite/redux-transduce

It supports transducers that conform to the transducer protocol used by transducers.js and transducers-js. I did discover a caveat, though (quoting from the README):

Transducers typically operate on collections. It's possible to use transducers to transform asynchronous streams, but it requires the use of local state that persists over time. We can't do this, because Redux makes a hard assumption that the reducer is a pure function -- it must return the same result for a given state and action, every time.

For this reason, transduce() transforms actions one at a time. That means transducers like filter() and map() work fine, but take() and dedupe() do not.

I'm not sure if this limits its usefulness prohibitively. On the plus side, I finally understand transducers :D

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

Isn't it possible to store transducer (and generally, middleware) state in the global app state under some unique key?

Contributor

clearjs commented Jul 5, 2015

Isn't it possible to store transducer (and generally, middleware) state in the global app state under some unique key?

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

I thought about that, but how would the key be generated? What if you call the same transducer multiple times? Eventually, you end up just writing your own reducer manually, which kinda defeats the purpose.

Collaborator

acdlite commented Jul 5, 2015

I thought about that, but how would the key be generated? What if you call the same transducer multiple times? Eventually, you end up just writing your own reducer manually, which kinda defeats the purpose.

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jul 5, 2015

@acdlite Wow, cool! I was thinking about function with exact same signature earlier today :)

I would implement it a bit different, though:

function applyTransducer(transducer, reducer) {
  const xform = transducer({
    '@@transducer/step': reducer
  })
  return (state, action) => {
    const result = xform['@@transducer/step'](state, action)
    if (result && result['@@transducer/reduced']) {
      throw new Error('Early termination doesn\'t make sense in context of Redux (you should\'t use transducers like take(n))')
    }
    return result
  }
}

This way stateful transducers like dedupe() will work, and if a transducer with early termination used like take() an exception will be thrown.

rpominov commented Jul 5, 2015

@acdlite Wow, cool! I was thinking about function with exact same signature earlier today :)

I would implement it a bit different, though:

function applyTransducer(transducer, reducer) {
  const xform = transducer({
    '@@transducer/step': reducer
  })
  return (state, action) => {
    const result = xform['@@transducer/step'](state, action)
    if (result && result['@@transducer/reduced']) {
      throw new Error('Early termination doesn\'t make sense in context of Redux (you should\'t use transducers like take(n))')
    }
    return result
  }
}

This way stateful transducers like dedupe() will work, and if a transducer with early termination used like take() an exception will be thrown.

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

@acdlite A unique key could be used for that. One per an invocation of any (including async) action creator.

Update: actually, this won't work, I guess. We'd need a way to let transducers store data there, which would require reimplementing them. Thoughts?

Contributor

clearjs commented Jul 5, 2015

@acdlite A unique key could be used for that. One per an invocation of any (including async) action creator.

Update: actually, this won't work, I guess. We'd need a way to let transducers store data there, which would require reimplementing them. Thoughts?

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

@rpominov That doesn't look like it works with stateful transducers to me... Can you show me an example?

Collaborator

acdlite commented Jul 5, 2015

@rpominov That doesn't look like it works with stateful transducers to me... Can you show me an example?

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

@rpominov Okay, I can see how that could work with dedupe(), but then there's still the bigger problem of the reducer no longer being pure.

Collaborator

acdlite commented Jul 5, 2015

@rpominov Okay, I can see how that could work with dedupe(), but then there's still the bigger problem of the reducer no longer being pure.

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jul 5, 2015

@acdlite Right, but I think they don't intend to be pure anyway, although it might be a problem with their usage in Redux, I agree. Seems like transducers don't fit very well here.

Btw, I recommend everybody to watch (if haven't already) this talk by Rich Hickey about transducers
http://www.youtube.com/watch?v=6mTbuzafcII

rpominov commented Jul 5, 2015

@acdlite Right, but I think they don't intend to be pure anyway, although it might be a problem with their usage in Redux, I agree. Seems like transducers don't fit very well here.

Btw, I recommend everybody to watch (if haven't already) this talk by Rich Hickey about transducers
http://www.youtube.com/watch?v=6mTbuzafcII

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jul 5, 2015

Right, but I think they don't intend to be pure anyway,

Sorry, need to clarify, I meant transducers don't intend to be pure, while reducers in Redux certainly are.

rpominov commented Jul 5, 2015

Right, but I think they don't intend to be pure anyway,

Sorry, need to clarify, I meant transducers don't intend to be pure, while reducers in Redux certainly are.

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

Custom implementations that would have their state stored externally may be used instead of stateful transducers. A key would be generated per each usage of such transducers. So, instead of dedupe(), there would be something like dedupe(state)().

Contributor

clearjs commented Jul 5, 2015

Custom implementations that would have their state stored externally may be used instead of stateful transducers. A key would be generated per each usage of such transducers. So, instead of dedupe(), there would be something like dedupe(state)().

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

A key would be generated per each usage of such transducers

How is that pure?

Collaborator

acdlite commented Jul 5, 2015

A key would be generated per each usage of such transducers

How is that pure?

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

Why not? UID generation is a pure operation. Did I misunderstand you?

Contributor

clearjs commented Jul 5, 2015

Why not? UID generation is a pure operation. Did I misunderstand you?

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jul 5, 2015

A key would be generated per each usage of such transducers

But where the state of dedupe will be stored? I also don't understand.

rpominov commented Jul 5, 2015

A key would be generated per each usage of such transducers

But where the state of dedupe will be stored? I also don't understand.

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

dedupe would be a function that returns a transducer which stores its state in the global app state under a unique key generated per a call to the dedupe function. Each invocation of this transducer should store a new state instance under the same key (i.e., transducer's state shouldn't be mutated).

Contributor

clearjs commented Jul 5, 2015

dedupe would be a function that returns a transducer which stores its state in the global app state under a unique key generated per a call to the dedupe function. Each invocation of this transducer should store a new state instance under the same key (i.e., transducer's state shouldn't be mutated).

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

Those keys can be stored under another unique key, one per an action creator invocation. This should work, but looks really complicated. Would some kind of middleware be able to simplify that?

Contributor

clearjs commented Jul 5, 2015

Those keys can be stored under another unique key, one per an action creator invocation. This should work, but looks really complicated. Would some kind of middleware be able to simplify that?

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

On the other hand, it's difficult for me to imagine a use case where such pseudo-stateful operations were necessary for a reducer.

Contributor

clearjs commented Jul 5, 2015

On the other hand, it's difficult for me to imagine a use case where such pseudo-stateful operations were necessary for a reducer.

@rpominov

This comment has been minimized.

Show comment
Hide comment
@rpominov

rpominov Jul 5, 2015

If we want to make each action invocation to be pure, @acdlite already did that in https://github.com/acdlite/redux-transduce by simply initialising transducer on each action. What we can't do is to allow transducers to store state between actions invocations (if we want reducers to be pure).

rpominov commented Jul 5, 2015

If we want to make each action invocation to be pure, @acdlite already did that in https://github.com/acdlite/redux-transduce by simply initialising transducer on each action. What we can't do is to allow transducers to store state between actions invocations (if we want reducers to be pure).

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 5, 2015

Contributor

@rpominov If there are good use cases for this, it can be done. Transducers needn't store local state, they can return it instead and it can be made to land in the global app state. The problem is that this brings in more magic.

Contributor

clearjs commented Jul 5, 2015

@rpominov If there are good use cases for this, it can be done. Transducers needn't store local state, they can return it instead and it can be made to land in the global app state. The problem is that this brings in more magic.

@acdlite

This comment has been minimized.

Show comment
Hide comment
@acdlite

acdlite Jul 5, 2015

Collaborator

So it turns out that transducers work really well if you use them outside the reducer — to dispatch actions! This can be done by wrapping the store (using a higher-order store) so that it conforms to the transducer protocol. Then you can use into(to, xform, from) to dispatch a collection of actions.

screen shot 2015-07-05 at 2 24 59 pm

https://github.com/acdlite/redux-transducers#example-mapping-strings-to-actions

Collaborator

acdlite commented Jul 5, 2015

So it turns out that transducers work really well if you use them outside the reducer — to dispatch actions! This can be done by wrapping the store (using a higher-order store) so that it conforms to the transducer protocol. Then you can use into(to, xform, from) to dispatch a collection of actions.

screen shot 2015-07-05 at 2 24 59 pm

https://github.com/acdlite/redux-transducers#example-mapping-strings-to-actions

@Chris-Andrews

This comment has been minimized.

Show comment
Hide comment
@Chris-Andrews

Chris-Andrews Jul 7, 2015

I think the approach @acdlite posted above is a great method for combining transducers with redux. As it was pointed out above, an important aspect of the design of redux is that its reducers are pure functions, and some transducers are stateful transformations. The idea that you could use only stateless transducers is problematic because transducers are composable, and it may be difficult to know whether a composed transducer contains any stateful operations. However, by transforming the input to a reducer, you can keep the reducer pure, enable stateful transformations, and preserve the ability to combine and reuse those transformations by applying them to the input of other reducers.

Chris-Andrews commented Jul 7, 2015

I think the approach @acdlite posted above is a great method for combining transducers with redux. As it was pointed out above, an important aspect of the design of redux is that its reducers are pure functions, and some transducers are stateful transformations. The idea that you could use only stateless transducers is problematic because transducers are composable, and it may be difficult to know whether a composed transducer contains any stateful operations. However, by transforming the input to a reducer, you can keep the reducer pure, enable stateful transformations, and preserve the ability to combine and reuse those transformations by applying them to the input of other reducers.

@kurtharriger

This comment has been minimized.

Show comment
Hide comment
@kurtharriger

kurtharriger Jul 7, 2015

Perhaps alternatively a library such as baconjs. There is a library called react-bacon that sorta prototyped the idea that I played around with once before. There is also a react rxjs library too that may be woth looking at.
These libraries already provide ways to compose event streams and and implement some common stateful transforms such as debounce and flatmapLatest.

kurtharriger commented Jul 7, 2015

Perhaps alternatively a library such as baconjs. There is a library called react-bacon that sorta prototyped the idea that I played around with once before. There is also a react rxjs library too that may be woth looking at.
These libraries already provide ways to compose event streams and and implement some common stateful transforms such as debounce and flatmapLatest.

@gaearon gaearon added the ecosystem label Jul 31, 2015

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jul 31, 2015

Collaborator

Closing, as the issue has been dormant for a while.
redux-transducers is mentioned in the Ecosystem page of the new docs.

Please feel free to keep the discussion going.

Collaborator

gaearon commented Jul 31, 2015

Closing, as the issue has been dormant for a while.
redux-transducers is mentioned in the Ecosystem page of the new docs.

Please feel free to keep the discussion going.

@gaearon gaearon closed this Jul 31, 2015

@mindjuice

This comment has been minimized.

Show comment
Hide comment
@mindjuice

mindjuice Jul 31, 2015

Contributor

Sorry to necro this thread, but I just came across it. Very interesting example of using transducers with Redux @acdlite. Thanks for that.

Regarding this though:

Why not? UID generation is a pure operation. Did I misunderstand you?

A UUID generation function is the least pure function you can possibly have. It's guaranteed to return a different value every time you call it! 😄

It's even worse than a random number generator, because that will sometimes return a previously returned value.

Contributor

mindjuice commented Jul 31, 2015

Sorry to necro this thread, but I just came across it. Very interesting example of using transducers with Redux @acdlite. Thanks for that.

Regarding this though:

Why not? UID generation is a pure operation. Did I misunderstand you?

A UUID generation function is the least pure function you can possibly have. It's guaranteed to return a different value every time you call it! 😄

It's even worse than a random number generator, because that will sometimes return a previously returned value.

@clearjs

This comment has been minimized.

Show comment
Hide comment
@clearjs

clearjs Jul 31, 2015

Contributor

@mindjuice You're right, thanks! That's a glitch in my thought process that happened due to inertia after thinking about commands vs. queries (CQS) in context of another problem. UUID has query signature (it returns a value), which somehow led me to stating that it is pure. But actually it isn't pure nor does it satisfy CQS, as it is not referentially transparent.

Contributor

clearjs commented Jul 31, 2015

@mindjuice You're right, thanks! That's a glitch in my thought process that happened due to inertia after thinking about commands vs. queries (CQS) in context of another problem. UUID has query signature (it returns a value), which somehow led me to stating that it is pure. But actually it isn't pure nor does it satisfy CQS, as it is not referentially transparent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment