stock-0.1.0.0: plugin/Stock/Bounded.hs
{-# LANGUAGE BlockArguments #-}
-- @head@/@last cons@ are guarded by the caller's
-- enum-or-single-constructor contract and the class always having its methods.
{-# OPTIONS_GHC -Wno-x-partial -Wno-unused-imports #-}
-- | @Bounded@ via the SOP-EDSL. @minBound@\/@maxBound@ are values, so this is
-- pure 'injectSOP' (no @matchSOP@): an enumeration injects its first\/last
-- (nullary) constructor; a single-constructor product injects that constructor
-- with each field set to its own @minBound@\/@maxBound@ ('pureFields' +
-- 'field'). A clean demonstration that the SDK alone expresses a real
-- synthesizer — this module needs nothing from the plugin substrate.
module Stock.Bounded (boundedDeriver) where
import GHC.Plugins
import GHC.Core.Class (classMethods)
import Stock.Derive
-- | The caller guarantees the type is an enumeration or a single constructor
-- (GHC's @Bounded@ deriving has the same restriction).
boundedDeriver :: Deriver
boundedDeriver = Deriver \cls dt -> do
let cons = dtCons dt
minSel = classMethod "minBound" cls
maxSel = classMethod "maxBound" cls
bound sel ft d = mkApps (Var sel) [Type ft, d]
if all (null . conFields) cons
then -- enumeration: first / last constructor
pure (classDict cls (dtVia dt) [ injectSOP dt (head cons) []
, injectSOP dt (last cons) [] ])
else do -- single product: each field at its bound
let con = productCon dt
fds <- pureFields (\ft -> do d <- field cls ft; pure (ft, d)) con
pure (classDict cls (dtVia dt)
[ injectSOP dt con [ bound minSel ft d | (ft, d) <- fds ]
, injectSOP dt con [ bound maxSel ft d | (ft, d) <- fds ] ])