synthExpr defs gam pol (LetDiamond s _ p optionalTySig e1 e2) = do
  (sig, gam1, subst1, elaborated1) <- synthExpr defs gam pol e1

  -- Check that a graded possibility type was inferred
  (ef1, ty1) <- case sig of
      Diamond ef1 ty1 -> return (ef1, ty1)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  -- Type body of the let...
  -- ...in the context of the binders from the pattern
  (binders, _, substP, elaboratedP, _)  <- ctxtFromTypedPattern s ty1 p NotFull
  pIrrefutable <- isIrrefutable s ty1 p
  unless pIrrefutable $ throw RefutablePatternError{ errLoc = s, errPat = p }
  (tau, gam2, subst2, elaborated2) <- synthExpr defs (binders <> gam) pol e2

  -- Check that a graded possibility type was inferred
  (ef2, ty2) <- case tau of
      Diamond ef2 ty2 -> return (ef2, ty2)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  optionalSigEquality s optionalTySig ty1

  -- Check that usage matches the binding grades/linearity
  -- (performs the linearity check)
  ctxtEquals s (gam2 `intersectCtxts` binders) binders

  gamNew <- ctxtPlus s (gam2 `subtractCtxt` binders) gam1

  (efTy, u) <- twoEqualEffectTypes s ef1 ef2
  -- Multiply the effects
  ef <- effectMult s efTy ef1 ef2
  let t = Diamond ef ty2

  subst <- combineManySubstitutions s [substP, subst1, subst2, u]
  -- Synth subst
  t' <- substitute substP t

  let elaborated = LetDiamond s t elaboratedP optionalTySig elaborated1 elaborated2
  return (t, gamNew, subst, elaborated)

-- Variables
synthExpr defs gam pol (LetDiamond s _ p optionalTySig e1 e2) = do
  (sig, gam1, subst1, elaborated1) <- synthExpr defs gam pol e1

  -- Check that a graded possibility type was inferred
  (ef1, ty1) <- case sig of
      Diamond ef1 ty1 -> return (ef1, ty1)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  -- Type body of the let...
  -- ...in the context of the binders from the pattern
  (binders, _, substP, elaboratedP, _)  <- ctxtFromTypedPattern s ty1 p NotFull
  pIrrefutable <- isIrrefutable s ty1 p
  unless pIrrefutable $ throw RefutablePatternError{ errLoc = s, errPat = p }
  (tau, gam2, subst2, elaborated2) <- synthExpr defs (binders <> gam) pol e2

  -- Check that a graded possibility type was inferred
  (ef2, ty2) <- case tau of
      Diamond ef2 ty2 -> return (ef2, ty2)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  optionalSigEquality s optionalTySig ty1

  -- Check that usage matches the binding grades/linearity
  -- (performs the linearity check)
  ctxtEquals s (gam2 `intersectCtxts` binders) binders

  gamNew <- ctxtPlus s (gam2 `subtractCtxt` binders) gam1

  (efTy, u) <- twoEqualEffectTypes s ef1 ef2
  -- Multiply the effects
  ef <- effectMult s efTy ef1 ef2
  let t = Diamond ef ty2

  subst <- combineManySubstitutions s [substP, subst1, subst2, u]
  -- Synth subst
  t' <- substitute substP t

  let elaborated = LetDiamond s t elaboratedP optionalTySig elaborated1 elaborated2
  return (t, gamNew, subst, elaborated)

-- Variables
synthExpr defs gam pol (LetDiamond s _ p optionalTySig e1 e2) = do
  (sig, gam1, subst1, elaborated1) <- synthExpr defs gam pol e1

  -- Check that a graded possibility type was inferred
  (ef1, ty1) <- case sig of
      Diamond ef1 ty1 -> return (ef1, ty1)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  -- Type body of the let...
  -- ...in the context of the binders from the pattern
  (binders, _, substP, elaboratedP, _)  <- ctxtFromTypedPattern s ty1 p NotFull
  pIrrefutable <- isIrrefutable s ty1 p
  unless pIrrefutable $ throw RefutablePatternError{ errLoc = s, errPat = p }
  (tau, gam2, subst2, elaborated2) <- synthExpr defs (binders <> gam) pol e2

  -- Check that a graded possibility type was inferred
  (ef2, ty2) <- case tau of
      Diamond ef2 ty2 -> return (ef2, ty2)
      t -> throw ExpectedEffectType{ errLoc = s, errTy = t }

  optionalSigEquality s optionalTySig ty1

  -- Check that usage matches the binding grades/linearity
  -- (performs the linearity check)
  ctxtEquals s (gam2 `intersectCtxts` binders) binders

  gamNew <- ctxtPlus s (gam2 `subtractCtxt` binders) gam1

  (efTy, u) <- twoEqualEffectTypes s ef1 ef2
  -- Multiply the effects
  ef <- effectMult s efTy ef1 ef2
  let t = Diamond ef ty2

  subst <- combineManySubstitutions s [substP, subst1, subst2, u]
  -- Synth subst
  t' <- substitute substP t

  let elaborated = LetDiamond s t elaboratedP optionalTySig elaborated1 elaborated2
  return (t, gamNew, subst, elaborated)

-- Variables