92 lines
3.3 KiB
Text
92 lines
3.3 KiB
Text
/-
|
||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Sebastian Ullrich, Mac Malone
|
||
-/
|
||
import Std.Data.RBMap
|
||
|
||
open Std
|
||
namespace Lake
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- # Topological / Suspending Builder
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- ## Abstractions
|
||
|
||
/-- A recursive object fetcher. -/
|
||
def RecFetch.{u,v,w} (k : Type u) (o : Type v) (m : Type v → Type w) :=
|
||
k → (k → m o) → m o
|
||
|
||
/-- A monad equipped with a key-object store. -/
|
||
class MonadStore (k : Type u) (o : Type v) (m : Type v → Type w) where
|
||
fetch? : k → m (Option o)
|
||
store : k → o → m PUnit
|
||
|
||
export MonadStore (fetch? store)
|
||
|
||
instance [Monad m] [MonadStore k o m] : MonadStore k o (ExceptT ε m) where
|
||
fetch? key := liftM (m := m) <| fetch? key
|
||
store key obj := liftM (m := m) <| store key obj
|
||
|
||
-- ## Algorithm
|
||
|
||
/-- Auxiliary function for `buildTop`. -/
|
||
partial def buildTopCore
|
||
{k o} {m} [BEq k] [Inhabited o] [Monad m] [MonadStore k o m]
|
||
(parents : List k) (build : RecFetch k o (ExceptT (List k) m)) (key : k) : ExceptT (List k) m o := do
|
||
-- detect cyclic builds
|
||
if parents.contains key then
|
||
throw <| key :: (parents.partition (· != key)).1 ++ [key]
|
||
-- return previous output if already built
|
||
if let some output ← fetch? key then
|
||
return output
|
||
-- build the key recursively
|
||
let output ← build key (buildTopCore (key :: parents) build)
|
||
-- save the output (to prevent repeated builds of the same key)
|
||
store key output
|
||
return output
|
||
|
||
/--
|
||
Recursively fills a `MonadStore` of key-object pairs by
|
||
fetching objects topologically (i.e., via a depth-first search with memoization).
|
||
Called a suspending scheduler in "Build systems à la carte".
|
||
-/
|
||
def buildTop
|
||
{k o} {m} [BEq k] [Inhabited o] [Monad m] [MonadStore k o m]
|
||
(build : RecFetch k o (ExceptT (List k) m)) (key : k) : ExceptT (List k) m o :=
|
||
buildTopCore [] build key
|
||
|
||
--------------------------------------------------------------------------------
|
||
-- # RBMap Version
|
||
--------------------------------------------------------------------------------
|
||
|
||
/-- A exception plus state monad transformer (i.e., `ExceptT` + `StateT`). -/
|
||
abbrev EStateT (ε σ m) :=
|
||
ExceptT ε <| StateT σ m
|
||
|
||
def EStateT.run (state : σ) (self : EStateT ε σ m α) : m (Except ε α × σ) :=
|
||
ExceptT.run self |>.run state
|
||
|
||
def EStateT.run' [Monad m] (state : σ) (self : EStateT ε σ m α) : m (Except ε α) :=
|
||
ExceptT.run self |>.run' state
|
||
|
||
/-- A transformer that adds an `RBMap` store to a monad. -/
|
||
abbrev RBMapT.{u,v} (k : Type u) (o : Type u) (cmp) (m : Type u → Type v) :=
|
||
StateT (RBMap k o cmp) m
|
||
|
||
instance (cmp) [Monad m] : MonadStore k o (RBMapT k o cmp m) where
|
||
fetch? key := do (← get).find? key
|
||
store key obj := modify (·.insert key obj)
|
||
|
||
/--
|
||
Monad transformer for an RBMap-based topological walk.
|
||
If a cycle is detected, the list of keys traversed is thrown.
|
||
-/
|
||
abbrev RBTopT.{u,v} (k : Type u) (o : Type u) (cmp) (m : Type u → Type v) :=
|
||
EStateT (List k) (RBMap k o cmp) m
|
||
|
||
/-- The `RBMap` version of `buildTop`. -/
|
||
def buildRBTop {k o} {cmp} {m} [BEq k] [Inhabited o] [Monad m]
|
||
(build : RecFetch k o (RBTopT k o cmp m)) (key : k) : RBTopT k o cmp m o :=
|
||
buildTop build key
|