mirror of
https://github.com/unisonweb/unison.git
synced 2024-10-05 06:07:21 +03:00
wip factoring out logic for hashing strongly connected components
This commit is contained in:
parent
ac5f973efc
commit
2c678a6918
@ -32,6 +32,7 @@ import qualified Data.Text as Text
|
||||
import qualified Data.Vector as Vector
|
||||
import qualified Unison.Hashable as Hashable
|
||||
import qualified Unison.Var as Var
|
||||
import qualified Unison.Util.Components as Components
|
||||
|
||||
data ABT f v r
|
||||
= Var v
|
||||
@ -452,6 +453,9 @@ instance (Foldable f, Functor f, Eq1 f, Var v) => Eq (Term f v a) where
|
||||
go (Tm f1) (Tm f2) = f1 ==# f2
|
||||
go _ _ = False
|
||||
|
||||
components :: Var v => [(v, Term f v a)] -> [[(v, Term f v a)]]
|
||||
components = Components.components freeVars
|
||||
|
||||
-- Hash a strongly connected component and sort its definitions into a canonical order.
|
||||
hashComponent ::
|
||||
(Functor f, Hashable1 f, Foldable f, Eq v, Var v, Ord h, Accumulate h)
|
||||
|
@ -19,7 +19,6 @@ import Unison.Reference (Reference)
|
||||
import qualified Unison.Reference as Reference
|
||||
import Unison.Type (AnnotatedType)
|
||||
import qualified Unison.Type as Type
|
||||
import Unison.Typechecker.Components (components)
|
||||
import Unison.Var (Var)
|
||||
|
||||
type DataDeclaration v = DataDeclaration' v ()
|
||||
@ -138,7 +137,7 @@ hashDecls0
|
||||
:: (Eq v, Var v)
|
||||
=> Map v (DataDeclaration' v ())
|
||||
-> [(v, Reference, DataDeclaration' v ())]
|
||||
hashDecls0 decls = reverse . snd . foldl f ([], []) $ components abts
|
||||
hashDecls0 decls = reverse . snd . foldl f ([], []) $ ABT.components abts
|
||||
where
|
||||
f (m, newDecls) cycle =
|
||||
let
|
||||
|
@ -7,6 +7,8 @@ import Unison.Hashable as Hashable
|
||||
import qualified Data.Text as Text
|
||||
import qualified Unison.Hash as H
|
||||
|
||||
-- could add a `Word` parameter to `Derived`
|
||||
-- associated with each hash would actually be a list of terms / type decls
|
||||
data Reference = Builtin Text.Text | Derived H.Hash deriving (Eq,Ord,Generic)
|
||||
|
||||
instance Show Reference where
|
||||
|
@ -1,65 +1,19 @@
|
||||
module Unison.Typechecker.Components (components, minimize, minimize') where
|
||||
module Unison.Typechecker.Components (minimize, minimize') where
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
import Data.Bifunctor (first)
|
||||
import Data.Function (on)
|
||||
import qualified Data.Graph as Graph
|
||||
import Data.List (groupBy, sortBy)
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as Nel
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
import qualified Unison.ABT as ABT
|
||||
import Unison.Term (AnnotatedTerm')
|
||||
import qualified Unison.Term as Term
|
||||
import Unison.Var (Var)
|
||||
|
||||
components
|
||||
:: Var v => [(v, ABT.Term f v a)] -> [[(v, ABT.Term f v a)]]
|
||||
components = components' ABT.freeVars
|
||||
|
||||
-- | Order bindings by dependencies and group into components.
|
||||
-- Each component consists of > 1 bindings, each of which depends
|
||||
-- transitively on all other bindings in the component.
|
||||
--
|
||||
-- 1-element components may or may not depend on themselves.
|
||||
--
|
||||
-- The order is such that a component at index i will not depend
|
||||
-- on components and indexes > i. But a component at index i does not
|
||||
-- _necessarily_ depend on any components at earlier indices.
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- let rec
|
||||
-- ping n = pong (n + 1);
|
||||
-- pong n = ping (n + 1);
|
||||
-- g = id 42;
|
||||
-- y = id "hi"
|
||||
-- id x = x;
|
||||
-- in ping g
|
||||
--
|
||||
-- `components` would produce `[[ping,pong], [id], [g], [y]]`
|
||||
-- Notice that `id` comes before `g` and `y` in the output, since
|
||||
-- both `g` and `y` depend on `id`.
|
||||
--
|
||||
-- Uses Tarjan's algorithm:
|
||||
-- https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
components' :: Var v => (t -> Set v) -> [(v, t)] -> [[(v, t)]]
|
||||
components' freeVars bs =
|
||||
let varIds =
|
||||
Map.fromList (map fst bs `zip` reverse [(1 :: Int) .. length bs])
|
||||
-- something horribly wrong if this bombs
|
||||
varId v = fromJust $ Map.lookup v varIds
|
||||
|
||||
-- use ints as keys for graph to preserve original source order as much as
|
||||
-- possible
|
||||
graph = [ ((v, b), varId v, deps b) | (v, b) <- bs ]
|
||||
vars = Set.fromList (map fst bs)
|
||||
deps b = varId <$> Set.toList (Set.intersection vars (freeVars b))
|
||||
in Graph.flattenSCC <$> Graph.stronglyConnComp graph
|
||||
|
||||
-- | Algorithm for minimizing cycles of a `let rec`. This can
|
||||
-- improve generalization during typechecking and may also be more
|
||||
-- efficient for execution.
|
||||
@ -87,7 +41,7 @@ minimize (Term.LetRecNamedAnnotatedTop' isTop ann bs e) =
|
||||
in if not $ null dupes
|
||||
then Left $ Nel.fromList dupes
|
||||
else
|
||||
let cs = components bindings
|
||||
let cs = ABT.components bindings
|
||||
varAnnotations = Map.fromList ((\((a, v), _) -> (v, a)) <$> bs)
|
||||
annotationFor v = fromJust $ Map.lookup v varAnnotations
|
||||
annotatedVar v = (annotationFor v, v)
|
||||
|
48
parser-typechecker/src/Unison/Util/Components.hs
Normal file
48
parser-typechecker/src/Unison/Util/Components.hs
Normal file
@ -0,0 +1,48 @@
|
||||
module Unison.Util.Components where
|
||||
|
||||
import qualified Data.Graph as Graph
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
import Unison.Var (Var)
|
||||
|
||||
-- | Order bindings by dependencies and group into components.
|
||||
-- Each component consists of > 1 bindings, each of which depends
|
||||
-- transitively on all other bindings in the component.
|
||||
--
|
||||
-- 1-element components may or may not depend on themselves.
|
||||
--
|
||||
-- The order is such that a component at index i will not depend
|
||||
-- on components and indexes > i. But a component at index i does not
|
||||
-- _necessarily_ depend on any components at earlier indices.
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- let rec
|
||||
-- ping n = pong (n + 1);
|
||||
-- pong n = ping (n + 1);
|
||||
-- g = id 42;
|
||||
-- y = id "hi"
|
||||
-- id x = x;
|
||||
-- in ping g
|
||||
--
|
||||
-- `components` would produce `[[ping,pong], [id], [g], [y]]`
|
||||
-- Notice that `id` comes before `g` and `y` in the output, since
|
||||
-- both `g` and `y` depend on `id`.
|
||||
--
|
||||
-- Uses Tarjan's algorithm:
|
||||
-- https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
components :: Var v => (t -> Set v) -> [(v, t)] -> [[(v, t)]]
|
||||
components freeVars bs =
|
||||
let varIds =
|
||||
Map.fromList (map fst bs `zip` reverse [(1 :: Int) .. length bs])
|
||||
-- something horribly wrong if this bombs
|
||||
varId v = fromJust $ Map.lookup v varIds
|
||||
|
||||
-- use ints as keys for graph to preserve original source order as much as
|
||||
-- possible
|
||||
graph = [ ((v, b), varId v, deps b) | (v, b) <- bs ]
|
||||
vars = Set.fromList (map fst bs)
|
||||
deps b = varId <$> Set.toList (Set.intersection vars (freeVars b))
|
||||
in Graph.flattenSCC <$> Graph.stronglyConnComp graph
|
@ -87,6 +87,7 @@ library
|
||||
Unison.UnisonFile
|
||||
Unison.Util.AnnotatedText
|
||||
Unison.Util.ColorText
|
||||
Unison.Util.Components
|
||||
Unison.Util.Logger
|
||||
Unison.Util.Menu
|
||||
Unison.Util.Monoid
|
||||
|
Loading…
Reference in New Issue
Block a user