lean4-htt/Lake/Util/Async.lean
2022-02-03 21:32:27 -05:00

273 lines
11 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) 2021 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/
import Lake.Util.Task
import Lake.Util.OptionIO
import Lake.Util.Misc
/-!
This module Defines the asynchronous monadic interface for Lake.
The interface is composed of three major abstract monadic types:
* `m`: The monad of the synchronous action (e.g., `IO`).
* `n`: The monad of the (a)synchronous task manager (e.g., `BaseIO`).
* `k`: The monad of the (a)synchronous task (e.g., `IOTask`).
The definitions within this module provide the basic utilities for converting
between these monads and combining them in different ways.
-/
namespace Lake
--------------------------------------------------------------------------------
-- # Async / Await Abstraction
--------------------------------------------------------------------------------
class Sync (m : Type u → Type v) (n : outParam $ Type u' → Type w) (k : outParam $ Type u → Type u') where
/- Run the monadic action as a synchronous task. -/
sync : m α → n (k α)
export Sync (sync)
class Async (m : Type u → Type v) (n : outParam $ Type u' → Type w) (k : outParam $ Type u → Type u') where
/- Run the monadic action as an asynchronous task. -/
async : m α → n (k α)
export Async (async)
class Await (k : Type u → Type v) (m : outParam $ Type u → Type w) where
/- Wait for an (a)synchronous task to finish. -/
await : k α → m α
export Await (await)
-- ## Standard Instances
instance : Sync Id Id Task := ⟨Task.pure⟩
instance : Sync BaseIO BaseIO BaseIOTask := ⟨Functor.map Task.pure⟩
instance [Sync m n k] : Sync (ReaderT ρ m) (ReaderT ρ n) k where
sync x := fun r => sync (x r)
instance [Sync m n k] : Sync (ExceptT ε m) n (ExceptT ε k) where
sync x := cast (by delta ExceptT; rfl) <| sync (n := n) x.run
instance [Sync m n k] : Sync (OptionT m) n (OptionT k) where
sync x := cast (by delta OptionT; rfl) <| sync (n := n) x.run
instance : Sync (EIO ε) BaseIO (EIOTask ε) where
sync x := sync <| ExceptT.mk x.toBaseIO
instance : Sync OptionIO BaseIO OptionIOTask where
sync x := sync <| OptionT.mk x.toBaseIO
instance : Async Id Id Task := ⟨Task.pure⟩
instance : Async BaseIO BaseIO BaseIOTask := ⟨BaseIO.asTask⟩
instance [Async m n k] : Async (ReaderT ρ m) (ReaderT ρ n) k where
async x := fun r => async (x r)
instance [Async m n k] : Async (ExceptT ε m) n (ExceptT ε k) where
async x := cast (by delta ExceptT; rfl) <| async (n := n) x.run
instance [Async m n k] : Async (OptionT m) n (OptionT k) where
async x := cast (by delta OptionT; rfl) <| async (n := n) x.run
instance : Async (EIO ε) BaseIO (EIOTask ε) where
async x := async <| ExceptT.mk x.toBaseIO
instance : Async OptionIO BaseIO OptionIOTask where
async x := async <| OptionT.mk x.toBaseIO
instance : Await Task Id := ⟨Task.get⟩
instance : Await (EIOTask ε) (EIO ε) where
await x := IO.wait x >>= liftExcept
instance : Await OptionIOTask OptionIO where
await x := IO.wait x >>= liftOption
instance [Await k m] : Await (ExceptT ε k) (ExceptT ε m) where
await x := ExceptT.mk <| await x.run
instance [Await k m] : Await (OptionT k) (OptionT m) where
await x := OptionT.mk <| await x.run
--------------------------------------------------------------------------------
-- # Combinators
--------------------------------------------------------------------------------
class BindSync (m : Type u → Type v) (n : outParam $ Type u' → Type w) (k : outParam $ Type u → Type u') where
/-- Perform a synchronous action after another (a)synchronous task completes successfully. -/
bindSync {α β : Type u} : k α → (α → m β) → n (k β)
export BindSync (bindSync)
class BindAsync (n : Type u → Type v) (k : outParam $ Type u → Type u) where
/-- Perform a asynchronous task after another (a)synchronous task completes successfully. -/
bindAsync {α β : Type u} : k α → (α → n (k β)) → n (k β)
export BindAsync (bindAsync)
class SeqAsync (n : outParam $ Type u → Type v) (k : Type u → Type u) where
/-- Combine two (a)synchronous tasks, applying the result of the second one ot the first one. -/
seqAsync {α β : Type u} : k (α → β) → k α → n (k β)
export SeqAsync (seqAsync)
class SeqLeftAsync (n : outParam $ Type u → Type v) (k : Type u → Type u) where
/-- Combine two (a)synchronous tasks, returning the result of the first one. -/
seqLeftAsync {α β : Type u} : k α → k β → n (k α)
export SeqLeftAsync (seqLeftAsync)
class SeqRightAsync (n : outParam $ Type u → Type v) (k : Type u → Type u) where
/-- Combine two (a)synchronous tasks, returning the result of the second one. -/
seqRightAsync {α β : Type u} : k α → k β → n (k β)
export SeqRightAsync (seqRightAsync)
class SeqWithAsync (n : outParam $ Type u → Type v) (k : Type u → Type u) where
/-- Combine two (a)synchronous tasks using `f`. -/
seqWithAsync {α β : Type u} : (f : α → β → γ) → k α → k β → n (k γ)
export SeqWithAsync (seqWithAsync)
class ApplicativeAsync (n : outParam $ Type u → Type v) (k : Type u → Type u)
extends SeqAsync n k, SeqLeftAsync n k, SeqRightAsync n k, SeqWithAsync n k where
seqAsync := seqWithAsync fun f a => f a
seqLeftAsync := seqWithAsync fun a _ => a
seqRightAsync := seqWithAsync fun _ b => b
-- ## Standard Instances
instance : BindSync Id Id Task := ⟨flip Task.map⟩
instance : BindSync BaseIO BaseIO BaseIOTask := ⟨flip BaseIO.mapTask⟩
instance : BindSync (EIO ε) BaseIO (ETask ε) where
bindSync ka f := ka.run |> BaseIO.mapTask fun
| Except.ok a => f a |>.toBaseIO
| Except.error e => pure <| Except.error e
instance : BindSync OptionIO BaseIO OptionIOTask where
bindSync ka f := ka.run |> BaseIO.mapTask fun
| some a => f a |>.toBaseIO
| none => pure none
instance [BindSync m n k] : BindSync (ReaderT ρ m) (ReaderT ρ n) k where
bindSync ka f := fun r => bindSync ka fun a => f a r
instance [BindSync m n k] [Pure m] : BindSync (ExceptT ε m) n (ExceptT ε k) where
bindSync ka f := cast (by delta ExceptT; rfl) <| bindSync (n := n) ka.run fun
| Except.ok a => f a |>.run
| Except.error e => pure <| Except.error e
instance [BindSync m n k] [Pure m] : BindSync (OptionT m) n (OptionT k) where
bindSync ka f := cast (by delta OptionT; rfl) <| bindSync ka.run fun
| some a => f a |>.run
| none => pure none
instance : BindAsync Id Task := ⟨Task.bind⟩
instance : BindAsync BaseIO BaseIOTask := ⟨BaseIO.bindTask⟩
instance : BindAsync BaseIO (EIOTask ε) where
bindAsync ka f := BaseIO.bindTask ka.run fun
| Except.ok a => f a
| Except.error e => pure <| pure (Except.error e)
instance : BindAsync BaseIO OptionIOTask where
bindAsync ka f := BaseIO.bindTask ka.run fun
| some a => f a
| none => pure (pure none)
instance [BindAsync n k] : BindAsync (ReaderT ρ n) k where
bindAsync ka f := fun r => bindAsync ka fun a => f a r
instance [BindAsync n k] [Pure n] [Pure k] : BindAsync n (ExceptT ε k) where
bindAsync ka f := cast (by delta ExceptT; rfl) <| bindAsync ka.run fun
| Except.ok a => f a
| Except.error e => pure <| pure <| Except.error e
instance [BindAsync n k] [Pure n] [Pure k] : BindAsync n (OptionT k) where
bindAsync ka f := cast (by delta OptionT; rfl) <| bindAsync ka.run fun
| some a => f a
| none => pure (pure none)
instance : ApplicativeAsync Id Task where
seqWithAsync f ka kb := ka.bind fun a => kb.bind fun b => pure <| f a b
instance : ApplicativeAsync BaseIO BaseIOTask where
seqWithAsync f ka kb := BaseIO.bindTask ka fun a => BaseIO.bindTask kb fun b => pure <| pure <| f a b
instance [ApplicativeAsync n k] : ApplicativeAsync n (ExceptT ε k) where
seqWithAsync f ka kb :=
let h xa xb : Except ε _ := Id.run <| ExceptT.run do
let a ← liftExcept xa
let b ← liftExcept xb
pure <| f a b
cast (by delta ExceptT; rfl) <| seqWithAsync (n := n) h ka kb
instance [ApplicativeAsync n k] : ApplicativeAsync n (OptionT k) where
seqWithAsync f ka kb :=
let h xa xb := Id.run <| OptionT.run do
let a ← liftOption xa
let b ← liftOption xb
pure <| f a b
cast (by delta OptionT; rfl) <| seqWithAsync (n := n) h ka kb
--------------------------------------------------------------------------------
-- # List/Array Utilities
--------------------------------------------------------------------------------
-- ## Sequencing (A)synchronous Tasks
/-- Combine all (a)synchronous tasks in a `List` from right to left into a single task ending `last`. -/
def seqLeftList1Async [SeqLeftAsync n k] [Monad n] (last : (k α)) : (tasks : List (k α)) → n (k α)
| [] => return last
| t::ts => seqLeftList1Async t ts >>= (seqLeftAsync last ·)
/-- Combine all (a)synchronous tasks in a `List` from right to left into a single task. -/
def seqLeftListAsync [SeqLeftAsync n k] [Monad n] [Pure k] : (tasks : List (k PUnit)) → n (k PUnit)
| [] => return (pure ())
| t::ts => seqLeftList1Async t ts
/-- Combine all (a)synchronous tasks in a `List` from left to right into a single task. -/
def seqRightListAsync [SeqRightAsync n k] [Monad n] [Pure k] : (tasks : List (k PUnit)) → n (k PUnit)
| [] => return (pure ())
| t::ts => ts.foldlM seqRightAsync t
/-- Combine all (a)synchronous tasks in a `Array` from right to left into a single task. -/
def seqLeftArrayAsync [SeqLeftAsync n k] [Monad n] [Pure k] (tasks : Array (k PUnit)) : n (k PUnit) :=
if h : 0 < tasks.size then
tasks.pop.foldrM seqLeftAsync (tasks.get ⟨tasks.size - 1, Nat.sub_lt h (by decide)⟩)
else
return (pure ())
/-- Combine all (a)synchronous tasks in a `Array` from left to right into a single task. -/
def seqRightArrayAsync [SeqRightAsync n k] [Monad n] [Pure k] (tasks : Array (k PUnit)) : n (k PUnit) :=
if h : 0 < tasks.size then
tasks.foldlM seqRightAsync (tasks.get ⟨0, h⟩)
else
return (pure ())
-- ## Folding (A)synchronous Tasks
variable [SeqWithAsync n k] [Monad n] [Pure k]
/-- Fold a `List` of (a)synchronous tasks from right to left (i.e., a right fold) into a single task. -/
def foldLeftListAsync (f : α → β → β) (init : β) (tasks : List (k α)) : n (k β) :=
tasks.foldrM (seqWithAsync f) (pure init)
/-- Fold an `Array` of (a)synchronous tasks from right to left (i.e., a right fold) into a single task. -/
def foldLeftArrayAsync (f : α → β → β) (init : β) (tasks : Array (k α)) : n (k β) :=
tasks.foldrM (seqWithAsync f) (pure init)
/-- Fold a `List` of (a)synchronous tasks from left to right (i.e., a left fold) into a single task. -/
def foldRightListAsync (f : β → α → β) (init : β) (tasks : List (k α)) : n (k β) :=
tasks.foldlM (seqWithAsync f) (pure init)
/-- Fold an `Array` of (a)synchronous tasks from left to right (i.e., a left fold) into a single task. -/
def foldRightArrayAsync (f : β → α → β) (init : β) (tasks : Array (k α)) : n (k β) :=
tasks.foldlM (seqWithAsync f) (pure init)