synthExpr defs gam pol (Binop s _ op e1 e2) = do
    (t1, gam1, subst1, elaboratedL) <- synthExpr defs gam pol e1
    (t2, gam2, subst2, elaboratedR) <- synthExpr defs gam pol e2
    -- Look through the list of operators (of which there might be
    -- multiple matching operators)
    returnType <-
      selectFirstByType t1 t2
      . NonEmpty.toList
      . Primitives.binaryOperators
      $ op
    gamOut <- ctxtPlus s gam1 gam2
    subst <- combineSubstitutions s subst1 subst2
    let elaborated = Binop s returnType op elaboratedL elaboratedR
    return (returnType, gamOut, subst, elaborated)

  where
    -- No matching type were found (meaning there is a type error)
    selectFirstByType t1 t2 [] = throw FailedOperatorResolution
        { errLoc = s, errOp = op, errTy = t1 .-> t2 .-> var "..." }

    selectFirstByType t1 t2 ((FunTy opt1 (FunTy opt2 resultTy)):ops) = do
      -- Attempt to use this typing
      (result, local) <- peekChecker $ do
         (eq1, _, _) <- equalTypes s t1 opt1
         (eq2, _, _) <- equalTypes s t2 opt2
         return (eq1 && eq2)
      -- If successful then return this local computation
      case result of
        Right True -> local >> return resultTy
        _         -> selectFirstByType t1 t2 ops

    selectFirstByType t1 t2 (_:ops) = selectFirstByType t1 t2 ops


-- Abstraction, can only synthesise the types of
-- lambda in Church style (explicit type)
synthExpr defs gam pol (Binop s _ op e1 e2) = do
    (t1, gam1, subst1, elaboratedL) <- synthExpr defs gam pol e1
    (t2, gam2, subst2, elaboratedR) <- synthExpr defs gam pol e2
    -- Look through the list of operators (of which there might be
    -- multiple matching operators)
    returnType <-
      selectFirstByType t1 t2
      . NonEmpty.toList
      . Primitives.binaryOperators
      $ op
    gamOut <- ctxtPlus s gam1 gam2
    subst <- combineSubstitutions s subst1 subst2
    let elaborated = Binop s returnType op elaboratedL elaboratedR
    return (returnType, gamOut, subst, elaborated)

  where
    -- No matching type were found (meaning there is a type error)
    selectFirstByType t1 t2 [] = throw FailedOperatorResolution
        { errLoc = s, errOp = op, errTy = t1 .-> t2 .-> var "..." }

    selectFirstByType t1 t2 ((FunTy opt1 (FunTy opt2 resultTy)):ops) = do
      -- Attempt to use this typing
      (result, local) <- peekChecker $ do
         (eq1, _, _) <- equalTypes s t1 opt1
         (eq2, _, _) <- equalTypes s t2 opt2
         return (eq1 && eq2)
      -- If successful then return this local computation
      case result of
        Right True -> local >> return resultTy
        _         -> selectFirstByType t1 t2 ops

    selectFirstByType t1 t2 (_:ops) = selectFirstByType t1 t2 ops


-- Abstraction, can only synthesise the types of
-- lambda in Church style (explicit type)
synthExpr defs gam pol (Binop s _ op e1 e2) = do
    (t1, gam1, subst1, elaboratedL) <- synthExpr defs gam pol e1
    (t2, gam2, subst2, elaboratedR) <- synthExpr defs gam pol e2
    -- Look through the list of operators (of which there might be
    -- multiple matching operators)
    returnType <-
      selectFirstByType t1 t2
      . NonEmpty.toList
      . Primitives.binaryOperators
      $ op
    gamOut <- ctxtPlus s gam1 gam2
    subst <- combineSubstitutions s subst1 subst2
    let elaborated = Binop s returnType op elaboratedL elaboratedR
    return (returnType, gamOut, subst, elaborated)

  where
    -- No matching type were found (meaning there is a type error)
    selectFirstByType t1 t2 [] = throw FailedOperatorResolution
        { errLoc = s, errOp = op, errTy = t1 .-> t2 .-> var "..." }

    selectFirstByType t1 t2 ((FunTy opt1 (FunTy opt2 resultTy)):ops) = do
      -- Attempt to use this typing
      (result, local) <- peekChecker $ do
         (eq1, _, _) <- equalTypes s t1 opt1
         (eq2, _, _) <- equalTypes s t2 opt2
         return (eq1 && eq2)
      -- If successful then return this local computation
      case result of
        Right True -> local >> return resultTy
        _         -> selectFirstByType t1 t2 ops

    selectFirstByType t1 t2 (_:ops) = selectFirstByType t1 t2 ops


-- Abstraction, can only synthesise the types of
-- lambda in Church style (explicit type)