Skip to content

Instantly share code, notes, and snippets.

@mchav
Created December 14, 2025 08:30
Show Gist options
  • Select an option

  • Save mchav/ba7d1271144443102150ce1a924198b9 to your computer and use it in GitHub Desktop.

Select an option

Save mchav/ba7d1271144443102150ce1a924198b9 to your computer and use it in GitHub Desktop.
-- Sort of like Java's step builder pattern.
-- https://medium.com/@castigliego/step-builder-pattern-3bcac4eaf9e8
-- A type that encodes that Y doesn't exist.
data NeedY
-- A type that encodes that a chart is ready.
data Ready
data Plot (s :: *) where
Scatter ::
{ pX :: SomeNumExpr
, pY :: Maybe SomeNumExpr
, pColour :: Maybe SomeCatExpr
, pHudEdits :: Endo HudOptions
, pMarkupEdits :: Endo MarkupOptions
, pForceRange :: Maybe ((Double, Double), (Double, Double))
} -> Plot s
data SomeNumExpr = forall a. (Real a) => SomeNumExpr (Expr a)
data SomeCatExpr = forall a. (Enum a) => SomeCatExpr (Expr a)
scatter :: Real a => Expr a -> Plot NeedY
scatter x = Scatter (SomeNumExpr x) Nothing Nothing mempty mempty Nothing
against :: Real b => Expr b -> Plot NeedY -> Plot Ready
against y p = p { pY = Just (SomeNumExpr y) }
colourWith :: Enum c => Expr c -> Plot Ready -> Plot Ready
colourWith c p = p { pColour = Just (SomeCatExpr c) }
setTitle :: T.Text -> Plot s -> Plot s
setTitle t p =
p { pHudEdits = pHudEdits p <> Endo (\hud ->
hud { titles = [Priority 0 (defaultTitleOptions t)] } ) }
axisTitles :: T.Text -> T.Text -> Plot s -> Plot s
axisTitles xlab ylab p =
p { pHudEdits = pHudEdits p <> Endo (\hud ->
hud { titles = ...
}) }
axisTicks :: [Double] -> [Double] -> Plot s -> Plot s
axisTicks xt yt p =
p { pHudEdits = pHudEdits p <> Endo (\hud ->
hud { axes = ...
}) }
dimensions :: (Double, Double) -> (Double, Double) -> Plot s -> Plot s
dimensions xr yr p = p { pForceRange = Just (xr, yr) }
data PlotError
= MissingY
| ColumnEvalError T.Text
deriving (Show)
render :: Plot Ready -> DataFrame -> Either PlotError ByteString
render p df = do
-- we can finally run the expressions through the dataframe to get the x and y points.
ptsByColour <- buildPoints p df
let charts = ... -- build chart from points
chartOpts =
mempty
{ chartTree = named "scatter" charts'
-- endomorphism means we can combine all the different HUD options
-- seems like the right abstraction for composing these.
, hudOptions = appEndo (pHudEdits p) defaultHudOptions
, markupOptions = appEndo (pMarkupEdits p) defaultMarkupOptions
}
pure (encodeChartOptions chartOpts)
-- somwhere in the call point
let svgBytes =
( Plt.scatter penguinHeight
|> Plt.against penguinWeight
|> Plt.colourWith penguinSpecies
|> Plt.setTitle "my penguine chart"
|> Plt.axisTitles "Penguin Height" "Penguin Weight"
|> Plt.axisTicks [1, 5, 12] [123, 349, 400]
|> Plt.dimensions (0, 15) (100, 500)
|> Plt.render
) df
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment