-
Notifications
You must be signed in to change notification settings - Fork 3
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
uncurry store update function and drop Option wrapper around effects #224
Conversation
docs/examples/cancellation.md
Outdated
store.dispatch(SetActivity(activity.some)) | ||
) | ||
) | ||
(_, _) match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could avoid this I think since case matches at the top level of a function are allowed (due to the questionable PartialFunction Function subclass relationship)
docs/examples/counter.md
Outdated
case Inc(amount) => | ||
state => state.copy(counter = state.counter + amount) -> none | ||
case Reset => _.copy(counter = 0) -> none | ||
(_, _) match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can omit this I think.
docs/examples/reusable-components.md
Outdated
_ match { | ||
case SetFruit(fruit) => _.copy(fruit = fruit) -> none | ||
case Inc => state => state.copy(counter = state.counter + 1) -> none | ||
(_, _) match { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, could be:
_ =>
{
case (SetFruit(fruit), state) => state.copy(fruit = fruit) -> F.unit
case (Inc, state) => state.copy(counter = state.counter + 1) -> F.unit
}
I think.
@@ -44,9 +43,9 @@ class App[F[_]](implicit val F: Async[F]) extends ff4s.App[F, State, Action] { | |||
override val store = for { | |||
|
|||
store <- ff4s.Store[F, State, Action](State())(_ => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we want something like this:
def simple[F[_]: Concurrent, State, Action](init: State)(update: (Action, State) => State): Store[F, State, Action] = {
val unit = Concurrent[F].unit
apply[F, State, Action](init) { _ => { (a, s) => (update(a, s), unit) } }
}
Then I think many simple apps can become something like:
store <- ff4s.Store.simple[F, State, Action](State())) {
case (Action.Toggle(), state) => state.copy(toggle = !state.toggle)
}
or similar.
Also, adding a method def flipToggle: State
to your case class State
this example becomes even cleaner:
store <- ff4s.Store.simple[F, State, Action](State())) {
case (Action.Toggle(), state) => state.flipToggle
}
ff4s/src/main/scala/ff4s/Store.scala
Outdated
.modify(state => update(action, state)) | ||
.flatMap(fu => | ||
Applicative[F] | ||
.unlessA(fu == Applicative[F].unit)(supervisor.supervise(fu)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make one call to unit
and internally cache. For internal code of a library, I bias towards avoiding allocations. In fact here I would write:
.flatMap { fu =>
if (fu == unit) unit
else supervisor.supervise(fu)
}
so the additional cost in the case of non-unit effect is 1 additional .equals
and 1 additional branch (which possibly a branch predictor would basically wind up hiding the cost of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand: if we know that unit
is a val
for cats.effect.IO
(and presumably any other reasonable implementation), then where is the allocation? Also, I think I prefer keeping unlessA
b/c it makes the intent clear and the cost is just one additional function call (hopefully inlined by the JIT).
case Action.SetTodoInput(what) => | ||
_.copy(todoInput = Some(what)) -> none | ||
case (Action.SetTodoInput(what), state) => | ||
state.copy(todoInput = Some(what)) -> F.unit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
many (most?) of the examples are of the simple
kind I mentioned above. I think having a helper on Store to support this would be really nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really great to me.
ff4s/src/main/scala/ff4s/Store.scala
Outdated
.modify(state => update(action, state)) | ||
.flatMap(fu => | ||
Applicative[F] | ||
.unlessA(fu == Applicative[F].unit)(supervisor.supervise(fu)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second argument is a call by name param which requires an allocation but it's probably not a big deal if you much prefer it.
@@ -75,12 +76,21 @@ sealed trait Store[F[_], State, Action] { | |||
|
|||
object Store { | |||
|
|||
/** A simple type of store where all actions are pure state updates. | |||
*/ | |||
def pure[F[_]: Concurrent, State, Action](init: State)( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great. I like the name too. Maybe it would be a a good idea to use it in one or two of your existing examples?
Thanks so much for this! |
No description provided.