Skip to content
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

Port "available actions" code from Vonnegut frontend #159

Merged
23 commits merged into from
Oct 12, 2021

Conversation

georgefst
Copy link
Contributor

@georgefst georgefst commented Oct 6, 2021

This is the first significant step towards #60.

This is in preparation for them being used elsewhere.
These are equivalent to definitions from our old frontend, and will shortly be used by some code we port from there.
This is a straightforward translation of `Vonnegut.AST.unfoldFun` from our old frontend, using a pair instead of an anonymous record.
This makes way for some upcoming changes which would otherwise cause module dependency cycles, since `Primer.App` is near the top of the graph.
There are only trivial changes:
- We replace `top` with the Haskell equivalent `maxBound`.
- Changes to the module header.

See:
```
git remote add vonnegut https://github.com/hackworthltd/vonnegut
git fetch vonnegut georgefst/frontend-refactors-for-backend-port
git diff vonnegut/georgefst/frontend-refactors-for-backend-port:frontend/src/Vonnegut/ActionPriorities.purs primer/src/Primer/Action/Priorities.hs
```
Changes are mostly trivial - see:
```
git remote add vonnegut https://github.com/hackworthltd/vonnegut
git fetch vonnegut georgefst/frontend-refactors-for-backend-port
git diff vonnegut/georgefst/frontend-refactors-for-backend-port:frontend/src/Vonnegut/Actions.purs primer/src/Primer/Action/Available.hs
```

The only subtle change is that `exprMeta` and `exprMetaTraversal` are renamed to `exprMetaLens` and `exprMeta` respectively (and similarly `typeMeta` etc.), due to a mismatch in naming conventions between the old frontend and backend.
This was unused in the old frontend before porting. It's just that the PureScript compiler doesn't warn about such things.
We use this to avoid passing the same `toProgAction` function around everywhere, instead simply mapping over the structure at the end.
These were unused in the old frontend before porting, but we wouldn't have noticed, since the PureScript compiler doesn't warn about such things.
These are all type/kind arguments to an `ActionSpec`, used by `realise` to match `ActionSpec`'s phantom parameter.
@georgefst georgefst force-pushed the georgefst/frontend-actions-port branch from 16fcd92 to 0d8e966 Compare October 7, 2021 14:10
@@ -1 +1,6 @@
{ roots = [ "^Main.main$" ], type-class-roots = False }
let
-- TODO remove these once this code (recently ported from old frontend) is exercised
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be tracked in an issue.

primer/src/Primer/Core.hs Outdated Show resolved Hide resolved
primer/src/Primer/Action.hs Outdated Show resolved Hide resolved
where
avoid :: [Text]
avoid = Map.elems $ map (unName . defName) defs

Copy link
Contributor Author

@georgefst georgefst Oct 7, 2021

Choose a reason for hiding this comment

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

This and Level possibly belong somewhere more general than Primer.Action. But this seemed a convenient place to put them for now.

primer/src/Primer/Action.hs Outdated Show resolved Hide resolved
@georgefst
Copy link
Contributor Author

georgefst commented Oct 7, 2021

One recurring problem here is a lack of clarity about which module to put things in, especially when it comes to avoiding dependency cycles.

In general, perhaps we should clarify the scope and responsibilities of each module. Having a brief top-level Haddock comment for each module in the core library would be a good start.

@georgefst georgefst requested a review from a team October 7, 2021 17:02
primer/src/Primer/Core.hs Outdated Show resolved Hide resolved
primer/src/Primer/Core.hs Outdated Show resolved Hide resolved
Comment on lines 299 to 305
-- Type parameter 'p' is a ghost parameter that provides some type
-- safety, to prevent the accidental mixing of actions for different
-- types (e.g., to prevent the mixing of actions for 'Expr's and
-- 'Type's). The argument of type 'p' in the type signature is not
-- used other than to provide proof that you have a value of type 'p'.
type ActionSpec p a =
p -> ([Action] -> [ProgAction]) -> Meta a -> OfferedAction
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it is worth taking advantage of GADTs here?

I don't know how this will affect automatic deriving of serialisation code though (if that is a thing we are going to do). Though I expect we will not serialise ActionSpecs, but only OfferedActions?

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if it is worth taking advantage of GADTs here?

Yeah this was a not-very-satisfying thing I did in PureScript to try to get get better type-safety. Now that we're on the Haskell side there's no reason not to use its full power.

I don't know how this will affect automatic deriving of serialisation code though (if that is a thing we are going to do). Though I expect we will not serialise ActionSpecs, but only OfferedActions?

Agreed, and possibly even something simpler than that. Pretty much all we need to send to the frontend is what to display on the button, and an identifier of some kind to send back to the backend to say, "Here's the action the student selected."

Copy link
Contributor Author

@georgefst georgefst Oct 12, 2021

Choose a reason for hiding this comment

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

I'm not sure exactly what you're proposing if you mean turning ActionSpec in to a GADT?

What I could see us wanting is for Action to be a GADT, with each constructor carrying a tag which tells us whether it applies to a type or expression. Then, in theory, we could write something like applyAction :: a -> Action a -> m (Either ActionError a). But this is difficult. I think it's best that I open a separate issue and link to the branch in which I have a very messy WIP.

Copy link
Member

Choose a reason for hiding this comment

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

Sure, let's punt this until later.

Copy link
Contributor

@brprice brprice left a comment

Choose a reason for hiding this comment

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

Thanks for taking on this large bit of work! Before we merge, I would like some clarity around how we expect to integrate this into the API. In particular I have some comments at #159 (comment).

Other than this I have only small comments, left above.

Existing `True` or `False` values have been set to `Destructive` or `Primary` respectively.
This untangles our module dependency tree a little, in particular removing the dependency of `Question` on `Action`. In the next commit, we will introduce the inverse dependency.

Requires generalising the type signatures of `mkFreshName` and `mkFreshNameTy`. All this does is remove some redundant constraints coming from the `ActionM` synonym. Other than this, the contents of `Fresh.hs` (except the module header) come verbatim from `Action.hs`
This type was ported from PureScript, which does not generate field selector functions, so it was safe in that context.
@georgefst
Copy link
Contributor Author

bors merge

@ghost
Copy link

ghost commented Oct 12, 2021

@ghost ghost merged commit fc58368 into main Oct 12, 2021
@ghost ghost deleted the georgefst/frontend-actions-port branch October 12, 2021 15:09
@georgefst
Copy link
Contributor Author

My only regret is that we didn't do some of these things in the original implementation: in retrospect, it seems like a bad idea to have moved so much "smarts" into the frontend, regardless of what language the frontend was written in, especially given our intention to open source the Haskell implementation and make it generally useful without needing our particular frontend implementation. Ultimately, that was a poor decision on my part to split things up the way we did.

Historical notes, for the curious:

We calculated available actions on the frontend from essentially the very beginning. It was pretty simple, and a frontend/backend split at that point would probably have seemed unnecessary over-engineering.

We missed an opportunity to move the logic to the backend when we separated it from rendering code a few weeks later, creating Actions.purs. But at that point, it was clearly more expedient to keep it on the frontend.

Actions.purs gradually expanded in complexity. It ended up being the only significant chunk of frontend code which we decided was worth porting to the backend. We did so in #159. It was then heavily re-written in this PR.

@georgefst
Copy link
Contributor Author

My only regret is that we didn't do some of these things in the original implementation: in retrospect, it seems like a bad idea to have moved so much "smarts" into the frontend, regardless of what language the frontend was written in, especially given our intention to open source the Haskell implementation and make it generally useful without needing our particular frontend implementation. Ultimately, that was a poor decision on my part to split things up the way we did.

Historical notes, for the curious:

We calculated available actions on the frontend from essentially the very beginning. It was pretty simple, and a frontend/backend split at that point would probably have seemed unnecessary over-engineering.

We missed an opportunity to move the logic to the backend when we separated it from rendering code a few weeks later, creating Actions.purs. But at that point, it was clearly more expedient to keep it on the frontend.

Actions.purs gradually expanded in complexity. It ended up being the only significant chunk of frontend code which we decided was worth porting to the backend. We did so in #159. It was then heavily re-written in this PR.

Whoops, I put this on the wrong thread (juggling too many open tabs with similar names...): #762 (comment),

This pull request was closed.
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.

3 participants