Skip to content

react: Add new exercise #20

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

Merged
merged 10 commits into from
Jan 20, 2017
Merged

react: Add new exercise #20

merged 10 commits into from
Jan 20, 2017

Conversation

petertseng
Copy link
Member

@petertseng petertseng commented Jan 17, 2017

I highly recommend squashing these commits before merging, because the intermediary WIP commits do not necessarily build and are not reflective of a complete exercise.

Copy link
Member Author

@petertseng petertseng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a very ambitious choice for the sixth exercise to be added to this track, and it might have been too ambitious.

There are ideas I feel like I don't yet know how to express well in the language, so it may pay well to try porting some different exercises first, shelving this attempt.

I sent this PR so that I have a record of the work, I can comment it on where I had troubles, and maybe others will help. I'll see what happens.

variable Element currentValue_;
Element() newValue;

shared new single(Cell c, Element(Element) f) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here I had a hope that a call Reactor<Integer>.ComputeCell.single would be able to treat this Element(Element) as Integer(Integer), but it does not currently seem this is true.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it was not true, I was forced to give up making this generic. Now our reactor only acts on Integers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update: given Element satisfies Object was necessary.

shared new single(Cell c, Element(Element) f) {
c.addDependent(this);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not legal.

My alternatives are:

  • create a different function on Reactor that constructs a compute cell then adds it as a dependent of its dependencies. I was afraid it would be unidiomatic to expose this constructor-that-isn't-a-constructor, but perhaps it is not so bad.
  • do it a different way, do not let cells know about their dependencies (why do I do it this way? only because it's the way which I did it in other language)
  • force the caller to add the just-constructed ComputeCell as a dependent of its dependencies. I don't find this a reasonable interface.

newValue = () => f(c.currentValue);
currentValue_ = newValue();
}

shared new double(Cell c1, Cell c2, Element(Element, Element) f) {
c1.addDependent(this);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also illegal.

newValue = () => f(c.currentValue);
currentValue_ = newValue();
}

shared new double(Cell c1, Cell c2, Element(Element, Element) f) {
c1.addDependent(this);
c2.addDependent(this);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also illegal.

@petertseng
Copy link
Member Author

closing to prevent accidental merges, but thoughts still appreciated.

@petertseng petertseng closed this Jan 17, 2017
@petertseng petertseng reopened this Jan 17, 2017
@petertseng
Copy link
Member Author

Okay, guess I have to make it so that the reactor is in charge of constructing.

@petertseng petertseng force-pushed the react branch 2 times, most recently from 18780f4 to 1b90337 Compare January 17, 2017 03:35
Copy link
Member Author

@petertseng petertseng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a little unsure if I should keep all the value in the tests (lets students be a bit more flexible with types if they can find a way to) or explicitly specify types (to make sure the types are as we expect)


alias Element => Integer;

class Reactor() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this example solution, the reactor is absolutely useless; it does nothing. But I wanted to leave it in case a student's implementation truly needs it (for example, they store all their cells in an array and iterate over the array to propagate values)

shared actual Element currentValue => currentValue_;

variable Integer callbacksIssued = 0;
variable MutableMap<Integer, Callback> activeCallbacks = HashMap<Integer, Callback>();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ouch, a bit of repetition here.

@petertseng petertseng changed the title WIP: react exercise react: Add new exercise Jan 17, 2017
}
}

shared ComputeCell newComputeCell1(Cell c, Element(Element) f) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#20 (comment) explains why Reactor must be in charge of creating new compute cells - notice the addDependent call on the newly-created cell. If this was done in an ComputeCell initialiser, it will leak a reference to this, which is illegal.

return cell;
}

shared ComputeCell newComputeCell2(Cell c1, Cell c2, Element(Element, Element) f) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#20 (comment) explains why Reactor must be in charge of creating new compute cells - notice the addDependent call on the newly-created cell. If this was done in an ComputeCell initialiser, it will leak a reference to this, which is illegal.

same.

}
}

shared InputCell newInputCell(Element initialValue) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not strictly necessary to delegate the creation to a Reactor, but it is consistent with newComputeCell* functions, which are necessary because of #20 (comment).

}

test
void removingCallbackMultipleTimesDoesntInterfereWithOtherCallbacks() {
Copy link
Member Author

@petertseng petertseng Jan 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was supposed to be another test saying that callbacks can be removed - where did it go??????

(I'll add it back, just puzzled at how it could have disappeared from under my nose)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added back.

@petertseng
Copy link
Member Author

I'd like to go back to being able to say reactor.ComputeCell.single and reactor.ComputeCell.double and I think I have an idea (wrap another Cell in a ComputeCell). I'll see whether it works.

@petertseng
Copy link
Member Author

I'd like to go back to being able to say reactor.ComputeCell.single and reactor.ComputeCell.double and I think I have an idea

ah very interesting, it is at this very point where I run into:

source/react/ReactorTest.ceylon:22: error: left operand must be of summable type: 'Element' is not a subtype of 'Summable'
  value output = r.ComputeCell.single(input, (x) => x + 1);
                                                    ^
1 error

I wonder why this is. I may have to defer on this, merge it as it is now, and try again later.

@petertseng
Copy link
Member Author

Annotating the functions passed-in makes it work.

@petertseng
Copy link
Member Author

It pleases me more (since it seems more idiomatic Ceylon, though what would I know?) to provide this cleaner interface to students, though it complicates the implementation a bit. I will see if there is anything else that can be done.

Argh, we have to annotate types of all the compute functions now!!!
Inner inner;

shared new single(Cell c, Element(Element) f) extends Cell() {
// Since `c.addDependent(this)` is illegal, we have to create an inner cell.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the record, I understand why it's illegal (if you give someone access to your this before you've initialised, they can reference one of your uninitialised field, which is unsafe)

@petertseng
Copy link
Member Author

Final answer.

@petertseng petertseng merged commit de77cf2 into exercism:master Jan 20, 2017
@petertseng petertseng deleted the react branch January 20, 2017 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant