-
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
Animations #1164
Animations #1164
Conversation
b18ffca
to
1a943c7
Compare
Is the goal of this PR to merge it into |
078536a
to
0b1e1c8
Compare
fa26026
to
ad9148d
Compare
b2ec4fe
to
c7e473f
Compare
To merge. This code is now pretty solid, so I see no harm in keeping it around, even if some of it will be rewritten as we change our implementation or the student-facing API. The one unsatisfying part is that we're extremely limited in what sort of lambdas we can pass to our |
0360f3f
to
24d070b
Compare
24d070b
to
5d52e9c
Compare
Could you point to where you expanded on this? (ETA: the explanation will be expanded on in the linked #1169 itself) |
Where it says "(full explanation to be filled in imminently)"! Sorry, I'll nudge you when that's done. |
primer/src/Primer/Primitives.hs
Outdated
Animate -> | ||
( | ||
[ c tInt -- Loop time, in seconds. | ||
, c tInt `f` c tPicture -- A function from frame number to output. | ||
] | ||
, c tAnimation | ||
) |
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.
To check my understanding, we have
- (from the commit message) a fixed 10fps
- an argument for loop length
- thus
Animate n p
will denote a gif of10*n
frames of0.1
s duration each, for a total ofn
seconds, and will callp
with arguments0,1,...,10*n-1
or maybe1,2,...,10*n
to compute each frame? - Also, it is set to loop forever
Maybe elaborate the comments here?
I believe you said at our last meet-up that a Primer primitive for |
primer/src/Primer/Primitives.hs
Outdated
Animate -> case args of | ||
-- Since we can only translate a `Picture` expression to an image once it is in normal form, | ||
-- this guard will only pass when `picture` has no free variables other than `time`. | ||
[PrimCon () (PrimInt duration), Lam () time picture] | ||
| Just frames <- traverse diagramAtTime [0 .. duration * 100 `div` frameLength] -> |
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 only": not true in single-step user-directed eval mode (though this is not exposed in the frontend currently)! I don't think this particularly matters, but maybe less confusing if you want to rephrase the comment to say something implying "we only support" rather than "we can only" (I initially read the comment as saying "it is only possible to ask to translate a picture when it is in normal form", rather than "you can ask, but we will just get stuck, if the argument is not in nf"). (This "only support" treatment is consistent with, say, primitive integers).
primer/src/Primer/Primitives.hs
Outdated
-- Since we can only translate a `Picture` expression to an image once it is in normal form, | ||
-- this guard will only pass when `picture` has no free variables other than `time`. | ||
[PrimCon () (PrimInt duration), Lam () time picture] | ||
| Just frames <- traverse diagramAtTime [0 .. duration * 100 `div` frameLength] -> |
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.
(Pedantic off-by-one): I think this length of frame indices is one too long (not that a human would be able to visually tell unless the construction function was wild)!
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.
Good point. This made sense initially when I was thinking in terms of seconds rather than frame counts, and didn't anticipate looping. In which case we would dwell on the final frame. I changed my mind on those due to lack of floats and playback controls, respectively.
I've added a link back to this conversation from #1173, since we may well want to revert this if we do drop looping.
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.
(Untested) Code looks good to me, modulo some minor comments.
You say in a commit message
Two points here:
|
85d1f94
to
9a258af
Compare
We have a new ADT, `Picture`, representing 2D images, and a primitive function `animate : Integer → (Integer → Picture) → Animation`, where `Animation` is a new primitive type, currently implemented as a base-64 encoding of a GIF for ease of use by clients. The arguments are a duration, in seconds, and a description of how to render each frame. This is inspired by the API of pure functional animation libraries such as Haskell's Gloss. We opt for keeping the API simple for now rather than providing full flexibility, so animations are always 10 frames/s on a low-resolution widescreen grid, looping forever (see `Values which are hardcoded`... comment). Both implementation and student-facing API are really just a proof-of-concept for exploring the use of Primer for effectful programming. Though much of what is added here (`Picture` ADT, new primitives etc.) is likely to remain useful for any future work in this direction. The one major current limitation is around the kinds of functions which we can pass as the second argument to `animate` without the evaluator getting stuck (`simple substitution hack`... comment). This is a high-priority task which will require some reworking of the evaluator, the details of which are as yet unclear. Using GIFs with `tasty-golden` for testing mostly works well, except that it has some issues displaying the `ByteString` in the terminal when the output differs from what is expected (outputs `Exception: Cannot decode byte`, which seems to be down to UnkindPartition/tasty-golden#51). But this is fine, since what we'd want to do in such a situation is to just pass `--accept` and compare the old and new GIF as rendered. Signed-off-by: George Thomas <[email protected]>
Note that we use `DisposalRestoreBackground` in encoding the GIF, whereas previously we indirectly used `diagrams-rasterific`'s default, `DisposalAny`. While that worked with the default black background, retaining it now would mean that we would see previous frames remaining in the background rather than disappearing. Signed-off-by: George Thomas <[email protected]>
9a258af
to
66c14ec
Compare
primer/src/Primer/Typecheck.hs
Outdated
unless (tc == tInt || tc == tChar) $ throwError' $ InternalError $ "Unknown primitive type: " <> show tc | ||
unless (tc == tInt || tc == tChar || tc == tAnimation) $ throwError' $ InternalError $ "Unknown primitive type: " <> show tc | ||
let f b = case caseBranchName b of | ||
PatCon _ -> Nothing | ||
PatPrim pc -> case pc 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.
It might be nice here to link to a note giving an overview of the Animation
type. In particular to explain why we do not allow pattern matching on it
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 added this line yesterday to fix a test failure*, without initially looking at the context, which I was going to ask you about. But anyway, I've worked it out now, and realised the logic was actually wrong, and have now fixed this in 76d51cc.
I've also rewritten the whole passage in a form which I consider much easier to follow, and which would extend more easily to adding further primitives.
* This can be hit when cabal run primer-test -- -p "tcWholeProg idempotent" --hedgehog-replay "375/638:oAbA2d Seed 3320906864796586236 8917501983448077275"
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 refactoring this, the new version is indeed much clearer
This is mentioned several times in #1173. Essentially, all mentions of integer in this PR should be replaced with a real number representation, freeing students from thinking in terms of discrete time steps and drawing on fixed grid lines. |
Turns out I pushed the wrong branch after a rebase, which explains the missing tests, the empty module, and the negations on I've now applied all suggestions, and filled in #1169. I'm happy to merge this if there are no further objections. |
Signed-off-by: George Thomas <[email protected]>
ce07999
to
3897c97
Compare
This is a proof-of-concept, but the implementation is solid enough to merge. Follow-up work is tracked in #1173.
(note to self - if any significant changes are made here due to reviews, #1173 will probably need to be modified accordingly)