-
Notifications
You must be signed in to change notification settings - Fork 1
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
Conversation
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.
16fcd92
to
0d8e966
Compare
@@ -1 +1,6 @@ | |||
{ roots = [ "^Main.main$" ], type-class-roots = False } | |||
let | |||
-- TODO remove these once this code (recently ported from old frontend) is exercised |
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.
Should be tracked in an issue.
where | ||
avoid :: [Text] | ||
avoid = Map.elems $ map (unName . defName) defs | ||
|
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 and Level
possibly belong somewhere more general than Primer.Action
. But this seemed a convenient place to put them for now.
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. |
-- 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 |
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 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 ActionSpec
s, but only OfferedAction
s?
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 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
ActionSpec
s, but onlyOfferedAction
s?
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."
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 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.
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.
Sure, let's punt this until later.
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.
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.
bors merge |
Build succeeded: |
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
|
Whoops, I put this on the wrong thread (juggling too many open tabs with similar names...): #762 (comment), |
This is the first significant step towards #60.