diff --git a/.gitignore b/.gitignore index fb1c996a9..d1e036b45 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,6 @@ cabal-dev *.un~ **/cache/** **/build/** -store -codestore -codebase tags unison-src/.loaded **cabal.sandbox.config @@ -20,6 +17,8 @@ blockstore blockstore.leveldb target htags +scala-tags +haskell-tags # Stack .stack-work diff --git a/parser-typechecker/src/Unison/Codebase.hs b/parser-typechecker/src/Unison/Codebase.hs new file mode 100644 index 000000000..b0ac5f5af --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase.hs @@ -0,0 +1,24 @@ +module Unison.Codebase where + +import qualified Unison.Term as Term +import qualified Unison.DataDeclaration as DD +import Unison.Reference (Reference) +import Unison.Codebase.Code (Code) +import Unison.Codebase.Branch (Branch) +import Unison.Codebase.Release (Release) + +type DataDeclaration v a = DD.DataDeclaration' v a +type Term v a = Term.AnnotatedTerm v a +type Name = String + +data Codebase m v a = + Codebase { getCode :: Reference -> m (Maybe (Code v a)) + , putCode :: Code v a -> m Reference + , branches :: m [Name] + , getBranch :: Name -> m (Maybe (Branch v a)) + , putBranch :: Name -> Branch v a -> m () + , releases :: m [Name] + , getRelease :: Name -> m (Maybe (Release v a)) + , putRelease :: Name -> Release v a -> m () + } + diff --git a/parser-typechecker/src/Unison/Codebase/Branch.hs b/parser-typechecker/src/Unison/Codebase/Branch.hs new file mode 100644 index 000000000..4f7296a71 --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Branch.hs @@ -0,0 +1,18 @@ +module Unison.Codebase.Branch where + +import Data.Map (Map) +import Unison.Codebase.Causal (Causal) +import Unison.Codebase.Conflicted (Conflicted) +import Unison.Codebase.Name (Name) +import Unison.Codebase.NameEdit (NameEdit) +import Unison.Codebase.TermEdit (TermEdit) +import Unison.Codebase.TypeEdit (TypeEdit) +import Unison.Reference (Reference) + +data Branch v a = + Branch { namespace :: Map Name (Causal NameEdit) + , edited :: Map Reference (Causal (Conflicted TermEdit)) + , editedDatas :: Map Reference (Causal (Conflicted TypeEdit)) + , editedEffects :: Map Reference (Causal (Conflicted TypeEdit)) + } + diff --git a/parser-typechecker/src/Unison/Codebase/Causal.hs b/parser-typechecker/src/Unison/Codebase/Causal.hs new file mode 100644 index 000000000..91e4fd5b7 --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Causal.hs @@ -0,0 +1,45 @@ +module Unison.Codebase.Causal where + +import Prelude hiding (head, sequence) +import Data.List (sort) +import Unison.Hash (Hash) +import qualified Unison.Hashable as Hashable +import Unison.Hashable (Hashable) + +data Causal e + = One { currentHash :: Hash, head :: e } + | Cons { currentHash :: Hash, head :: e, tail :: Causal e } + | Merge { currentHash :: Hash, head :: e, tail1 :: Causal e, tail2 :: Causal e } + +instance Semigroup e => Semigroup (Causal e) where + a <> b + | before a b = b + | before b a = a + | otherwise = Merge (mixHashes [currentHash a, currentHash b]) + (head a <> head b) a b + +hash :: Hashable e => e -> Hash +hash = Hashable.accumulate' + +-- commutative combine of a list of hashes +mixHashes :: [Hash] -> Hash +mixHashes = hash . sort + +one :: Hashable e => e -> Causal e +one e = One (hash e) e + +cons :: Hashable e => e -> Causal e -> Causal e +cons e tl = Cons (hash [hash e, currentHash tl]) e tl + +sequence :: (Semigroup e, Hashable e) => Causal e -> Causal e -> Causal e +sequence a (One _ e) = cons e a +sequence a (Cons _ e tl) = cons e (sequence a tl) +sequence a (Merge _ _ l r) = sequence a l <> r +-- note: if causal had a `split` operation, we'd need to sequence on both sides + +-- Does `h2` incorporate all of `h1`? +before :: Causal e -> Causal e -> Bool +before h1 h2 = go (currentHash h1) h2 where + go h1 (One h _) = h == h1 + go h1 (Cons h _ tl) = h == h1 || go h1 tl + go h1 (Merge h _ left right) = h == h1 || go h1 left || go h1 right diff --git a/parser-typechecker/src/Unison/Codebase/Code.hs b/parser-typechecker/src/Unison/Codebase/Code.hs new file mode 100644 index 000000000..4325d1bcd --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Code.hs @@ -0,0 +1,12 @@ +module Unison.Codebase.Code where + +import qualified Unison.Term as Term +import qualified Unison.DataDeclaration as DD + +type DataDeclaration v a = DD.DataDeclaration' v a +type Term v a = Term.AnnotatedTerm v a + +data Code v a + = Term (Term v a) + | DataDeclaration (DataDeclaration v a) + diff --git a/parser-typechecker/src/Unison/Codebase/Conflicted.hs b/parser-typechecker/src/Unison/Codebase/Conflicted.hs new file mode 100644 index 000000000..52bd00658 --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Conflicted.hs @@ -0,0 +1,12 @@ +module Unison.Codebase.Conflicted where + +import qualified Data.Set as Set +import Data.Set (Set) + +data Conflicted a = One a | Many (Set a) + +instance Ord a => Semigroup (Conflicted a) where + One a <> One a2 = if a == a2 then One a else Many (Set.fromList [a,a2]) + One a <> Many as = Many (Set.insert a as) + Many as <> One a = Many (Set.insert a as) + Many as <> Many as2 = Many (as `Set.union` as2) diff --git a/parser-typechecker/src/Unison/Codebase/Name.hs b/parser-typechecker/src/Unison/Codebase/Name.hs new file mode 100644 index 000000000..1c041eac5 --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Name.hs @@ -0,0 +1,3 @@ +module Unison.Codebase.Name where + +type Name = String diff --git a/parser-typechecker/src/Unison/Codebase/NameEdit.hs b/parser-typechecker/src/Unison/Codebase/NameEdit.hs new file mode 100644 index 000000000..9e257a32a --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/NameEdit.hs @@ -0,0 +1,7 @@ +module Unison.Codebase.NameEdit where + +import Data.Set (Set) +import Unison.Reference (Reference) + +data NameEdit = + NameEdit { added :: Set Reference, removed :: Set Reference } diff --git a/parser-typechecker/src/Unison/Codebase/Release.hs b/parser-typechecker/src/Unison/Codebase/Release.hs new file mode 100644 index 000000000..148008993 --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/Release.hs @@ -0,0 +1,21 @@ +module Unison.Codebase.Release where + +import Data.Map (Map) +import Unison.Codebase.Code (Code) +import Unison.Codebase.Name (Name) +import Unison.Codebase.TermEdit (TermEdit) +import Unison.Codebase.TypeEdit (TypeEdit) +import Unison.Reference (Reference) +import qualified Unison.DataDeclaration as DD +import qualified Unison.Term as Term + +type DataDeclaration v a = DD.DataDeclaration' v a +type Term v a = Term.AnnotatedTerm v a + +data Release v a = + Release { namespace :: Map Name (Code v a) + , edited :: Map Reference TermEdit + , editedDatas :: Map Reference TypeEdit + , editedEffects :: Map Reference TypeEdit } + + diff --git a/parser-typechecker/src/Unison/Codebase/TermEdit.hs b/parser-typechecker/src/Unison/Codebase/TermEdit.hs new file mode 100644 index 000000000..060e50e9c --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/TermEdit.hs @@ -0,0 +1,10 @@ +module Unison.Codebase.TermEdit where + +import Unison.Reference (Reference) + +data TermEdit = Replace Reference Typing | Deprecate + +-- Replacements with the Same type can be automatically propagated. +-- Replacements with a Subtype can be automatically propagated but may result in dependents getting more general types, so requires re-inference. +-- Replacements of a Different type need to be manually propagated by the programmer. +data Typing = Same | Subtype | Different diff --git a/parser-typechecker/src/Unison/Codebase/TypeEdit.hs b/parser-typechecker/src/Unison/Codebase/TypeEdit.hs new file mode 100644 index 000000000..1eaed639e --- /dev/null +++ b/parser-typechecker/src/Unison/Codebase/TypeEdit.hs @@ -0,0 +1,5 @@ +module Unison.Codebase.TypeEdit where + +import Unison.Reference (Reference) + +data TypeEdit = Replace Reference | Deprecated diff --git a/parser-typechecker/src/Unison/Hash.hs b/parser-typechecker/src/Unison/Hash.hs index d1d816da8..eb5a374c7 100644 --- a/parser-typechecker/src/Unison/Hash.hs +++ b/parser-typechecker/src/Unison/Hash.hs @@ -23,6 +23,9 @@ newtype Hash = Hash { toBytes :: ByteString } deriving (Eq,Ord,Generic) instance Show Hash where show h = take 8 $ Text.unpack (base58 h) +instance H.Hashable Hash where + tokens h = [H.Bytes (toBytes h)] + fromBytesImpl :: ByteString -> Hash fromBytesImpl = fromBytes diff --git a/parser-typechecker/unison-parser-typechecker.cabal b/parser-typechecker/unison-parser-typechecker.cabal index 0e65b43f2..7398139d0 100644 --- a/parser-typechecker/unison-parser-typechecker.cabal +++ b/parser-typechecker/unison-parser-typechecker.cabal @@ -38,6 +38,16 @@ library Unison.Blank Unison.Builtin Unison.Codecs + Unison.Codebase + Unison.Codebase.Branch + Unison.Codebase.Causal + Unison.Codebase.Code + Unison.Codebase.Conflicted + Unison.Codebase.Name + Unison.Codebase.NameEdit + Unison.Codebase.Release + Unison.Codebase.TermEdit + Unison.Codebase.TypeEdit Unison.DataDeclaration Unison.FileParser Unison.FileParsers