refactor: never implicitly ignore monadic results

Also change `do e; f` to desugar to `e *> f` so that it is affected as well
This commit is contained in:
Sebastian Ullrich 2020-04-21 13:13:01 +02:00 committed by Leonardo de Moura
parent aa4fc12dff
commit 8f67db0101
35 changed files with 62 additions and 63 deletions

View file

@ -19,19 +19,19 @@ class HasSeq (f : Type u → Type v) : Type (max (u+1) v) :=
infixl <*> := HasSeq.seq
class HasSeqLeft (f : Type u → Type v) : Type (max (u+1) v) :=
(seqLeft : ∀ {α β : Type u}, f α → f β → f α)
(seqLeft : ∀ {α : Type u}, f α → f PUnit → f α)
infixl <* := HasSeqLeft.seqLeft
class HasSeqRight (f : Type u → Type v) : Type (max (u+1) v) :=
(seqRight : ∀ {α β : Type u}, f α → f β → f β)
(seqRight : ∀ {β : Type u}, f PUnit → f β → f β)
infixr *> := HasSeqRight.seqRight
class Applicative (f : Type u → Type v) extends Functor f, HasPure f, HasSeq f, HasSeqLeft f, HasSeqRight f :=
(map := fun _ _ x y => pure x <*> y)
(seqLeft := fun α β a b => const β <$> a <*> b)
(seqRight := fun α β a b => const α id <$> a <*> b)
(seqLeft := fun α a b => const _ <$> a <*> b)
(seqRight := fun β a b => const _ id <$> a <*> b)
@[macroInline]
def when {m : Type → Type u} [Applicative m] (c : Prop) [h : Decidable c] (t : m Unit) : m Unit :=

View file

@ -102,7 +102,7 @@ fun s => match x s with
| Result.ok a s => Result.ok (f a) s
| Result.error e s => Result.error e s
@[inline] protected def seqRight (x : EStateM ε σ α) (y : EStateM ε σ β) : EStateM ε σ β :=
@[inline] protected def seqRight (x : EStateM ε σ PUnit) (y : EStateM ε σ β) : EStateM ε σ β :=
fun s => match x s with
| Result.ok _ s => y s
| Result.error e s => Result.error e s

View file

@ -181,7 +181,7 @@ instance (ε m out) [MonadRun out m] : MonadRun (fun α => out (Except ε α)) (
⟨fun α => run⟩
/-- Execute `x` and then execute `finalizer` even if `x` threw an exception -/
@[inline] def finally {ε α β : Type u} {m : Type u → Type v} [Monad m] [MonadExcept ε m] (x : m α) (finalizer : m β) : m α :=
@[inline] def finally {ε α : Type u} {m : Type u → Type v} [Monad m] [MonadExcept ε m] (x : m α) (finalizer : m PUnit) : m α :=
catch
(do a ← x; finalizer; pure a)
(fun e => do finalizer; throw e)

View file

@ -22,3 +22,8 @@ infixr `$>` := Functor.mapConstRev
@[reducible] def Functor.mapRev {f : Type u → Type v} [Functor f] {α β : Type u} : f α → (α → β) → f β :=
fun a f => f <$> a
infixl `<&>` := Functor.mapRev
def Functor.discard {f : Type u → Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
PUnit.unit <$ x
export Functor (discard)

View file

@ -25,8 +25,8 @@ infixr `>=>` := mcomp
class Monad (m : Type u → Type v) extends Applicative m, HasBind m : Type (max (u+1) v) :=
(map := fun α β f x => x >>= pure ∘ f)
(seq := fun α β f x => f >>= (fun y => y <$> x))
(seqLeft := fun α β x y => x >>= fun a => y >>= fun _ => pure a)
(seqRight := fun α β x y => x >>= fun _ => y)
(seqLeft := fun α x y => x >>= fun a => y >>= fun _ => pure a)
(seqRight := fun β x y => x >>= fun _ => y)
instance monadInhabited' {α : Type u} {m : Type u → Type v} [Monad m] : Inhabited (α → m α) :=
⟨pure⟩

View file

@ -504,7 +504,7 @@ variables {m : Type v → Type w} [Monad m]
variable {β : Type v}
@[specialize]
partial def forMAux (f : α → m β) (a : Array α) : Nat → m PUnit
partial def forMAux (f : α → m PUnit) (a : Array α) : Nat → m PUnit
| i =>
if h : i < a.size then
let idx : Fin a.size := ⟨i, h⟩;
@ -513,11 +513,11 @@ partial def forMAux (f : α → m β) (a : Array α) : Nat → m PUnit
else
pure ⟨⟩
@[inline] def forM (f : α → m β) (a : Array α) : m PUnit :=
@[inline] def forM (f : α → m PUnit) (a : Array α) : m PUnit :=
a.forMAux f 0
@[specialize]
partial def forRevMAux (f : α → m β) (a : Array α) : forall (i : Nat), i ≤ a.size → m PUnit
partial def forRevMAux (f : α → m PUnit) (a : Array α) : forall (i : Nat), i ≤ a.size → m PUnit
| i, h =>
if hLt : 0 < i then
have i - 1 < i from Nat.subLt hLt (Nat.zeroLtSucc 0);
@ -528,7 +528,7 @@ partial def forRevMAux (f : α → m β) (a : Array α) : forall (i : Nat), i
else
pure ⟨⟩
@[inline] def forRevM (f : α → m β) (a : Array α) : m PUnit :=
@[inline] def forRevM (f : α → m PUnit) (a : Array α) : m PUnit :=
a.forRevMAux f a.size (Nat.leRefl _)
end

View file

@ -62,22 +62,22 @@ def mapA₂ {m : Type u → Type v} [Applicative m] {α : Type u₁} {β : Type
| _, _ => pure []
@[specialize]
def forM {m : Type u → Type v} [Monad m] {α : Type w} {β : Type u} (f : α → m β) : List α → m PUnit
def forM {m : Type u → Type v} [Monad m] {α : Type w} (f : α → m PUnit) : List α → m PUnit
| [] => pure ⟨⟩
| h :: t => do f h; forM t
@[specialize]
def forM₂ {m : Type u → Type v} [Monad m] {α : Type u₁} {β : Type u₂} {γ : Type u} (f : α → β → m γ) : List α → List β → m PUnit
def forM₂ {m : Type u → Type v} [Monad m] {α : Type u₁} {β : Type u₂} (f : α → β → m PUnit) : List α → List β → m PUnit
| a::as, b::bs => do f a b; forM₂ as bs
| _, _ => pure ⟨⟩
@[specialize]
def forA {m : Type u → Type v} [Applicative m] {α : Type w} {β : Type u} (f : α → m β) : List α → m PUnit
def forA {m : Type u → Type v} [Applicative m] {α : Type w} (f : α → m PUnit) : List α → m PUnit
| [] => pure ⟨⟩
| h :: t => f h *> forA t
@[specialize]
def forA₂ {m : Type u → Type v} [Applicative m] {α : Type u₁} {β : Type u₂} {γ : Type u} (f : α → β → m γ) : List α → List β → m PUnit
def forA₂ {m : Type u → Type v} [Applicative m] {α : Type u₁} {β : Type u₂} (f : α → β → m PUnit) : List α → List β → m PUnit
| a::as, b::bs => f a b *> forA₂ as bs
| _, _ => pure ⟨⟩

View file

@ -231,11 +231,11 @@ else do
b ← foldlFromMAux f t.root (USize.ofNat ini) t.shift b;
t.tail.foldlM f b
@[specialize] partial def forMAux (f : α → m β) : PersistentArrayNode α → m PUnit
@[specialize] partial def forMAux (f : α → m PUnit) : PersistentArrayNode α → m PUnit
| node cs => cs.forM (fun c => forMAux c)
| leaf vs => vs.forM f
@[specialize] def forM (t : PersistentArray α) (f : α → m β) : m PUnit :=
@[specialize] def forM (t : PersistentArray α) (f : α → m PUnit) : m PUnit :=
forMAux f t.root *> t.tail.forM f
end

View file

@ -236,8 +236,8 @@ t.val.depth f
@[inline] def mfold {m : Type w → Type w'} [Monad m] (f : σα → β → m σ) : σ → RBMap α β lt → m σ
| b, ⟨t, _⟩ => t.mfold f b
@[inline] def mfor {m : Type w → Type w'} [Monad m] (f : α → β → m σ) (t : RBMap α β lt) : m PUnit :=
t.mfold (fun _ k v => f k v *> pure ⟨⟩) ⟨⟩
@[inline] def mfor {m : Type w → Type w'} [Monad m] (f : α → β → m PUnit) (t : RBMap α β lt) : m PUnit :=
t.mfold (fun _ k v => f k v) ⟨⟩
@[inline] def isEmpty : RBMap α β lt → Bool
| ⟨leaf, _⟩ => true

View file

@ -34,8 +34,8 @@ RBMap.revFold (fun r a _ => f r a) b t
@[inline] def mfold {m : Type v → Type w} [Monad m] (f : β → α → m β) (b : β) (t : RBTree α lt) : m β :=
RBMap.mfold (fun r a _ => f r a) b t
@[inline] def mfor {m : Type v → Type w} [Monad m] (f : α → m β) (t : RBTree α lt) : m PUnit :=
t.mfold (fun _ a => f a *> pure ⟨⟩) ⟨⟩
@[inline] def mfor {m : Type v → Type w} [Monad m] (f : α → m PUnit) (t : RBTree α lt) : m PUnit :=
t.mfold (fun _ a => f a) ⟨⟩
@[inline] def isEmpty (t : RBTree α lt) : Bool :=
RBMap.isEmpty t

View file

@ -400,7 +400,7 @@ ys.toList.map argToCString
def emitSimpleExternalCall (f : String) (ps : Array Param) (ys : Array Arg) : M Unit := do
emit f; emit "(";
-- We must remove irrelevant arguments to extern calls.
ys.size.foldM
_ ← ys.size.foldM
(fun i (first : Bool) =>
if (ps.get! i).ty.isIrrelevant then
pure first

View file

@ -435,14 +435,14 @@ Expr.const c@(Name.str _ f _) _ _ ← pure fn.getAppFn | failure;
env ← liftM getEnv;
some info ← pure $ env.getProjectionFnInfo c | failure;
-- can't use with classes since the instance parameter is implicit
assert (!info.fromClass);
guard $ !info.fromClass;
-- projection function should be fully applied (#struct params + 1 instance parameter)
-- TODO: support over-application
assert $ e.getAppNumArgs == info.nparams + 1;
guard $ e.getAppNumArgs == info.nparams + 1;
-- If pp.explicit is true, and the structure has parameters, we should not
-- use field notation because we will not be able to see the parameters.
expl ← getPPOption getPPExplicit;
assert $ !expl || info.nparams == 0;
guard $ !expl || info.nparams == 0;
appStx ← withAppArg delab;
`($(appStx).$(mkIdent f):ident)
@ -450,7 +450,7 @@ appStx ← withAppArg delab;
@[builtinDelab app.coe]
def delabCoe : Delab := whenPPOption getPPCoercions $ do
e ← getExpr;
assert $ e.getAppNumArgs >= 4;
guard $ e.getAppNumArgs >= 4;
-- delab as application, then discard function
stx ← delabAppImplicit;
match_syntax stx with

View file

@ -180,7 +180,7 @@ unless (ctx.explicit || ctx.foundExplicit || ctx.typeMVars.isEmpty) $ do
| some eTypeBody =>
unless eTypeBody.hasLooseBVars $
when (hasTypeMVar ctx eTypeBody && hasOnlyTypeMVar ctx eTypeBody) $ do
isDefEq ctx.ref expectedType eTypeBody;
_ ← isDefEq ctx.ref expectedType eTypeBody;
pure ()
private def nextArgIsHole (ctx : ElabAppArgsCtx) : Bool :=
@ -201,7 +201,7 @@ private partial def elabAppArgsAux : ElabAppArgsCtx → Expr → Expr → TermEl
| none => pure ()
| some expectedType => do {
-- Try to propagate expected type. Ignore if types are not definitionally equal, caller must handle it.
isDefEq ctx.ref expectedType eType;
_ ← isDefEq ctx.ref expectedType eType;
pure ()
};
synthesizeAppInstMVars ctx.ref ctx.instMVars;

View file

@ -305,7 +305,7 @@ match s.expectedType? with
expectedType ← whnfForall ref expectedType;
match expectedType with
| Expr.forallE _ d b _ => do
isDefEq ref fvarType d;
_ ← isDefEq ref fvarType d;
checkNoOptAutoParam ref fvarType;
let b := b.instantiate1 fvar;
pure { expectedType? := some b, .. s }

View file

@ -509,7 +509,7 @@ fun stx => do
def hasNoErrorMessages : CommandElabM Bool := do
s ← get; pure $ !s.messages.hasErrors
def failIfSucceeds {α} (ref : Syntax) (x : CommandElabM α) : CommandElabM Unit := do
def failIfSucceeds (ref : Syntax) (x : CommandElabM Unit) : CommandElabM Unit := do
let resetMessages : CommandElabM MessageLog := do {
s ← get;
let messages := s.messages;

View file

@ -521,7 +521,7 @@ match expectedType? with
| none => pure ()
| some typeBody =>
unless typeBody.hasLooseBVars $ do
isDefEq ref expectedType typeBody;
_ ← isDefEq ref expectedType typeBody;
pure ()
private def mkCtorHeader (ref : Syntax) (ctorVal : ConstructorVal) (expectedType? : Option Expr) : TermElabM CtorHeaderResult := do

View file

@ -1166,7 +1166,7 @@ fun stx expectedType? => do
typeMVar ← mkFreshTypeMVar ref MetavarKind.synthetic;
registerSyntheticMVar ref typeMVar.mvarId! (SyntheticMVarKind.withDefault (Lean.mkConst `Nat));
match expectedType? with
| some expectedType => do isDefEq ref expectedType typeMVar; pure ()
| some expectedType => do _ ← isDefEq ref expectedType typeMVar; pure ()
| _ => pure ();
u ← getLevel ref typeMVar;
u ← decLevel ref u;

View file

@ -222,10 +222,10 @@ lctx.decls.foldlM (fun b decl => match decl with
| some decl => f b decl)
b
@[specialize] def forM (lctx : LocalContext) (f : LocalDecl → m β) : m PUnit :=
@[specialize] def forM (lctx : LocalContext) (f : LocalDecl → m PUnit) : m PUnit :=
lctx.decls.forM $ fun decl => match decl with
| none => pure PUnit.unit
| some decl => f decl *> pure PUnit.unit
| some decl => f decl
@[specialize] def findDeclM? (lctx : LocalContext) (f : LocalDecl → m (Option β)) : m (Option β) :=
lctx.decls.findSomeM? $ fun decl => match decl with

View file

@ -254,9 +254,9 @@ def throwBug {α} (b : Bug) : MetaM α :=
throwEx $ Exception.bug b
/-- Execute `x` only in debugging mode. -/
@[inline] private def whenDebugging {α} (x : MetaM α) : MetaM Unit := do
@[inline] private def whenDebugging (x : MetaM Unit) : MetaM Unit := do
ctx ← read;
when ctx.config.debug (do x; pure ())
when ctx.config.debug x
@[inline] def shouldReduceAll : MetaM Bool := do
ctx ← read; pure $ ctx.config.transparency == TransparencyMode.all

View file

@ -15,7 +15,7 @@ namespace Lean
namespace Meta
private def ensureType (e : Expr) : MetaM Unit := do
getLevel e; pure ()
_ ← getLevel e; pure ()
@[specialize] private def checkLambdaLet
(check : Expr → MetaM Unit)

View file

@ -165,8 +165,8 @@ if h : args₁.size = args₂.size then do
let a₂ := args₂.get! i;
let info := finfo.paramInfo.get! i;
when info.instImplicit $ do {
trySynthPending a₁;
trySynthPending a₂;
_ ← trySynthPending a₁;
_ ← trySynthPending a₂;
pure ()
};
withAtLeastTransparency TransparencyMode.default $ isExprDefEqAux a₁ a₂)

View file

@ -1842,7 +1842,7 @@ categoryParserFnRef.set categoryParserFnImpl
def addToken (env : Environment) (tk : TokenConfig) : Except String Environment := do
-- Recall that `ParserExtension.addEntry` is pure, and assumes `addTokenConfig` does not fail.
-- So, we must run it here to handle exception.
addTokenConfig (parserExtension.getState env).tokens tk;
_ ← addTokenConfig (parserExtension.getState env).tokens tk;
pure $ parserExtension.addEntry env $ ParserExtensionEntry.token tk
def addSyntaxNodeKind (env : Environment) (k : SyntaxNodeKind) : Environment :=

View file

@ -58,7 +58,7 @@ whenM (isTracingEnabledFor cls) (do msg ← mkMsg; addTrace cls msg)
@[inline] def traceCtx (cls : Name) (ctx : m α) : m α := do
b ← isTracingEnabledFor cls;
if !b then do old ← enableTracing false; a ← ctx; enableTracing old; pure a
if !b then do old ← enableTracing false; a ← ctx; _ ← enableTracing old; pure a
else do
oldCurrTraces ← getResetTraces;
a ← ctx;
@ -78,8 +78,8 @@ b ← isTracingEnabledFor cls;
if !b then do
old ← enableTracing false;
catch
(do a ← ctx; enableTracing old; pure a)
(fun e => do enableTracing old; throw e)
(do a ← ctx; _ ← enableTracing old; pure a)
(fun e => do _ ← enableTracing old; throw e)
else do
oldCurrTraces ← getResetTraces;
catch

View file

@ -346,10 +346,7 @@ static expr parse_do(parser & p, bool has_braces) {
pos);
}
} else {
r = p.rec_save_pos(mk_app(p.save_pos(mk_bind_fn(p), ps[i]),
es[i],
p.save_pos(mk_lambda("_x", mk_expr_placeholder(), r), p.pos_of(r))),
ps[i]);
r = p.rec_save_pos(mk_app(p.save_pos(mk_const({"HasSeqRight", "seqRight"}), ps[i]), es[i], r), ps[i]);
}
}
return r;

View file

@ -346,10 +346,7 @@ static expr parse_do(parser & p, bool has_braces) {
pos);
}
} else {
r = p.rec_save_pos(mk_app(p.save_pos(mk_bind_fn(p), ps[i]),
es[i],
p.save_pos(mk_lambda("_x", mk_expr_placeholder(), r), p.pos_of(r))),
ps[i]);
r = p.rec_save_pos(mk_app(p.save_pos(mk_const({"HasSeqRight", "seqRight"}), ps[i]), es[i], r), ps[i]);
}
}
return r;

View file

@ -53,7 +53,7 @@ def main : List String → IO UInt32
-- allocate, walk, and deallocate many bottom-up binary trees
let vs := (depth minN maxN); -- `using` (parList $ evalTuple3 r0 r0 rseq)
vs.mapM (fun ⟨m,d,i⟩ => out (toString m ++ "\t trees") d i.get);
vs.forM (fun ⟨m,d,i⟩ => out (toString m ++ "\t trees") d i.get);
-- confirm the the long-lived binary tree still exists
out "long lived tree" maxN (check long);

View file

@ -94,6 +94,6 @@ unsafe def main : List String → IO UInt32
let n := s.toNat!;
let x := Var "x";
let f := pow x x;
nest deriv n f;
_ ← nest deriv n f;
pure 0
| _ => pure 1

View file

@ -4,6 +4,6 @@ def main : List String → IO Unit
| [fname, n] => do
env ← Lean.mkEmptyEnvironment;
n.toNat!.forM $ fun _ =>
Lean.Parser.parseFile env fname *> pure ();
discard $ Lean.Parser.parseFile env fname;
pure ()
| _ => throw $ IO.userError "give file"

View file

@ -80,7 +80,7 @@ do r₁ ← findEntry n₁;
def mkNodes : Nat → M Unit
| 0 => pure ()
| n+1 => mk *> mkNodes n
| n+1 => do _ ← mk; mkNodes n
def checkEq (n₁ n₂ : Node) : M Unit :=
do r₁ ← find n₁; r₂ ← find n₂;

View file

@ -92,7 +92,7 @@ do
def main (xs : List String) : IO UInt32 :=
do let x := Var "x";
let f := pow x x;
nest deriv 7 f;
_ ← nest deriv 7 f;
pure 0
-- setOption profiler True

View file

@ -107,7 +107,7 @@ def main (xs : List String) : IO UInt32 :=
do let x := Var "x";
let f := add x (mul x (mul x (add x x)));
IO.println f;
nest deriv 3 f;
_ ← nest deriv 3 f;
pure 0
-- setOption profiler True

View file

@ -3,13 +3,13 @@ import Init.System.IO
open IO.FS
#eval (IO.FS.Handle.mk "non-existent-file.txt" Mode.read *> pure () : IO Unit)
#eval (discard $ IO.FS.Handle.mk "non-existent-file.txt" Mode.read : IO Unit)
#eval do condM (IO.fileExists "readonly.txt")
(pure ())
(IO.FS.withFile "readonly.txt" Mode.write $ fun _ => pure ());
IO.setAccessRights "readonly.txt" { user := { read := true } };
(pure () : IO Unit)
#eval (IO.FS.Handle.mk "readonly.txt" Mode.write *> pure () : IO Unit)
#eval (discard $ IO.FS.Handle.mk "readonly.txt" Mode.write : IO Unit)
#eval do h ← IO.FS.Handle.mk "readonly.txt" Mode.read;
h.putStr "foo";
IO.println "foo";

View file

@ -10,7 +10,7 @@ n.forM $ fun i => do
def showArrayRef (r : IO.Ref (Array Nat)) : IO Unit :=
do a ← r.swap ∅;
a.size.forM (fun i => IO.println ("[" ++ toString i ++ "]: " ++ toString (a.get! i)));
r.swap a;
_ ← r.swap a;
pure ()
def tst (n : Nat) : IO Unit :=

View file

@ -92,5 +92,5 @@ do
unsafe def main : IO Unit :=
do let x := Var "x";
let f := pow x x;
nest deriv 9 f;
_ ← nest deriv 9 f;
pure ()

View file

@ -5,7 +5,7 @@ if System.Platform.isWindows then
pure () -- TODO investigate why the following doesn't work on Windows
else do
env ← Lean.mkEmptyEnvironment;
Lean.Parser.parseFile env (System.mkFilePath ["..", "..", "..", "src", "Init", "Core.lean"]);
_ ← Lean.Parser.parseFile env (System.mkFilePath ["..", "..", "..", "src", "Init", "Core.lean"]);
IO.println "done"
#eval test