Skip to content

Commit

Permalink
feat: Add support for creating simple 2D animations
Browse files Browse the repository at this point in the history
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 GIF for ease of use by clients. The arguments are a frame count 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.

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.

Signed-off-by: George Thomas <[email protected]>
  • Loading branch information
georgefst committed Oct 30, 2023
1 parent a482844 commit 68fa211
Show file tree
Hide file tree
Showing 17 changed files with 591 additions and 19 deletions.
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@
# https://github.com/input-output-hk/haskell.nix/issues/1242
packages.mtl-compat.writeHieFiles = false;
packages.bytestring-builder.writeHieFiles = false;
packages.fail.writeHieFiles = false;
packages.diagrams.writeHieFiles = false;
}
{
#TODO This shouldn't be necessary - see the commented-out `build-tool-depends` in primer.cabal.
Expand Down
18 changes: 18 additions & 0 deletions primer-service/test/outputs/OpenAPI/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,24 @@
"contents"
],
"type": "object"
},
{
"properties": {
"contents": {
"type": "string"
},
"tag": {
"enum": [
"PrimAnimation"
],
"type": "string"
}
},
"required": [
"tag",
"contents"
],
"type": "object"
}
]
},
Expand Down
1 change: 1 addition & 0 deletions primer/gen/Primer/Gen/Core/Raw.hs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ genPrimCon =
_ = \case
PrimChar _ -> ()
PrimInt _ -> ()
PrimAnimation _ -> ()

genType :: ExprGen Type
genType =
Expand Down
3 changes: 2 additions & 1 deletion primer/gen/Primer/Gen/Core/Typed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ genChk ty = do
brs0 <- Gen.list (Range.linear 0 5) $ do
p <- pg
(p,) . CaseBranch (PatPrim p) [] <$> genChk ty
let brs = nubSortOn ((\case PrimInt n -> Left n; PrimChar c -> Right c) . fst) brs0
let brs = nubSortOn ((\case PrimInt n -> Left (Left n); PrimChar c -> Left (Right c); PrimAnimation b -> Right b) . fst) brs0
fb <- genChk ty
pure $ Case () e (snd <$> brs) (CaseFallback fb)

Expand Down Expand Up @@ -676,6 +676,7 @@ genPrimCon = catMaybes <$> sequence [whenInScope PrimChar 'a' genChar, whenInSco
_ = \case
PrimChar _ -> ()
PrimInt _ -> ()
PrimAnimation _ -> ()

-- We bias the distribution towards a small set, to make it more likely we
-- generate name clashes on occasion
Expand Down
4 changes: 4 additions & 0 deletions primer/primer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ library
Primer.App.Utils
Primer.Builtins
Primer.Builtins.DSL
Primer.Builtins.Picture
Primer.Core
Primer.Core.DSL
Primer.Core.Meta
Expand Down Expand Up @@ -109,8 +110,11 @@ library
, aeson >=2.0 && <2.2
, assoc ^>=1.1
, base >=4.12 && <4.19
, base64-bytestring ^>=1.2.1
, containers >=0.6.0.1 && <0.7.0
, deriving-aeson >=0.2 && <0.3.0
, diagrams-lib ^>=1.4.6
, diagrams-rasterific ^>=1.4.2
, exceptions >=0.10.4 && <0.11.0
, extra >=1.7.10 && <1.8.0
, generic-optics >=2.0 && <2.3.0
Expand Down
1 change: 1 addition & 0 deletions primer/src/Primer/Builtins/Picture.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module Primer.Builtins.Picture () where
2 changes: 2 additions & 0 deletions primer/src/Primer/Core/Meta.hs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ instance HasMetadata (Meta a) where
data PrimCon
= PrimChar Char
| PrimInt Integer
| -- | Contains a base-64 encoding of an animated GIF.
PrimAnimation Text
deriving stock (Eq, Show, Read, Data, Generic)
deriving (FromJSON, ToJSON) via PrimerJSON PrimCon
deriving anyclass (NFData)
Expand Down
5 changes: 3 additions & 2 deletions primer/src/Primer/Module.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import Primer.JSON (
ToJSON,
)
import Primer.Name (Name)
import Primer.Primitives (allPrimTypeDefs, primDefName, primitiveModuleName)
import Primer.Primitives (allPrimTypeDefs, pictureDef, primDefName, primitiveModuleName, tPicture)
import Primer.TypeDef (TypeDef (..), TypeDefMap, forgetTypeDefMetadata, generateTypeDefIDs)

data Module = Module
Expand Down Expand Up @@ -133,10 +133,11 @@ nextModuleID m =
primitiveModule :: MonadFresh ID m => m Module
primitiveModule = do
allPrimTypeDefs' <- traverse (generateTypeDefIDs . TypeDefPrim) allPrimTypeDefs
pictureDef' <- generateTypeDefIDs $ TypeDefAST pictureDef
pure
Module
{ moduleName = primitiveModuleName
, moduleTypes = M.mapKeys baseName allPrimTypeDefs'
, moduleTypes = M.mapKeys baseName allPrimTypeDefs' <> M.fromList [(baseName tPicture, pictureDef')]
, moduleDefs = M.fromList $ [(primDefName def, DefPrim def) | def <- enumerate]
}

Expand Down
1 change: 1 addition & 0 deletions primer/src/Primer/Pretty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ prettyExpr opts = \case
prim = \case
PrimChar c -> "Char" <+> pretty @Text (show c)
PrimInt n -> "Int" <+> pretty @Text (show n)
PrimAnimation n -> pretty @Text (show n)
typeann e t = brac Round Yellow (pE e) <+> col Yellow "::" <> line <> brac Round Yellow (pT t)

-- When grouped: " x "
Expand Down
Loading

0 comments on commit 68fa211

Please sign in to comment.