lean4-htt/library/Init/Control/EState.lean
Leonardo de Moura 0714716477 fix: file and import names, tests and stage0
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2019-10-04 17:04:02 -07:00

165 lines
6.1 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
The combined Except and State Monad that minimizes the number
of memory allocations using the approach described in the paper
"Counting immutable beans" by Sebastian and Leo
-/
prelude
import Init.Control.State
import Init.Control.Except
universes u v
namespace EState
inductive Result (ε σ α : Type u)
| ok {} : ασ → Result
| error {} : ε → σ → Result
variables {ε σ α : Type u}
inductive Result.IsOk : Result ε σ α → Prop
| mk (a : α) (s : σ) : Result.IsOk (Result.ok a s)
theorem notIsOkError {e : ε} {s : σ} (h : @Result.IsOk _ _ α (Result.error e s)) : False :=
nomatch h
@[inline] def unreachableError {β : Type v} {e : ε} {s : σ} (h : @Result.IsOk _ _ α (Result.error e s)) : β :=
False.elim (notIsOkError h)
abbrev resultOk (ε σ α : Type u) := {r : Result ε σ α // r.IsOk}
@[inline] protected def resultOk.mk (a : α) (s : σ) : resultOk ε σ α :=
⟨Result.ok a s, Result.IsOk.mk a s⟩
protected def Result.toString [HasToString ε] [HasToString α] : Result ε σ α → String
| Result.ok a _ => "ok: " ++ toString a
| Result.error e _ => "error: " ++ toString e
protected def Result.repr [HasRepr ε] [HasRepr α] : Result ε σ α → String
| Result.error e _ => "(error " ++ repr e ++ ")"
| Result.ok a _ => "(ok " ++ repr a ++ ")"
instance [HasToString ε] [HasToString α] : HasToString (Result ε σ α) := ⟨Result.toString⟩
instance [HasRepr ε] [HasRepr α] : HasRepr (Result ε σ α) := ⟨Result.repr⟩
end EState
def EState (ε σ α : Type u) := EState.resultOk PUnit σ PUnit → EState.Result ε σ α
namespace EState
variables {ε σ α β : Type u}
instance [Inhabited ε] : Inhabited (EState ε σ α) :=
⟨fun r => match r with
| ⟨Result.ok _ s, _⟩ => Result.error (default ε) s
| ⟨Result.error _ _, h⟩ => unreachableError h⟩
@[inline] protected def pure (a : α) : EState ε σ α :=
fun r => match r with
| ⟨Result.ok _ s, _⟩ => Result.ok a s
| ⟨Result.error _ _, h⟩ => unreachableError h
@[inline] protected def set (s : σ) : EState ε σ PUnit :=
fun r => match r with
| ⟨Result.ok _ _, _⟩ => Result.ok ⟨⟩ s
| ⟨Result.error _ _, h⟩ => unreachableError h
@[inline] protected def get : EState ε σ σ :=
fun r => match r with
| ⟨Result.ok _ s, _⟩ => Result.ok s s
| ⟨Result.error _ _, h⟩ => unreachableError h
@[inline] protected def modifyGet (f : σα × σ) : EState ε σ α :=
fun r => match r with
| ⟨Result.ok _ s, _⟩ =>
match f s with
| (a, s) => Result.ok a s
| ⟨Result.error _ _, h⟩ => unreachableError h
@[inline] protected def throw (e : ε) : EState ε σ α :=
fun r => match r with
| ⟨Result.ok _ s, _⟩ => Result.error e s
| ⟨Result.error _ _, h⟩ => unreachableError h
@[inline] protected def catch (x : EState ε σ α) (handle : ε → EState ε σ α) : EState ε σ α :=
fun r => match x r with
| Result.error e s => handle e (resultOk.mk ⟨⟩ s)
| ok => ok
@[inline] protected def orelse (x₁ x₂ : EState ε σ α) : EState ε σ α :=
fun r => match x₁ r with
| Result.error _ s => x₂ (resultOk.mk ⟨⟩ s)
| ok => ok
/-- Alternative orelse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orelse` uses the second. -/
@[inline] protected def orelse' (x₁ x₂ : EState ε σ α) (useFirstEx := true) : EState ε σ α :=
fun r => match x₁ r with
| Result.error e₁ s₁ =>
match x₂ (resultOk.mk ⟨⟩ s₁) with
| Result.error e₂ s₂ => Result.error (if useFirstEx then e₁ else e₂) s₂
| ok => ok
| ok => ok
@[inline] def adaptExcept {ε' : Type u} [HasLift ε ε'] (x : EState ε σ α) : EState ε' σ α :=
fun r => match x r with
| Result.error e s => Result.error (lift e) s
| Result.ok a s => Result.ok a s
@[inline] protected def bind (x : EState ε σ α) (f : α → EState ε σ β) : EState ε σ β :=
fun r => match x r with
| Result.ok a s => f a (resultOk.mk ⟨⟩ s)
| Result.error e s => Result.error e s
@[inline] protected def map (f : α → β) (x : EState ε σ α) : EState ε σ β :=
fun r => match x r with
| Result.ok a s => Result.ok (f a) s
| Result.error e s => Result.error e s
@[inline] protected def seqRight (x : EState ε σ α) (y : EState ε σ β) : EState ε σ β :=
fun r => match x r with
| Result.ok _ s => y (resultOk.mk ⟨⟩ s)
| Result.error e s => Result.error e s
instance : Monad (EState ε σ) :=
{ bind := @EState.bind _ _, pure := @EState.pure _ _, map := @EState.map _ _, seqRight := @EState.seqRight _ _ }
instance : HasOrelse (EState ε σ α) :=
{ orelse := @EState.orelse _ _ _ }
instance : MonadState σ (EState ε σ) :=
{ set := @EState.set _ _, get := @EState.get _ _, modifyGet := @EState.modifyGet _ _ }
instance : MonadExcept ε (EState ε σ) :=
{ throw := @EState.throw _ _, catch := @EState.catch _ _ }
@[inline] def adaptState {σ₁ σ₂} (x : EState ε σ₁ α) (split : σ → σ₁ × σ₂) (merge : σ₁ → σ₂ → σ) : EState ε σ α :=
fun r => match r with
| ⟨Result.error _ _, h⟩ => unreachableError h
| ⟨Result.ok _ s, _⟩ =>
let (s₁, s₂) := split s;
match x (resultOk.mk ⟨⟩ s₁) with
| Result.ok a s₁ => Result.ok a (merge s₁ s₂)
| Result.error e s₁ => Result.error e (merge s₁ s₂)
def fromState {ε σ α : Type} (s : State σ α) : EState ε σ α :=
λ (ok : EState.resultOk PUnit σ PUnit) =>
match ok with
| ⟨EState.Result.ok _ st₁, _⟩ =>
match s.run st₁ with
| ⟨val₂, st₂⟩ => EState.Result.ok val₂ st₂
| ⟨EState.Result.error _ _, H⟩ => False.elim $ EState.notIsOkError H
@[inline] def run (x : EState ε σ α) (s : σ) : Result ε σ α :=
x (resultOk.mk ⟨⟩ s)
@[inline] def run' (x : EState ε σ α) (s : σ) : Option α :=
match run x s with
| Result.ok v _ => some v
| Result.error _ _ => none
end EState