Merge branch 'trunk' into cp/project-root

# Conflicts:
#	unison-cli-integration/integration-tests/IntegrationTests/transcript.output.md
#	unison-src/transcripts-round-trip/main.output.md
#	unison-src/transcripts/add-run.output.md
#	unison-src/transcripts/bug-strange-closure.output.md
#	unison-src/transcripts/cycle-update-5.output.md
#	unison-src/transcripts/delete.output.md
#	unison-src/transcripts/diff-namespace.output.md
#	unison-src/transcripts/move-namespace.output.md
#	unison-src/transcripts/name-selection.output.md
#	unison-src/transcripts/names.output.md
#	unison-src/transcripts/namespace-dependencies.output.md
#	unison-src/transcripts/propagate.output.md
#	unison-src/transcripts/reflog.output.md
#	unison-src/transcripts/reset.output.md
#	unison-src/transcripts/tab-completion.output.md
#	unison-src/transcripts/transcript-parser-commands.output.md
This commit is contained in:
Arya Irani 2024-07-10 23:49:04 -04:00
commit 0cd3cd1cff
307 changed files with 3415 additions and 2497 deletions

View File

@ -178,6 +178,7 @@ invertDomain =
g x acc y =
Map.insert y x acc
-- | Construct a left-unique relation from a mapping from its right-elements to its left-elements.
fromRange :: (Ord a, Ord b) => Map b a -> BiMultimap a b
fromRange m =
BiMultimap (Map.foldlWithKey' f Map.empty m) m

View File

@ -139,6 +139,7 @@ import Unison.NameSegment (NameSegment)
import Unison.NameSegment qualified as NameSegment
import Unison.Prelude hiding (empty)
import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId)
import Unison.Reference qualified as Reference
import Unison.Referent (Referent)
import Unison.Referent qualified as Referent
import Unison.Util.List qualified as List
@ -148,7 +149,6 @@ import Unison.Util.Set qualified as Set
import Unison.Util.Star2 qualified as Star2
import Witherable (FilterableWithIndex (imapMaybe))
import Prelude hiding (head, read, subtract)
import qualified Unison.Reference as Reference
instance AsEmpty (Branch m) where
_Empty = prism' (const empty) matchEmpty
@ -215,7 +215,6 @@ deepTypeReferenceIds :: Branch0 m -> Set TypeReferenceId
deepTypeReferenceIds =
Set.mapMaybe Reference.toId . deepTypeReferences
namespaceStats :: Branch0 m -> NamespaceStats
namespaceStats b =
NamespaceStats

View File

@ -1,7 +1,7 @@
{-# LANGUAGE TemplateHaskell #-}
module Unison.Codebase.Causal
( Causal (currentHash, head, tail, tails),
( Causal (currentHash, valueHash, head, tail, tails),
pattern One,
pattern Cons,
pattern Merge,
@ -40,7 +40,8 @@ import Unison.Codebase.Causal.Type
currentHash,
head,
tail,
tails
tails,
valueHash
),
before,
lca,

View File

@ -19,6 +19,7 @@ dependencies:
- base
- bytes
- bytestring
- cmark
- co-log-core
- code-page
- concurrent-output

View File

@ -9,6 +9,9 @@ module Unison.Codebase.Editor.HandleInput.Merge2
LcaMergeInfo (..),
doMerge,
doMergeLocalBranch,
-- * API exported for @todo@
hasDefnsInLib,
)
where
@ -241,11 +244,7 @@ doMerge info = do
-- Assert that neither Alice nor Bob have defns in lib
for_ [(mergeTarget, branches.alice), (mergeSource, branches.bob)] \(who, branch) -> do
libdeps <-
case Map.lookup NameSegment.libSegment branch.children of
Nothing -> pure V2.Branch.empty
Just libdeps -> Cli.runTransaction libdeps.value
when (not (Map.null libdeps.terms) || not (Map.null libdeps.types)) do
whenM (Cli.runTransaction (hasDefnsInLib branch)) do
done (Output.MergeDefnsInLib who)
-- Load Alice/Bob/LCA definitions and decl name lookups
@ -487,6 +486,17 @@ loadLibdeps branches = do
libdepsBranch <- libdepsCausal.value
pure libdepsBranch.children
------------------------------------------------------------------------------------------------------------------------
-- Merge precondition violation checks
hasDefnsInLib :: Applicative m => V2.Branch m -> m Bool
hasDefnsInLib branch = do
libdeps <-
case Map.lookup NameSegment.libSegment branch.children of
Nothing -> pure V2.Branch.empty
Just libdeps -> libdeps.value
pure (not (Map.null libdeps.terms) || not (Map.null libdeps.types))
------------------------------------------------------------------------------------------------------------------------
-- Creating Unison files

View File

@ -4,7 +4,9 @@ module Unison.Codebase.Editor.HandleInput.Todo
)
where
import Data.Either qualified as Either
import Data.Set qualified as Set
import U.Codebase.HashTags (BranchHash (..))
import U.Codebase.Sqlite.Operations qualified as Operations
import Unison.Builtin qualified as Builtin
import Unison.Cli.Monad (Cli)
@ -14,7 +16,11 @@ import Unison.Cli.PrettyPrintUtils qualified as Cli
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Branch qualified as Branch
import Unison.Codebase.Branch.Names qualified as Branch
import Unison.Codebase.Causal qualified as Causal
import Unison.Codebase.Editor.HandleInput.Merge2 (hasDefnsInLib)
import Unison.Codebase.Editor.Output
import Unison.Hash (HashFor (..))
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..), checkAllDeclCoherency)
import Unison.Names qualified as Names
import Unison.Prelude
import Unison.Reference (TermReference)
@ -26,11 +32,22 @@ handleTodo :: Cli ()
handleTodo = do
-- For now, we don't go through any great trouble to seek out the root of the project branch. Just assume the current
-- namespace is the root, which will be the case unless the user uses `deprecated.cd`.
currentNamespace <- Cli.getCurrentBranch0
currentCausal <- Cli.getCurrentBranch
let currentNamespace = Branch.head currentCausal
let currentNamespaceWithoutLibdeps = Branch.deleteLibdeps currentNamespace
(dependentsOfTodo, directDependencies, hashLen) <-
(defnsInLib, dependentsOfTodo, directDependencies, hashLen, incoherentDeclReasons) <-
Cli.runTransaction do
-- We call a shared `hasDefnsLib` helper even though we could easily duplicate the logic with the branch in hand
defnsInLib <- do
branch <-
currentCausal
& Branch._history
& Causal.valueHash
& coerce @_ @BranchHash
& Operations.expectBranchByBranchHash
hasDefnsInLib branch
let todoReference :: TermReference
todoReference =
Set.asSingleton (Names.refTermsNamed Builtin.names (Name.unsafeParseText "todo"))
@ -51,20 +68,28 @@ handleTodo = do
hashLen <- Codebase.hashLength
pure (dependentsOfTodo.terms, directDependencies, hashLen)
incoherentDeclReasons <-
fmap (Either.fromLeft (IncoherentDeclReasons [] [] [] [])) $
checkAllDeclCoherency
Operations.expectDeclNumConstructors
(Names.lenientToNametree (Branch.toNames currentNamespaceWithoutLibdeps))
pure (defnsInLib, dependentsOfTodo.terms, directDependencies, hashLen, incoherentDeclReasons)
ppe <- Cli.currentPrettyPrintEnvDecl
Cli.respondNumbered $
Output'Todo
TodoOutput
{ hashLen,
{ defnsInLib,
dependentsOfTodo,
directDependenciesWithoutNames =
Defns
{ terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace),
types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace)
},
hashLen,
incoherentDeclReasons,
nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps),
ppe
}

View File

@ -57,6 +57,7 @@ import Unison.Hash (Hash)
import Unison.HashQualified qualified as HQ
import Unison.HashQualified' qualified as HQ'
import Unison.LabeledDependency (LabeledDependency)
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..))
import Unison.Name (Name)
import Unison.NameSegment (NameSegment)
import Unison.Names (Names)
@ -161,9 +162,11 @@ data NumberedOutput
[ProjectReflog.Entry Project ProjectBranch (CausalHash, SCH.ShortCausalHash)]
data TodoOutput = TodoOutput
{ dependentsOfTodo :: !(Set TermReferenceId),
{ defnsInLib :: !Bool,
dependentsOfTodo :: !(Set TermReferenceId),
directDependenciesWithoutNames :: !(DefnsF Set TermReference TypeReference),
hashLen :: !Int,
incoherentDeclReasons :: !IncoherentDeclReasons,
nameConflicts :: !Names,
ppe :: !PrettyPrintEnvDecl
}
@ -173,6 +176,8 @@ todoOutputIsEmpty todo =
Set.null todo.dependentsOfTodo
&& defnsAreEmpty todo.directDependenciesWithoutNames
&& Names.isEmpty todo.nameConflicts
&& not todo.defnsInLib
&& todo.incoherentDeclReasons == IncoherentDeclReasons [] [] [] []
data AmbiguousReset'Argument
= AmbiguousReset'Hash

View File

@ -18,6 +18,7 @@ module Unison.Codebase.TranscriptParser
)
where
import CMark qualified
import Control.Lens (use, (?~))
import Crypto.Random qualified as Random
import Data.Aeson qualified as Aeson
@ -118,12 +119,14 @@ instance Show APIRequest where
show (GetRequest txt) = "GET " <> Text.unpack txt
show (APIComment txt) = "-- " <> Text.unpack txt
pattern CMarkCodeBlock :: (Maybe CMark.PosInfo) -> Text -> Text -> CMark.Node
pattern CMarkCodeBlock pos info body = CMark.Node pos (CMark.CODE_BLOCK info body) []
data Stanza
= Ucm Hidden ExpectingError [UcmLine]
| Unison Hidden ExpectingError (Maybe ScratchFileName) Text
| API [APIRequest]
| UnprocessedFence FenceType Text
| Unfenced Text
| UnprocessedBlock CMark.Node
instance Show UcmLine where
show = \case
@ -133,43 +136,34 @@ instance Show UcmLine where
showContext (UcmContextProject projectAndBranch) = Text.unpack (into @Text projectAndBranch)
instance Show Stanza where
show s = case s of
show s = (<> "\n") . Text.unpack . CMark.nodeToCommonmark [] Nothing $ stanzaToNode s
stanzaToNode :: Stanza -> CMark.Node
stanzaToNode =
\case
Ucm _ _ cmds ->
unlines
[ "```ucm",
foldl (\x y -> x ++ show y) "" cmds,
"```"
]
CMarkCodeBlock Nothing "ucm" . Text.pack $
foldl (\x y -> x ++ show y) "" cmds
Unison _hide _ fname txt ->
CMarkCodeBlock Nothing "unison" . Text.pack $
unlines
[ "```unison",
case fname of
Nothing -> Text.unpack txt <> "```\n"
[ case fname of
Nothing -> Text.unpack txt
Just fname ->
unlines
[ "---",
"title: " <> Text.unpack fname,
"---",
Text.unpack txt,
"```",
""
Text.unpack txt
]
]
API apiRequests ->
"```api\n"
<> ( apiRequests
CMarkCodeBlock Nothing "api" . Text.pack $
( apiRequests
& fmap show
& unlines
)
<> "```\n"
UnprocessedFence typ txt ->
unlines
[ "```" <> Text.unpack typ,
Text.unpack txt,
"```",
""
]
Unfenced txt -> Text.unpack txt
UnprocessedBlock node -> node
parseFile :: FilePath -> IO (Either TranscriptError [Stanza])
parseFile filePath = do
@ -181,7 +175,7 @@ parseFile filePath = do
else pure . Left . TranscriptParseError . Text.pack $ filePath <> " does not exist"
parse :: String -> Text -> Either TranscriptError [Stanza]
parse srcName txt = case P.parse (stanzas <* P.eof) srcName txt of
parse srcName txt = case stanzas srcName txt of
Right a -> Right a
Left e -> Left . TranscriptParseError . Text.pack . P.errorBundlePretty $ e
@ -333,7 +327,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
for (reverse scratchFileUpdates) \(fp, contents) -> do
let fenceDescription = "unison:added-by-ucm " <> fp
-- Output blocks for any scratch file updates the ucm block triggered.
Q.undequeue inputQueue (UnprocessedFence fenceDescription contents, Nothing)
Q.undequeue inputQueue (UnprocessedBlock $ CMarkCodeBlock Nothing fenceDescription contents, Nothing)
awaitInput
-- ucm command to run
Just (Just ucmLine) -> do
@ -414,10 +408,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
++ "."
IO.hFlush IO.stdout
case s of
Unfenced _ -> do
liftIO (output $ show s)
awaitInput
UnprocessedFence _ _ -> do
UnprocessedBlock _ -> do
liftIO (output $ show s)
awaitInput
Unison hide errOk filename txt -> do
@ -435,7 +426,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
API apiRequests -> do
liftIO (output "``` api\n")
liftIO (for_ apiRequests apiRequest)
liftIO (output "```")
liftIO (output "```\n\n")
awaitInput
Ucm hide errOk cmds -> do
liftIO (writeIORef hidden hide)
@ -588,8 +579,12 @@ transcriptFailure out msg = do
type P = P.Parsec Void Text
stanzas :: P [Stanza]
stanzas = P.many (fenced <|> unfenced)
stanzas :: String -> Text -> Either (P.ParseErrorBundle Text Void) [Stanza]
stanzas srcName = (\(CMark.Node _ _DOCUMENT blocks) -> traverse stanzaFromBlock blocks) . CMark.commonmarkToNode []
where
stanzaFromBlock block = case block of
CMarkCodeBlock _ info body -> fromMaybe (UnprocessedBlock block) <$> P.parse (fenced info) srcName body
_ -> pure $ UnprocessedBlock block
ucmLine :: P UcmLine
ucmLine = ucmCommand <|> ucmComment
@ -630,18 +625,21 @@ apiRequest = do
spaces
pure (APIComment comment)
fenced :: P Stanza
fenced = do
fence
-- | Produce the correct parser for the code block based on the provided info string.
fenced :: Text -> P (Maybe Stanza)
fenced info = do
body <- P.getInput
P.setInput info
fenceType <- lineToken (word "ucm" <|> word "unison" <|> word "api" <|> language)
stanza <-
case fenceType of
"ucm" -> do
hide <- hidden
err <- expectingError
P.setInput body
_ <- spaces
cmds <- many ucmLine
pure $ Ucm hide err cmds
pure . pure $ Ucm hide err cmds
"unison" ->
do
-- todo: this has to be more interesting
@ -651,44 +649,17 @@ fenced = do
hide <- lineToken hidden
err <- lineToken expectingError
fileName <- optional untilSpace1
blob <- spaces *> untilFence
pure $ Unison hide err fileName blob
P.setInput body
blob <- spaces *> (Text.init <$> P.getInput)
pure . pure $ Unison hide err fileName blob
"api" -> do
P.setInput body
_ <- spaces
apiRequests <- many apiRequest
pure $ API apiRequests
_ -> UnprocessedFence fenceType <$> untilFence
fence
pure . pure $ API apiRequests
_ -> pure Nothing
pure stanza
-- Three backticks, consumes trailing spaces too
-- ```
fence :: P ()
fence = P.try $ do void (word "```"); spaces
-- Parses up until next fence
unfenced :: P Stanza
unfenced = Unfenced <$> untilFence
untilFence :: P Text
untilFence = do
_ <- P.lookAhead (P.takeP Nothing 1)
go mempty
where
go :: Seq Text -> P Text
go !acc = do
f <- P.lookAhead (P.optional fence)
case f of
Nothing -> do
oneOrTwoBackticks <- optional (word' "``" <|> word' "`")
let start = fromMaybe "" oneOrTwoBackticks
txt <- P.takeWhileP (Just "unfenced") (/= '`')
eof <- P.lookAhead (P.optional P.eof)
case eof of
Just _ -> pure $ fold (acc <> pure txt)
Nothing -> go (acc <> pure start <> pure txt)
Just _ -> pure $ fold acc
word' :: Text -> P Text
word' txt = P.try $ do
chs <- P.takeP (Just $ show txt) (Text.length txt)

View File

@ -85,6 +85,7 @@ import Unison.Hash32 (Hash32)
import Unison.HashQualified qualified as HQ
import Unison.HashQualified' qualified as HQ'
import Unison.LabeledDependency as LD
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..))
import Unison.Name (Name)
import Unison.Name qualified as Name
import Unison.NameSegment qualified as NameSegment
@ -1405,6 +1406,7 @@ notifyUser dir = \case
<> "the same on both branches, or making neither of them a builtin, and then try the merge again."
)
]
-- Note [ConstructorAliasMessage] If you change this, also change the other similar one
MergeConstructorAlias aliceOrBob typeName conName1 conName2 ->
pure . P.lines $
[ P.wrap "Sorry, I wasn't able to perform the merge:",
@ -1420,6 +1422,7 @@ notifyUser dir = \case
"",
P.wrap "Please delete all but one name for each constructor, and then try merging again."
]
-- Note [DefnsInLibMessage] If you change this, also change the other similar one
MergeDefnsInLib aliceOrBob ->
pure . P.lines $
[ P.wrap "Sorry, I wasn't able to perform the merge:",
@ -1432,6 +1435,7 @@ notifyUser dir = \case
"",
P.wrap "Please move or remove it and then try merging again."
]
-- Note [MissingConstructorNameMessage] If you change this, also change the other similar one
MergeMissingConstructorName aliceOrBob name ->
pure . P.lines $
[ P.wrap "Sorry, I wasn't able to perform the merge:",
@ -1450,6 +1454,7 @@ notifyUser dir = \case
<> IP.makeExample IP.aliasTerm ["<hash>", prettyName name <> ".<ConstructorName>"]
<> "to give names to each unnamed constructor, and then try the merge again."
]
-- Note [NestedDeclAliasMessage] If you change this, also change the other similar one
MergeNestedDeclAlias aliceOrBob shorterName longerName ->
pure . P.wrap $
"On"
@ -1460,6 +1465,7 @@ notifyUser dir = \case
<> P.group (prettyName shorterName <> ".")
<> "I'm not able to perform a merge when a type exists nested under an alias of itself. Please separate them or"
<> "delete one copy, and then try merging again."
-- Note [StrayConstructorMessage] If you change this, also change the other similar one
MergeStrayConstructor aliceOrBob name ->
pure . P.lines $
[ P.wrap $
@ -2523,20 +2529,20 @@ unsafePrettyTermResultSig' ppe = \case
head (TypePrinter.prettySignaturesCT ppe [(r, name, typ)])
_ -> error "Don't pass Nothing"
renderNameConflicts :: PPE.PrettyPrintEnv -> Names -> Numbered Pretty
renderNameConflicts ppe conflictedNames = do
renderNameConflicts :: Int -> Names -> Numbered Pretty
renderNameConflicts hashLen conflictedNames = do
let conflictedTypeNames :: Map Name [HQ.HashQualified Name]
conflictedTypeNames =
conflictedNames
& Names.types
& R.domain
& fmap (foldMap (pure @[] . PPE.typeName ppe))
& Map.mapWithKey \name -> map (HQ.take hashLen . HQ.HashQualified name . Reference.toShortHash) . Set.toList
let conflictedTermNames :: Map Name [HQ.HashQualified Name]
conflictedTermNames =
conflictedNames
& Names.terms
& R.domain
& fmap (foldMap (pure @[] . PPE.termName ppe))
& Map.mapWithKey \name -> map (HQ.take hashLen . HQ.HashQualified name . Referent.toShortHash) . Set.toList
let allConflictedNames :: [Name]
allConflictedNames = Set.toList (Map.keysSet conflictedTermNames <> Map.keysSet conflictedTypeNames)
prettyConflictedTypes <- showConflictedNames "type" conflictedTypeNames
@ -2569,7 +2575,8 @@ renderNameConflicts ppe conflictedNames = do
prettyConflicts <- for hashes \hash -> do
n <- addNumberedArg $ SA.HashQualified hash
pure $ formatNum n <> (P.blue . P.syntaxToColor . prettyHashQualified $ hash)
pure . P.wrap $
pure $
P.wrap
( "The "
<> thingKind
<> " "
@ -2601,11 +2608,6 @@ handleTodoOutput :: TodoOutput -> Numbered Pretty
handleTodoOutput todo
| todoOutputIsEmpty todo = pure "You have no pending todo items. Good work! ✅"
| otherwise = do
prettyConflicts <-
if todo.nameConflicts == mempty
then pure mempty
else renderNameConflicts todo.ppe.unsuffixifiedPPE todo.nameConflicts
prettyDependentsOfTodo <- do
if Set.null todo.dependentsOfTodo
then pure mempty
@ -2654,11 +2656,159 @@ handleTodoOutput todo
<> P.newline
<> P.indentN 2 (P.lines types)
prettyConflicts <-
if todo.nameConflicts == mempty
then pure mempty
else renderNameConflicts todo.hashLen todo.nameConflicts
let prettyDefnsInLib =
if todo.defnsInLib
then
P.wrap $
-- Note [DefnsInLibMessage] If you change this, also change the other similar one
"There's a type or term at the top level of the `lib` namespace, where I only expect to find"
<> "subnamespaces representing library dependencies. Please move or remove it."
else mempty
prettyConstructorAliases <-
let -- We want to filter out constructor aliases whose types are part of a "nested decl alias" problem, because
-- otherwise we'd essentially be reporting those issues twice.
--
-- That is, if we have two nested aliases like
--
-- Foo = #XYZ
-- Foo.Bar = #XYZ#0
--
-- Foo.inner.Alias = #XYZ
-- Foo.inner.Alias.Constructor = #XYZ#0
--
-- then we'd prefer to say "oh no Foo and Foo.inner.Alias are aliases" but *not* additionally say "oh no
-- Foo.Bar and Foo.inner.Alias.Constructor are aliases".
notNestedDeclAlias (typeName, _, _) =
foldr
(\(short, long) acc -> typeName /= short && typeName /= long && acc)
True
todo.incoherentDeclReasons.nestedDeclAliases
in case filter notNestedDeclAlias todo.incoherentDeclReasons.constructorAliases of
[] -> pure mempty
aliases -> do
things <-
for aliases \(typeName, conName1, conName2) -> do
n1 <- addNumberedArg (SA.Name conName1)
n2 <- addNumberedArg (SA.Name conName2)
pure (typeName, formatNum n1 <> prettyName conName1, formatNum n2 <> prettyName conName2)
pure $
things
& map
( \(typeName, prettyCon1, prettyCon2) ->
-- Note [ConstructorAliasMessage] If you change this, also change the other similar one
P.wrap ("The type" <> prettyName typeName <> "has a constructor with multiple names.")
<> P.newline
<> P.newline
<> P.indentN 2 (P.lines [prettyCon1, prettyCon2])
<> P.newline
<> P.newline
<> P.wrap "Please delete all but one name for each constructor."
)
& P.sep "\n\n"
prettyMissingConstructorNames <-
case NEList.nonEmpty todo.incoherentDeclReasons.missingConstructorNames of
Nothing -> pure mempty
Just types0 -> do
stuff <-
for types0 \typ -> do
n <- addNumberedArg (SA.Name typ)
pure (n, typ)
-- Note [MissingConstructorNameMessage] If you change this, also change the other similar one
pure $
P.wrap
"These types have some constructors with missing names."
<> P.newline
<> P.newline
<> P.indentN 2 (P.lines (fmap (\(n, typ) -> formatNum n <> prettyName typ) stuff))
<> P.newline
<> P.newline
<> P.wrap
( "You can use"
<> IP.makeExample
IP.view
[ let firstNum = fst (NEList.head stuff)
lastNum = fst (NEList.last stuff)
in if firstNum == lastNum
then P.string (show firstNum)
else P.string (show firstNum) <> "-" <> P.string (show lastNum)
]
<> "and"
<> IP.makeExample IP.aliasTerm ["<hash>", "<TypeName>.<ConstructorName>"]
<> "to give names to each unnamed constructor."
)
prettyNestedDeclAliases <-
case todo.incoherentDeclReasons.nestedDeclAliases of
[] -> pure mempty
aliases0 -> do
aliases1 <-
for aliases0 \(short, long) -> do
n1 <- addNumberedArg (SA.Name short)
n2 <- addNumberedArg (SA.Name long)
pure (formatNum n1 <> prettyName short, formatNum n2 <> prettyName long)
-- Note [NestedDeclAliasMessage] If you change this, also change the other similar one
pure $
aliases1
& map
( \(short, long) ->
P.wrap
( "These types are aliases, but one is nested under the other. Please separate them or delete"
<> "one copy."
)
<> P.newline
<> P.newline
<> P.indentN 2 (P.lines [short, long])
)
& P.sep "\n\n"
prettyStrayConstructors <-
case todo.incoherentDeclReasons.strayConstructors of
[] -> pure mempty
constructors -> do
nums <-
for constructors \constructor -> do
addNumberedArg (SA.Name constructor)
-- Note [StrayConstructorMessage] If you change this, also change the other similar one
pure $
P.wrap "These constructors are not nested beneath their corresponding type names:"
<> P.newline
<> P.newline
<> P.indentN
2
( P.lines
( zipWith
(\n constructor -> formatNum n <> prettyName constructor)
nums
constructors
)
)
<> P.newline
<> P.newline
<> P.wrap
( "For each one, please either use"
<> IP.makeExample' IP.moveAll
<> "to move if, or if it's an extra copy, you can simply"
<> IP.makeExample' IP.delete
<> "it."
)
(pure . P.sep "\n\n" . P.nonEmpty)
[ prettyDependentsOfTodo,
prettyDirectTermDependenciesWithoutNames,
prettyDirectTypeDependenciesWithoutNames,
prettyConflicts
prettyConflicts,
prettyDefnsInLib,
prettyConstructorAliases,
prettyMissingConstructorNames,
prettyNestedDeclAliases,
prettyStrayConstructors
]
listOfDefinitions ::

View File

@ -197,6 +197,7 @@ library
, base
, bytes
, bytestring
, cmark
, co-log-core
, code-page
, concurrent-output
@ -338,6 +339,7 @@ executable transcripts
, base
, bytes
, bytestring
, cmark
, co-log-core
, code-page
, concurrent-output
@ -486,6 +488,7 @@ test-suite cli-tests
, base
, bytes
, bytestring
, cmark
, co-log-core
, code-page
, concurrent-output

View File

@ -66,6 +66,7 @@ default-extensions:
- DerivingStrategies
- DerivingVia
- DoAndIfThenElse
- DuplicateRecordFields
- FlexibleContexts
- FlexibleInstances
- GADTs

View File

@ -49,12 +49,15 @@ module Unison.Names
hashQualifyTypesRelation,
hashQualifyTermsRelation,
fromTermsAndTypes,
lenientToNametree,
)
where
import Data.Map qualified as Map
import Data.Semialign (alignWith)
import Data.Set qualified as Set
import Data.Text qualified as Text
import Data.These (These (..))
import Text.FuzzyFind qualified as FZF
import Unison.ConstructorReference (GConstructorReference (..))
import Unison.ConstructorType qualified as CT
@ -64,6 +67,7 @@ import Unison.LabeledDependency (LabeledDependency)
import Unison.LabeledDependency qualified as LD
import Unison.Name (Name)
import Unison.Name qualified as Name
import Unison.NameSegment (NameSegment)
import Unison.Prelude
import Unison.Reference (Reference, TermReference, TypeReference)
import Unison.Reference qualified as Reference
@ -71,6 +75,10 @@ import Unison.Referent (Referent)
import Unison.Referent qualified as Referent
import Unison.ShortHash (ShortHash)
import Unison.ShortHash qualified as SH
import Unison.Util.BiMultimap (BiMultimap)
import Unison.Util.BiMultimap qualified as BiMultimap
import Unison.Util.Defns (Defns (..), DefnsF)
import Unison.Util.Nametree (Nametree, unflattenNametree)
import Unison.Util.Relation (Relation)
import Unison.Util.Relation qualified as R
import Unison.Util.Relation qualified as Relation
@ -95,7 +103,7 @@ instance Monoid (Names) where
mempty = Names mempty mempty
isEmpty :: Names -> Bool
isEmpty n = R.null (terms n) && R.null (types n)
isEmpty n = R.null n.terms && R.null n.types
map :: (Name -> Name) -> Names -> Names
map f (Names {terms, types}) = Names terms' types'
@ -122,8 +130,8 @@ fuzzyFind nameToText query names =
. Prelude.filter prefilter
. Map.toList
-- `mapMonotonic` is safe here and saves a log n factor
$ (Set.mapMonotonic Left <$> R.toMultimap (terms names))
<> (Set.mapMonotonic Right <$> R.toMultimap (types names))
$ (Set.mapMonotonic Left <$> R.toMultimap names.terms)
<> (Set.mapMonotonic Right <$> R.toMultimap names.types)
where
lowerqueryt = Text.toLower . Text.pack <$> query
-- For performance, case-insensitive substring matching as a pre-filter
@ -250,8 +258,8 @@ unionLeft' ::
Names
unionLeft' shouldOmit a b = Names terms' types'
where
terms' = foldl' go (terms a) (R.toList $ terms b)
types' = foldl' go (types a) (R.toList $ types b)
terms' = foldl' go a.terms (R.toList b.terms)
types' = foldl' go a.types (R.toList b.types)
go :: (Ord a, Ord b) => Relation a b -> (a, b) -> Relation a b
go acc (n, r) = if shouldOmit n r acc then acc else R.insert n r acc
@ -260,7 +268,7 @@ numHashChars :: Int
numHashChars = 3
termsNamed :: Names -> Name -> Set Referent
termsNamed = flip R.lookupDom . terms
termsNamed = flip R.lookupDom . (.terms)
-- | Get all terms with a specific name.
refTermsNamed :: Names -> Name -> Set TermReference
@ -281,13 +289,13 @@ refTermsHQNamed names = \case
in Set.mapMaybe f (termsNamed names name)
typesNamed :: Names -> Name -> Set TypeReference
typesNamed = flip R.lookupDom . types
typesNamed = flip R.lookupDom . (.types)
namesForReferent :: Names -> Referent -> Set Name
namesForReferent names r = R.lookupRan r (terms names)
namesForReferent names r = R.lookupRan r names.terms
namesForReference :: Names -> TypeReference -> Set Name
namesForReference names r = R.lookupRan r (types names)
namesForReference names r = R.lookupRan r names.types
termAliases :: Names -> Name -> Referent -> Set Name
termAliases names n r = Set.delete n $ namesForReferent names r
@ -422,20 +430,20 @@ filterTypes f (Names terms types) = Names terms (R.filterDom f types)
difference :: Names -> Names -> Names
difference a b =
Names
(R.difference (terms a) (terms b))
(R.difference (types a) (types b))
(R.difference a.terms b.terms)
(R.difference a.types b.types)
contains :: Names -> Reference -> Bool
contains names =
-- We want to compute `termsReferences` only once, if `contains` is partially applied to a `Names`, and called over
-- and over for different references. GHC would probably float `termsReferences` out without the explicit lambda, but
-- it's written like this just to be sure.
\r -> Set.member r termsReferences || R.memberRan r (types names)
\r -> Set.member r termsReferences || R.memberRan r names.types
where
-- this check makes `contains` O(n) instead of O(log n)
termsReferences :: Set TermReference
termsReferences =
Set.map Referent.toReference (R.ran (terms names))
Set.map Referent.toReference (R.ran names.terms)
-- | filters out everything from the domain except what's conflicted
conflicts :: Names -> Names
@ -448,9 +456,9 @@ conflicts Names {..} = Names (R.filterManyDom terms) (R.filterManyDom types)
-- See usage in `FileParser` for handling precendence of symbol
-- resolution where local names are preferred to codebase names.
shadowTerms :: [Name] -> Names -> Names
shadowTerms ns n0 = Names terms' (types n0)
shadowTerms ns n0 = Names terms' n0.types
where
terms' = foldl' go (terms n0) ns
terms' = foldl' go n0.terms ns
go ts name = R.deleteDom name ts
-- | Given a mapping from name to qualified name, update a `Names`,
@ -461,8 +469,8 @@ shadowTerms ns n0 = Names terms' (types n0)
importing :: [(Name, Name)] -> Names -> Names
importing shortToLongName ns =
Names
(foldl' go (terms ns) shortToLongName)
(foldl' go (types ns) shortToLongName)
(foldl' go ns.terms shortToLongName)
(foldl' go ns.types shortToLongName)
where
go :: (Ord r) => Relation Name r -> (Name, Name) -> Relation Name r
go m (shortname, qname) = case Name.searchByRankedSuffix qname m of
@ -476,8 +484,8 @@ importing shortToLongName ns =
-- `[(foo, io.foo), (bar, io.bar)]`.
expandWildcardImport :: Name -> Names -> [(Name, Name)]
expandWildcardImport prefix ns =
[(suffix, full) | Just (suffix, full) <- go <$> R.toList (terms ns)]
<> [(suffix, full) | Just (suffix, full) <- go <$> R.toList (types ns)]
[(suffix, full) | Just (suffix, full) <- go <$> R.toList ns.terms]
<> [(suffix, full) | Just (suffix, full) <- go <$> R.toList ns.types]
where
go :: (Name, a) -> Maybe (Name, Name)
go (full, _) = do
@ -498,7 +506,7 @@ constructorsForType r ns =
possibleDatas = [Referent.Con (ConstructorReference r cid) CT.Data | cid <- [0 ..]]
possibleEffects = [Referent.Con (ConstructorReference r cid) CT.Effect | cid <- [0 ..]]
trim [] = []
trim (h : t) = case R.lookupRan h (terms ns) of
trim (h : t) = case R.lookupRan h ns.terms of
s
| Set.null s -> []
| otherwise -> [(n, h) | n <- toList s] ++ trim t
@ -517,3 +525,29 @@ hashQualifyRelation fromNamedRef rel = R.map go rel
if Set.size (R.lookupDom n rel) > 1
then (HQ.take numHashChars $ fromNamedRef n r, r)
else (HQ.NameOnly n, r)
-- | "Leniently" view a Names as a NameTree
--
-- This function is "lenient" in the sense that it does not handle conflicted names with any smarts whatsoever. The
-- resulting nametree will simply contain one of the associated references of a conflicted name - we don't specify
-- which.
lenientToNametree :: Names -> Nametree (DefnsF (Map NameSegment) Referent TypeReference)
lenientToNametree names =
alignWith
( \case
This terms -> Defns {terms, types = Map.empty}
That types -> Defns {terms = Map.empty, types}
These terms types -> Defns {terms, types}
)
(lenientRelationToNametree names.terms)
(lenientRelationToNametree names.types)
where
lenientRelationToNametree :: Ord a => Relation Name a -> Nametree (Map NameSegment a)
lenientRelationToNametree =
unflattenNametree . lenientRelationToLeftUniqueRelation
lenientRelationToLeftUniqueRelation :: (Ord a, Ord b) => Relation a b -> BiMultimap b a
lenientRelationToLeftUniqueRelation =
-- The partial `Set.findMin` are fine here because Relation.domain only has non-empty Set values. A NESet would be
-- better.
BiMultimap.fromRange . Map.map Set.findMin . Relation.domain

View File

@ -72,6 +72,7 @@ library
DerivingStrategies
DerivingVia
DoAndIfThenElse
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
GADTs
@ -140,6 +141,7 @@ test-suite tests
DerivingStrategies
DerivingVia
DoAndIfThenElse
DuplicateRecordFields
FlexibleContexts
FlexibleInstances
GADTs

View File

@ -83,15 +83,17 @@ module Unison.Merge.DeclCoherencyCheck
( IncoherentDeclReason (..),
checkDeclCoherency,
lenientCheckDeclCoherency,
-- * Getting all failures rather than just the first
IncoherentDeclReasons (..),
checkAllDeclCoherency,
)
where
import Control.Lens ((%=), (.=), _2)
import Control.Monad.Except (ExceptT)
import Control.Monad.Except qualified as Except
import Control.Monad.State.Strict (StateT)
import Control.Monad.State.Strict qualified as State
import Control.Monad.Trans.Except qualified as Except (except)
import Data.Functor.Compose (Compose (..))
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
@ -136,72 +138,180 @@ checkDeclCoherency ::
(TypeReferenceId -> m Int) ->
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
m (Either IncoherentDeclReason DeclNameLookup)
checkDeclCoherency loadDeclNumConstructors =
checkDeclCoherency loadDeclNumConstructors nametree =
Except.runExceptT
. fmap (view #declNameLookup)
( checkDeclCoherencyWith
(lift . loadDeclNumConstructors)
OnIncoherentDeclReasons
{ onConstructorAlias = \x y z -> Except.throwError (IncoherentDeclReason'ConstructorAlias x y z), -- :: Name -> Name -> Name -> m (),
onMissingConstructorName = \x -> Except.throwError (IncoherentDeclReason'MissingConstructorName x), -- :: Name -> m (),
onNestedDeclAlias = \x y -> Except.throwError (IncoherentDeclReason'NestedDeclAlias x y), -- :: Name -> Name -> m (),
onStrayConstructor = \x -> Except.throwError (IncoherentDeclReason'StrayConstructor x) -- :: Name -> m ()
}
nametree
)
data IncoherentDeclReasons = IncoherentDeclReasons
{ constructorAliases :: ![(Name, Name, Name)],
missingConstructorNames :: ![Name],
nestedDeclAliases :: ![(Name, Name)],
strayConstructors :: ![Name]
}
deriving stock (Eq, Generic)
-- | Like 'checkDeclCoherency', but returns info about all of the incoherent decls found, not just the first.
checkAllDeclCoherency ::
forall m.
Monad m =>
(TypeReferenceId -> m Int) ->
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
m (Either IncoherentDeclReasons DeclNameLookup)
checkAllDeclCoherency loadDeclNumConstructors nametree = do
State.runStateT doCheck emptyReasons <&> \(declNameLookup, reasons) ->
if reasons == emptyReasons
then Right declNameLookup
else Left (reverseReasons reasons)
where
doCheck :: StateT IncoherentDeclReasons m DeclNameLookup
doCheck =
checkDeclCoherencyWith
(lift . loadDeclNumConstructors)
( OnIncoherentDeclReasons
{ onConstructorAlias = \x y z -> #constructorAliases %= ((x, y, z) :),
onMissingConstructorName = \x -> #missingConstructorNames %= (x :),
onNestedDeclAlias = \x y -> #nestedDeclAliases %= ((x, y) :),
onStrayConstructor = \x -> #strayConstructors %= (x :)
}
)
nametree
emptyReasons :: IncoherentDeclReasons
emptyReasons =
IncoherentDeclReasons [] [] [] []
reverseReasons :: IncoherentDeclReasons -> IncoherentDeclReasons
reverseReasons reasons =
IncoherentDeclReasons
{ constructorAliases = reverse reasons.constructorAliases,
missingConstructorNames = reverse reasons.missingConstructorNames,
nestedDeclAliases = reverse reasons.nestedDeclAliases,
strayConstructors = reverse reasons.strayConstructors
}
data OnIncoherentDeclReasons m = OnIncoherentDeclReasons
{ onConstructorAlias :: Name -> Name -> Name -> m (),
onMissingConstructorName :: Name -> m (),
onNestedDeclAlias :: Name -> Name -> m (),
onStrayConstructor :: Name -> m ()
}
checkDeclCoherencyWith ::
forall m.
Monad m =>
(TypeReferenceId -> m Int) ->
OnIncoherentDeclReasons m ->
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
m DeclNameLookup
checkDeclCoherencyWith loadDeclNumConstructors callbacks =
fmap (view #declNameLookup)
. (`State.execStateT` DeclCoherencyCheckState Map.empty (DeclNameLookup Map.empty Map.empty))
. go []
where
go ::
[NameSegment] ->
(Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
StateT DeclCoherencyCheckState (ExceptT IncoherentDeclReason m) ()
StateT DeclCoherencyCheckState m ()
go prefix (Nametree defns children) = do
for_ (Map.toList defns.terms) \case
for_ (Map.toList defns.terms) (checkDeclCoherencyWith_DoTerms callbacks prefix)
childrenWeWentInto <-
forMaybe
(Map.toList defns.types)
(checkDeclCoherencyWith_DoTypes loadDeclNumConstructors callbacks go prefix children)
let childrenWeHaventGoneInto = children `Map.withoutKeys` Set.fromList childrenWeWentInto
for_ (Map.toList childrenWeHaventGoneInto) \(name, child) -> go (name : prefix) child
checkDeclCoherencyWith_DoTerms ::
forall m.
Monad m =>
OnIncoherentDeclReasons m ->
[NameSegment] ->
(NameSegment, Referent) ->
StateT DeclCoherencyCheckState m ()
checkDeclCoherencyWith_DoTerms callbacks prefix = \case
(_, Referent.Ref _) -> pure ()
(_, Referent.Con (ConstructorReference (ReferenceBuiltin _) _) _) -> pure ()
(name, Referent.Con (ConstructorReference (ReferenceDerived typeRef) conId) _) -> do
DeclCoherencyCheckState {expectedConstructors} <- State.get
expectedConstructors1 <- lift (Except.except (Map.upsertF f typeRef expectedConstructors))
state <- State.get
whenJustM (lift (runMaybeT (Map.upsertF f typeRef state.expectedConstructors))) \expectedConstructors1 ->
#expectedConstructors .= expectedConstructors1
where
f ::
Maybe (Name, ConstructorNames) ->
Either IncoherentDeclReason (Name, ConstructorNames)
f :: Maybe (Name, ConstructorNames) -> MaybeT m (Name, ConstructorNames)
f = \case
Nothing -> Left (IncoherentDeclReason'StrayConstructor name1)
Nothing -> do
lift (callbacks.onStrayConstructor name1)
MaybeT (pure Nothing)
Just (typeName, expected) ->
case recordConstructorName conId name1 expected of
Left existingName -> Left (IncoherentDeclReason'ConstructorAlias typeName existingName name1)
Right expected1 -> Right (typeName, expected1)
Left existingName -> do
lift (callbacks.onConstructorAlias typeName existingName name1)
MaybeT (pure Nothing)
Right expected1 -> pure (typeName, expected1)
where
name1 = fullName name
name1 =
Name.fromReverseSegments (name :| prefix)
childrenWeWentInto <-
forMaybe (Map.toList defns.types) \case
checkDeclCoherencyWith_DoTypes ::
forall m.
Monad m =>
(TypeReferenceId -> m Int) ->
OnIncoherentDeclReasons m ->
( [NameSegment] ->
(Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
StateT DeclCoherencyCheckState m ()
) ->
[NameSegment] ->
Map NameSegment (Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
(NameSegment, TypeReference) ->
StateT DeclCoherencyCheckState m (Maybe NameSegment)
checkDeclCoherencyWith_DoTypes loadDeclNumConstructors callbacks go prefix children = \case
(_, ReferenceBuiltin _) -> pure Nothing
(name, ReferenceDerived typeRef) -> do
DeclCoherencyCheckState {expectedConstructors} <- State.get
whatHappened <- do
state <- State.get
maybeWhatHappened <- do
let recordNewDecl ::
Maybe (Name, ConstructorNames) ->
Compose (ExceptT IncoherentDeclReason m) WhatHappened (Name, ConstructorNames)
Compose (MaybeT m) WhatHappened (Name, ConstructorNames)
recordNewDecl =
Compose . \case
Just (shorterTypeName, _) -> Except.throwError (IncoherentDeclReason'NestedDeclAlias shorterTypeName typeName)
Just (shorterTypeName, _) -> do
lift (callbacks.onNestedDeclAlias shorterTypeName typeName)
MaybeT (pure Nothing)
Nothing ->
lift (loadDeclNumConstructors typeRef) <&> \case
0 -> UninhabitedDecl
n -> InhabitedDecl (typeName, emptyConstructorNames n)
lift (getCompose (Map.upsertF recordNewDecl typeRef expectedConstructors))
case whatHappened of
UninhabitedDecl -> do
lift (runMaybeT (getCompose (Map.upsertF recordNewDecl typeRef state.expectedConstructors)))
case maybeWhatHappened of
Nothing -> pure Nothing
Just UninhabitedDecl -> do
#declNameLookup . #declToConstructors %= Map.insert typeName []
pure Nothing
InhabitedDecl expectedConstructors1 -> do
child <-
Map.lookup name children & onNothing do
Except.throwError (IncoherentDeclReason'MissingConstructorName typeName)
Just (InhabitedDecl expectedConstructors1) -> do
case Map.lookup name children of
Nothing -> do
lift (callbacks.onMissingConstructorName typeName)
pure Nothing
Just child -> do
#expectedConstructors .= expectedConstructors1
go (name : prefix) child
DeclCoherencyCheckState {expectedConstructors} <- State.get
state <- State.get
-- fromJust is safe here because we upserted `typeRef` key above
let (fromJust -> (_typeName, maybeConstructorNames), expectedConstructors1) =
Map.deleteLookup typeRef expectedConstructors
constructorNames <-
sequence (IntMap.elems maybeConstructorNames) & onNothing do
Except.throwError (IncoherentDeclReason'MissingConstructorName typeName)
Map.deleteLookup typeRef state.expectedConstructors
#expectedConstructors .= expectedConstructors1
case sequence (IntMap.elems maybeConstructorNames) of
Nothing -> lift (callbacks.onMissingConstructorName typeName)
Just constructorNames -> do
#declNameLookup . #constructorToDecl %= \constructorToDecl ->
List.foldl'
(\acc constructorName -> Map.insert constructorName typeName acc)
@ -210,12 +320,7 @@ checkDeclCoherency loadDeclNumConstructors =
#declNameLookup . #declToConstructors %= Map.insert typeName constructorNames
pure (Just name)
where
typeName = fullName name
let childrenWeHaventGoneInto = children `Map.withoutKeys` Set.fromList childrenWeWentInto
for_ (Map.toList childrenWeHaventGoneInto) \(name, child) -> go (name : prefix) child
where
fullName name =
typeName =
Name.fromReverseSegments (name :| prefix)
-- | A lenient variant of 'checkDeclCoherency' - so lenient it can't even fail! It returns partial decl name lookup,

View File

@ -17,6 +17,7 @@ runtime-tests/selected> run.native tests.jit.only
Per Dan:
It's testing a flaw in how we were sending code from a scratch file to the native runtime, when that happened multiple times.
Related to the verifiable refs and recursive functions.
``` unison
foo = do
go : Nat ->{Exception} ()

View File

@ -1,4 +1,3 @@
## Structural find and replace
Here's a scratch file with some rewrite rules:

View File

@ -30,7 +30,7 @@ scratch/a1> edit 1-1000
definitions currently in this namespace.
```
```unison:added-by-ucm scratch.u
````` unison:added-by-ucm scratch.u
structural ability Abort where abort : {Abort} a
structural ability Ask a where ask : {Ask a} a
@ -766,7 +766,7 @@ UUID.randomUUIDBytes = do
(|>) : a -> (a ->{e} b) ->{e} b
a |> f = f a
```
`````
This diff should be empty if the two namespaces are equivalent. If it's nonempty, the diff will show us the hashes that differ.
@ -795,7 +795,7 @@ scratch/a3> edit 1-5000
definitions currently in this namespace.
```
```unison:added-by-ucm scratch.u
```` unison:added-by-ucm scratch.u
explanationOfThisFile : Text
explanationOfThisFile =
"""
@ -815,7 +815,7 @@ sloppyDocEval =
1 + 1
```
}}
```
````
These are currently all expected to have different hashes on round trip.

View File

@ -1,4 +1,3 @@
Test for code serialization operations.
Define a function, serialize it, then deserialize it back to an actual

View File

@ -2,12 +2,12 @@
Unison documentation is written in Unison and has some neat features:
* The documentation type provides a rich vocabulary of elements that go beyond markdown, including asides, callouts, tooltips, and more.
* Docs may contain Unison code which is parsed and typechecked to ensure validity. No more out of date examples that don't compile or assume a bunch of implicit context!
* Embeded examples are live and can show the results of evaluation. This uses the same evaluation cache as Unison's scratch files, allowing Unison docs to function like well-commented spreadsheets or notebooks.
* Links to other definitions are typechecked to ensure they point to valid definitions. The links are resolved to hashes and won't be broken by name changes or moving definitions around.
* Docs can be included in other docs and you can assemble documentation programmatically, using Unison code.
* There's a powerful textual syntax for all of the above, which we'll introduce next.
- The documentation type provides a rich vocabulary of elements that go beyond markdown, including asides, callouts, tooltips, and more.
- Docs may contain Unison code which is parsed and typechecked to ensure validity. No more out of date examples that don't compile or assume a bunch of implicit context\!
- Embeded examples are live and can show the results of evaluation. This uses the same evaluation cache as Unison's scratch files, allowing Unison docs to function like well-commented spreadsheets or notebooks.
- Links to other definitions are typechecked to ensure they point to valid definitions. The links are resolved to hashes and won't be broken by name changes or moving definitions around.
- Docs can be included in other docs and you can assemble documentation programmatically, using Unison code.
- There's a powerful textual syntax for all of the above, which we'll introduce next.
## Introduction
@ -769,3 +769,4 @@ scratch/main> display doc.guide
```
🌻 THE END

View File

@ -36,6 +36,7 @@ some subtyping.
However, the ability handling was just processing rows in whatever
order they occurred, and during inference it happened that `g`
occurred in the row before `Async t g. Processing the stricter parts
occurred in the row before `Async t g`. Processing the stricter parts
first is better, becauase it can solve things more precisely and avoid
ambiguities relating to subtyping.

View File

@ -1,6 +1,5 @@
This tests a case where a function was somehow discarding abilities.
``` unison
structural ability Trivial where
trivial : ()

View File

@ -1,4 +1,3 @@
Tests a former error due to bad calling conventions on delay.impl
``` unison

View File

@ -38,7 +38,7 @@ go = do
```
This comes from issue #3513
This comes from issue \#3513
``` unison
(<<) : (b ->{e} c) -> (a ->{e} b) -> a ->{e} c

View File

@ -177,7 +177,7 @@ Note that the universal versions of `hash` and `hmac` are currently unimplemente
```
## Hashing tests
Here are some test vectors (taken from [here](https://www.di-mgt.com.au/sha_testvectors.html) and [here](https://en.wikipedia.org/wiki/BLAKE_(hash_function))) for the various hashing algorithms:
Here are some test vectors (taken from [here](https://www.di-mgt.com.au/sha_testvectors.html) and [here](https://en.wikipedia.org/wiki/BLAKE_\(hash_function\))) for the various hashing algorithms:
``` unison
ex alg input expected = checks [hashBytes alg (ascii input) == fromHex expected]

View File

@ -9,7 +9,6 @@ MVars are the building block on which many other concurrency
primitives can be built, such as Futures, Run at most once initializer
blocks, Queues, etc.
``` unison
eitherCk : (a -> Boolean) -> Either e a -> Boolean
eitherCk f = cases

View File

@ -16,14 +16,13 @@ socketAccept = compose reraise socketAccept.impl
This section tests functions in the IO builtin related to binding to
TCP server socket, as to be able to accept incoming TCP connections.
```builtin
.io2.IO.serverSocket : Optional Text -> Text ->{io2.IO} Either Failure io2.Socket
```
builtin.io2.IO.serverSocket : Optional Text -> Text ->{io2.IO} Either Failure io2.Socket
```
This function takes two parameters, The first is the Hostname. If None
is provided, We will attempt to bind to 0.0.0.0 (All ipv4
addresses). We currently only support IPV4 (we should fix this!)
addresses). We currently only support IPV4 (we should fix this\!)
The second is the name of the port to bind to. This can be
a decimal representation of a port number between 1-65535. This can be
a named port like "ssh" (for port 22) or "kermit" (for port 1649),

View File

@ -1,5 +1,6 @@
Loops that access a shared counter variable, accessed in transactions.
Some thread delaying is just accomplished by counting in a loop.
``` unison
count : Nat -> ()
count = cases

View File

@ -54,7 +54,7 @@ scratch/main> add
Tip: Use `help filestatus` to learn more.
```
---
-----
``` unison
y = 42

View File

@ -1,4 +1,4 @@
Test for new Text -> Bytes conversions explicitly using UTF-8 as the encoding
Test for new Text -\> Bytes conversions explicitly using UTF-8 as the encoding
Unison has function for converting between `Text` and a UTF-8 `Bytes` encoding of the Text.

View File

@ -1,4 +1,3 @@
Some random ability stuff to ensure things work.
``` unison

View File

@ -31,7 +31,7 @@ scratch/main> add
ability Channels
```
Now we update the ability, changing the name of the constructor, _but_, we simultaneously
Now we update the ability, changing the name of the constructor, *but*, we simultaneously
add a new top-level term with the same name as the constructor which is being
removed from Channels.
@ -89,7 +89,7 @@ scratch/main> update.old patch thing
ability Channels
```
If however, `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency.
If however, `Channels.send` and `thing` *depend* on `Channels`, updating them should succeed since it pulls in the ability as a dependency.
``` unison
unique ability Channels where

View File

@ -47,6 +47,7 @@ scratch/main> run is2even
```
it errors if the desired result name conflicts with a name in the
unison file
``` ucm
scratch/main> add.run is2even
@ -57,6 +58,7 @@ scratch/main> add.run is2even
```
otherwise, the result is successfully persisted
``` ucm
scratch/main> add.run foo.bar.baz
@ -223,6 +225,7 @@ x = 50
```
this saves 2 to xres, rather than 100
``` ucm
scratch/main> add.run xres

View File

@ -3,7 +3,7 @@ test> foo : [Test.Result]
foo = []
```
Apparently when we add a test watch, we add a type annotation to it, even if it already has one. We don't want this to happen though!
Apparently when we add a test watch, we add a type annotation to it, even if it already has one. We don't want this to happen though\!
``` ucm
scratch/main> add

View File

@ -1,17 +1,14 @@
The `alias.many` command can be used to copy definitions from the current namespace into your curated one.
The names that will be used in the target namespace are the names you specify, relative to the current namespace:
```scratch
/main> help alias.many
scratch/main> help alias.many
alias.many (or copy)
`alias.many <relative1> [relative2...] <namespace>` creates aliases `relative1`, `relative2`, ...
in the namespace `namespace`.
`alias.many foo.foo bar.bar .quux` creates aliases `.quux.foo.foo` and `.quux.bar.bar`.
```
Let's try it!
Let's try it\!
``` ucm
scratch/main> alias.many List.adjacentPairs List.all List.any List.chunk List.chunksOf List.dropWhile List.first List.init List.intersperse List.isEmpty List.last List.replicate List.splitAt List.tail List.takeWhile mylib
@ -63,4 +60,5 @@ scratch/main> find-in mylib
```
Thanks, `alias.many!
Thanks, `alias.many`\!

View File

@ -1,11 +1,10 @@
This tests a variable related bug in the ANF compiler.
The nested let would get flattened out, resulting in:
bar = result
which would be handled by renaming. However, the _context_ portion of
which would be handled by renaming. However, the *context* portion of
the rest of the code was not being renamed correctly, so `bar` would
remain in the definition of `baz`.

View File

@ -941,3 +941,4 @@ GET /api/projects/scratch/branches/main/getDefinition?names=term
"typeDefinitions": {}
}
```

View File

@ -253,3 +253,4 @@ GET /api/projects/scratch/branches/main/find?query=joey.http
]
]
```

View File

@ -205,7 +205,9 @@ GET /api/projects/scratch/branches/main/getDefinition?names=%23qkhkl0n238&relati
},
"typeDefinitions": {}
}
``````unison
```
``` unison
doctest.thing.doc = {{ The correct docs for the thing }}
doctest.thing = "A thing"
doctest.thingalias.doc = {{ Docs for the alias, should not be displayed }}
@ -332,7 +334,9 @@ GET /api/projects/scratch/branches/main/getDefinition?names=thing&relativeTo=doc
},
"typeDefinitions": {}
}
```If we request a doc, the api should return the source, but also the rendered doc should appear in the 'termDocs' list.
```
If we request a doc, the api should return the source, but also the rendered doc should appear in the 'termDocs' list.
``` api
GET /api/projects/scratch/branches/main/getDefinition?names=thing.doc&relativeTo=doctest
@ -508,3 +512,4 @@ GET /api/projects/scratch/branches/main/getDefinition?names=thing.doc&relativeTo
"typeDefinitions": {}
}
```

View File

@ -54,3 +54,4 @@ GET /api/projects/project-one/branches?prefix=branch-t
}
]
```

View File

@ -79,3 +79,4 @@ GET /api/projects/scratch/branches/main/namespaces/nested.names
}
}
```

View File

@ -132,3 +132,4 @@ GET /api/projects/scratch/branches/main/list?namespace=names&relativeTo=nested
"namespaceListingHash": "#oms19b4f9s3c8tb5skeb8jii95ij35n3hdg038pu6rv5b0fikqe4gd7lnu6a1i6aq5tdh2opdo4s0sfrupvk6vfkr9lf0n752gbl8o0"
}
```

View File

@ -667,7 +667,9 @@ GET /api/projects/scratch/branches/main/definitions/terms/by-hash/@@IO.putBytes.
},
"tag": "Plain"
}
```## Type Summary APIs
```
## Type Summary APIs
``` api
-- data
@ -824,3 +826,4 @@ GET /api/projects/scratch/branches/main/definitions/types/by-hash/@@Nat/summary?
"tag": "Data"
}
```

View File

@ -103,7 +103,7 @@ ex thing =
4201
```
Here's another example, showing that bindings cannot reference bindings declared in blocks nested in the _body_ (the final expression) of a block:
Here's another example, showing that bindings cannot reference bindings declared in blocks nested in the *body* (the final expression) of a block:
``` unison
ex thing =
@ -137,7 +137,7 @@ ex thing =
```
### Blocks can define one or more functions which are recursive or mutually recursive
We call these groups of definitions that reference each other in a block _cycles_. For instance:
We call these groups of definitions that reference each other in a block *cycles*. For instance:
``` unison
sumTo n =
@ -253,7 +253,7 @@ ex n =
ex : n -> r
```
Just don't try to run it as it's an infinite loop!
Just don't try to run it as it's an infinite loop\!
### Cyclic definitions in a block don't have access to any abilities
@ -279,7 +279,7 @@ ex n =
```
### The _body_ of recursive functions can certainly access abilities
### The *body* of recursive functions can certainly access abilities
For instance, this works fine:

View File

@ -1,4 +1,3 @@
We can display the guide before and after adding it to the codebase:
``` ucm

View File

@ -153,6 +153,7 @@ test> Nat.tests.conversions =
```
## `Boolean` functions
``` unison
test> Boolean.tests.orTable =
checks [
@ -345,6 +346,7 @@ test> checks [
```
Other list functions
``` unison
test> checks [
List.take bigN [1,2,3] == [1,2,3],

View File

@ -1,4 +1,3 @@
This should render as `Bytes.fromList [1,2,3,4]`, not `##Bytes.fromSequence [1,2,3,4]`:
``` unison

View File

@ -1,6 +1,7 @@
First we'll set up two libraries, and then we'll use them in some projects and show what `names` are deep-loaded for them.
Our two "libraries":
``` unison
text.a = 1
text.b = 2
@ -12,6 +13,7 @@ http.z = 8
```
Our `app1` project includes the text library twice and the http library twice as direct dependencies.
``` ucm
scratch/app1> fork text lib.text_v1
@ -39,6 +41,7 @@ scratch/app1> delete.namespace http
```
As such, we see two copies of `a` and two copies of `x` via these direct dependencies.
``` ucm
scratch/app1> names a
@ -59,6 +62,7 @@ scratch/app1> names x
```
Our `app2` project includes the `http` library twice as direct dependencies, and once as an indirect dependency via `webutil`.
It also includes the `text` library twice as indirect dependencies via `webutil`
``` ucm
scratch/app2> fork http lib.http_v1
@ -91,6 +95,7 @@ scratch/app2> delete.namespace text
```
Now we see two copies of `x` via direct dependencies on `http`, and one copy of `a` via indirect dependency on `text` via `webutil`.
We see neither the second indirect copy of `a` nor the indirect copy of `x` via webutil because we already have names for them.
``` ucm
scratch/app2> names a

View File

@ -558,7 +558,9 @@ GET /api/projects/diffs/diff/terms?oldBranchRef=main&newBranchRef=new&oldTerm=te
},
"project": "diffs"
}
```Diff types
```
Diff types
``` api
GET /api/projects/diffs/diff/types?oldBranchRef=main&newBranchRef=new&oldType=Type&newType=Type
@ -805,3 +807,4 @@ GET /api/projects/diffs/diff/types?oldBranchRef=main&newBranchRef=new&oldType=Ty
"project": "diffs"
}
```

View File

@ -2,7 +2,7 @@
# Delete namespace dependents check
This is a regression test, previously `delete.namespace` allowed a delete as long as the deletions had a name _anywhere_ in your codebase, it should only check the current project branch.
This is a regression test, previously `delete.namespace` allowed a delete as long as the deletions had a name *anywhere* in your codebase, it should only check the current project branch.
``` unison
sub.dependency = 123

View File

@ -1,7 +1,9 @@
### `debug.file`
I can use `debug.file` to see the hashes of the last typechecked file.
Given this .u file:
``` unison
structural type outside.A = A Nat outside.B
structural type outside.B = B Int
@ -30,7 +32,9 @@ scratch/main> debug.file
This will help me make progress in some situations when UCM is being deficient or broken.
### `dependents` / `dependencies`
But wait, there's more. I can check the dependencies and dependents of a definition:
``` ucm
scratch/main> add
@ -110,3 +114,4 @@ scratch/main> dependents d
```
We don't have an index for dependents of constructors, but iirc if you ask for that, it will show you dependents of the structural type that provided the constructor.

View File

@ -52,14 +52,14 @@ scratch/main> diff.namespace /b1: /b2:
```
Things we want to test:
* Diffing identical namespaces
* Adds, removes, updates
* Adds with multiple names
* Moved and copied definitions
* Moves that have more that 1 initial or final name
* ... terms and types
* New patches, modified patches, deleted patches, moved patches
* With and without propagated updates
- Diffing identical namespaces
- Adds, removes, updates
- Adds with multiple names
- Moved and copied definitions
- Moves that have more that 1 initial or final name
- ... terms and types
- New patches, modified patches, deleted patches, moved patches
- With and without propagated updates
``` unison
fromJust = 1
@ -504,43 +504,44 @@ Updates: -- 1 to 1
New name conflicts: -- updates where RHS has multiple hashes (excluding when RHS=LHS)
1. foo#jk19sm5bf8 : Nat - do we want to force a hashqualified? Arya thinks so
1. foo\#jk19sm5bf8 : Nat - do we want to force a hashqualified? Arya thinks so
2. ┌ foo#0ja1qfpej6 : Nat
3. └ foo#jk19sm5bf8 : Nat
2. ┌ foo\#0ja1qfpej6 : Nat
3. └ foo\#jk19sm5bf8 : Nat
Resolved name conflicts: -- updates where LHS had multiple hashes and RHS has one
4. ┌ bar#0ja1qfpej6 : Nat
5. └ bar#jk19sm5bf8 : Nat
4. ┌ bar\#0ja1qfpej6 : Nat
5. └ bar\#jk19sm5bf8 : Nat
6. bar#jk19sm5bf8 : Nat
6. bar\#jk19sm5bf8 : Nat
## Display issues to fixup
- [d] Do we want to surface new edit conflicts in patches?
- [t] two different auto-propagated changes creating a name conflict should show
- \[d\] Do we want to surface new edit conflicts in patches?
- \[t\] two different auto-propagated changes creating a name conflict should show
up somewhere besides the auto-propagate count
- [t] Things look screwy when the type signature doesn't fit and has to get broken
- \[t\] Things look screwy when the type signature doesn't fit and has to get broken
up into multiple lines. Maybe just disallow that?
- [d] Delete blank line in between copies / renames entries if all entries are 1 to 1
- \[d\] Delete blank line in between copies / renames entries if all entries are 1 to 1
see todo in the code
- [x] incorrectly calculated bracket alignment on hashqualified "Name changes" (delete.output.md)
- [x] just handle deletion of isPropagated in propagate function, leave HandleInput alone (assuming this does the trick)
- [x] might want unqualified names to be qualified sometimes:
- [x] if a name is updated to a not-yet-named reference, it's shown as both an update and an add
- [x] similarly, if a conflicted name is resolved by deleting the last name to
- \[x\] incorrectly calculated bracket alignment on hashqualified "Name changes" (delete.output.md)
- \[x\] just handle deletion of isPropagated in propagate function, leave HandleInput alone (assuming this does the trick)
- \[x\] might want unqualified names to be qualified sometimes:
- \[x\] if a name is updated to a not-yet-named reference, it's shown as both an update and an add
- \[x\] similarly, if a conflicted name is resolved by deleting the last name to
a reference, I (arya) suspect it will show up as a Remove
- [d] Maybe group and/or add headings to the types, constructors, terms
- [x] add tagging of propagated updates to test propagated updates output
- [x] missing old names in deletion ppe (delete.output.md) (superseded by \#1143)
- [x] delete.term has some bonkers output
- [x] Make a decision about how we want to show constructors in the diff
- [x] 12.patch patch needs a space
- [x] This looks like garbage
- [x] Extra 2 blank lines at the end of the add section
- [x] Fix alignment issues with buildTable, convert to column3M (to be written)
- [x] adding an alias is showing up as an Add and a Copy; should just show as Copy
- [x] removing one of multiple aliases appears in removes + moves + copies section
- [x] some overlapping cases between Moves and Copies^
- [x] Maybe don't list the type signature twice for aliases?
- \[d\] Maybe group and/or add headings to the types, constructors, terms
- \[x\] add tagging of propagated updates to test propagated updates output
- \[x\] missing old names in deletion ppe (delete.output.md) (superseded by \#1143)
- \[x\] delete.term has some bonkers output
- \[x\] Make a decision about how we want to show constructors in the diff
- \[x\] 12.patch patch needs a space
- \[x\] This looks like garbage
- \[x\] Extra 2 blank lines at the end of the add section
- \[x\] Fix alignment issues with buildTable, convert to column3M (to be written)
- \[x\] adding an alias is showing up as an Add and a Copy; should just show as Copy
- \[x\] removing one of multiple aliases appears in removes + moves + copies section
- \[x\] some overlapping cases between Moves and Copies^
- \[x\] Maybe don't list the type signature twice for aliases?

View File

@ -510,6 +510,7 @@ test2 = [:
```
View is fine.
``` ucm
scratch/main> view test2
@ -521,6 +522,7 @@ scratch/main> view test2
```
But note it's not obvious how display should best be handling this. At the moment it just does the simplest thing:
``` ucm
scratch/main> display test2

View File

@ -43,11 +43,11 @@ Syntax:
`[:` starts a documentation block; `:]` finishes it. Within the block:
* Links to definitions are done with `@List`. `\@` (and `\:]`) if you want to escape.
* `@[signature] List.take` expands to the type signature of `List.take`
* `@[source] List.map` expands to the full source of `List.map`
* `@[include] someOtherDoc`, inserts a value `someOtherDoc : Doc` here.
* `@[evaluate] someDefinition` expands to the result of evaluating `someDefinition`, which must be a pre-existing definition in the codebase (can't be an arbitrary expression).
- Links to definitions are done with `@List`. `\@` (and `\:]`) if you want to escape.
- `@[signature] List.take` expands to the type signature of `List.take`
- `@[source] List.map` expands to the full source of `List.map`
- `@[include] someOtherDoc`, inserts a value `someOtherDoc : Doc` here.
- `@[evaluate] someDefinition` expands to the result of evaluating `someDefinition`, which must be a pre-existing definition in the codebase (can't be an arbitrary expression).
### An example

View File

@ -1,5 +1,5 @@
If `foo#old` exists in old, and `foo#new` exists in new, you might think `upgrade old new` would rewrite references to
`#old` with references to `#new`. And it will... !!unless!! `#old` still exists in new.
`#old` with references to `#new`. And it will... \!\!unless\!\! `#old` still exists in new.
``` unison
lib.old.foo = 18

View File

@ -1,4 +1,3 @@
``` unison
up = 0xs0123456789abcdef
down = 0xsfedcba9876543210

View File

@ -16,7 +16,6 @@ mytest = [Ok "ok"]
```
``` ucm
Loading changes detected in /private/tmp/scratch.u.

View File

@ -5,6 +5,7 @@ mynamespace.x = 1
```
The deleted namespace shouldn't appear in `ls` output.
``` ucm
scratch/main> ls

View File

@ -2,9 +2,9 @@
The Unison codebase, when first initialized, contains no definitions in its namespace.
Not even `Nat` or `+`!
Not even `Nat` or `+`\!
BEHOLD!!!
BEHOLD\!\!\!
``` ucm
scratch/main> ls
@ -37,4 +37,5 @@ scratch/main> ls lib
2. builtinsio/ (643 terms, 92 types)
```
More typically, you'd start out by pulling `base.
More typically, you'd start out by pulling `base`.

View File

@ -1,4 +1,3 @@
This file contains programs with parse errors and type errors, for visual inspection of error message quality and to check for regressions or changes to error reporting.
## Parse errors
@ -212,7 +211,8 @@ foo = match 1 with
I got confused here:
3 |
2 | 2 -- no right-hand-side
I was surprised to find an end of section here.
I was expecting one of these instead:
@ -258,7 +258,8 @@ x = match Some a with
I got confused here:
7 |
6 | 2
I was surprised to find an end of section here.
I was expecting one of these instead:

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an error is encountered in a `unison:hide:all` block

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an error is encountered in a `unison:hide:all` block

View File

@ -1,7 +1,6 @@
### Transcript parser hidden errors
Dangerous scary words!
Dangerous scary words\!
When an expected error is not encountered in a `ucm:hide:all` block
then the transcript parser should print the stanza
@ -12,6 +11,7 @@ scratch/main> history
```
🛑
The transcript was expecting an error in the stanza above, but did not encounter one.

View File

@ -1,7 +1,6 @@
### Transcript parser hidden errors
Dangerous scary words!
Dangerous scary words\!
When an error is encountered in a `ucm:hide:all` block
then the transcript parser should print the stanza
@ -12,6 +11,7 @@ scratch/main> move.namespace foo bar
```
🛑
The transcript failed due to an error in the stanza above. The error is:

View File

@ -1,7 +1,6 @@
### Transcript parser hidden errors
Dangerous scary words!
Dangerous scary words\!
When an expected error is not encountered in a `ucm:hide` block
then the transcript parser should print the stanza
@ -12,6 +11,7 @@ scratch/main> history
```
🛑
The transcript was expecting an error in the stanza above, but did not encounter one.

View File

@ -1,7 +1,6 @@
### Transcript parser hidden errors
Dangerous scary words!
Dangerous scary words\!
When an error is encountered in a `ucm:hide` block
then the transcript parser should print the stanza
@ -12,6 +11,7 @@ scratch/main> move.namespace foo bar
```
🛑
The transcript failed due to an error in the stanza above. The error is:

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an expected error is not encountered in a `unison:hide:all:error` block

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an error is encountered in a `unison:hide:all` block

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an expected error is not encountered in a `unison:hide:error` block

View File

@ -1,4 +1,3 @@
### Transcript parser hidden errors
When an error is encountered in a `unison:hide` block

View File

@ -1,6 +1,7 @@
We were seeing an issue where (it seemed) that every namespace that was visited during a propagate would get a new history node, even when it didn't contain any dependents.
Example:
``` unison
a = "a term"
X.foo = "a namespace"
@ -16,6 +17,7 @@ scratch/main> add
```
Here is an update which should not affect `X`:
``` unison
a = "an update"
```
@ -30,6 +32,7 @@ scratch/main> update
```
As of the time of this writing, the history for `X` should be a single node, `#4eeuo5bsfr`;
``` ucm
scratch/main> history X
@ -42,6 +45,7 @@ scratch/main> history X
```
however, as of release/M1i, we saw an extraneous node appear. If your `ucm` is fixed, you won't see it below:
``` ucm
scratch/main> history #7nl6ppokhg

View File

@ -1,4 +1,4 @@
Previously, the `alias.term` and `alias.type` would fail if the source argument was hash-only, and there was no way to create an alias for a definition that didn't already have a name. Also, the `replace.term` and `replace.type` _only_ worked on hashes, and they had to be _full_ hashes.
Previously, the `alias.term` and `alias.type` would fail if the source argument was hash-only, and there was no way to create an alias for a definition that didn't already have a name. Also, the `replace.term` and `replace.type` *only* worked on hashes, and they had to be *full* hashes.
With this PR, the source of an alias can be a short hash (even of a definition that doesn't currently have a name in the namespace) along with a name or hash-qualified name from the current namespace as usual.

View File

@ -1,4 +1,3 @@
``` ucm
scratch/main> builtins.merge

View File

@ -0,0 +1,8 @@
```ucm
scratch/main> alias.type ##Nat Nat
scratch/main> alias.term ##Nat.+ Nat.+
```
```unison
unique type A = A Nat
unique type B = B Nat Nat
```

View File

@ -0,0 +1,29 @@
``` ucm
scratch/main> alias.type ##Nat Nat
Done.
scratch/main> alias.term ##Nat.+ Nat.+
Done.
```
``` unison
unique type A = A Nat
unique type B = B Nat Nat
```
``` ucm
Loading changes detected in scratch.u.
I found and typechecked these definitions in scratch.u. If you
do an `add` or `update`, here's how your codebase would
change:
⍟ These new definitions are ok to `add`:
type A
type B
```

View File

@ -13,9 +13,9 @@ foo.bar = 23
Suffix-based name resolution prefers to use names locally defined in the current file, then checks for matches in the codebase. Here are the precise rules, which will be explained below with examples:
* If a symbol, `s`, is a suffix of exactly one definition `d` in the file, then `s` refers to `d`.
* Otherwise, if `s` is a suffix of exactly one definition `d` in the codebase, then `s` refers to `d`.
* Otherwise, if `s` is a suffix of multiple definitions in the file or the codebase, then (at least for terms) type-directed name resolution will be attempted to figure out which definition `s` refers to.
- If a symbol, `s`, is a suffix of exactly one definition `d` in the file, then `s` refers to `d`.
- Otherwise, if `s` is a suffix of exactly one definition `d` in the codebase, then `s` refers to `d`.
- Otherwise, if `s` is a suffix of multiple definitions in the file or the codebase, then (at least for terms) type-directed name resolution will be attempted to figure out which definition `s` refers to.
## Example 1: local file term definitions shadow codebase term definitions
@ -32,7 +32,7 @@ baz = bar ++ ", world!"
## Example 2: any locally unique term suffix shadows codebase term definitions
This should also typecheck, using the file's `oog.bar`. This shows you can refer to a definition in the file by any suffix that is unique to definitions in the file (even if that suffix may match other definitions in the _codebase_). See example 4 below for overriding this behavior.
This should also typecheck, using the file's `oog.bar`. This shows you can refer to a definition in the file by any suffix that is unique to definitions in the file (even if that suffix may match other definitions in the *codebase*). See example 4 below for overriding this behavior.
``` unison
use Text ++

View File

@ -1,4 +1,3 @@
``` unison
structural ability Ask where ask : Nat

View File

@ -1,4 +1,3 @@
``` unison
structural ability CLI where
print : Text ->{CLI} ()

View File

@ -1,4 +1,3 @@
``` unison
printLine : Text ->{IO} ()
printLine msg =
@ -18,8 +17,8 @@ main3 _ = printLine "🦄 ☁️ 🌈"
Testing a few variations here:
* Should be able to run annotated and unannotated main functions in the current file.
* Should be able to run annotated and unannotated main functions from the codebase.
- Should be able to run annotated and unannotated main functions in the current file.
- Should be able to run annotated and unannotated main functions from the codebase.
``` ucm
scratch/main> run main1

View File

@ -1,4 +1,3 @@
``` unison
structural type One a = One a
unique type Woot a b c = Woot a b c

View File

@ -1,5 +1,3 @@
``` unison
structural ability Exception where raise : Failure -> x

View File

@ -1,4 +1,3 @@
Tests for a case where bad eta reduction was causing erroneous watch
output/caching.

View File

@ -37,3 +37,4 @@ fail because the type was invalid.
The fix was to avoid dropping certain existential variables out of
scope.

View File

@ -1,4 +1,3 @@
This should not typecheck - the inline `@eval` expression uses abilities.
``` unison

View File

@ -1,4 +1,3 @@
This transcript checks that updates to data types propagate successfully to dependent types and dependent terms that do pattern matching. First let's create some types and terms:
``` unison

View File

@ -1,4 +1,3 @@
Tests an issue where pattern matching matrices involving built-in
types was discarding default cases in some branches.

View File

@ -1,4 +1,3 @@
Checks a corner case with type checking involving destructuring binds.
The binds were causing some sequences of lets to be unnecessarily

View File

@ -1,4 +1,3 @@
This tests an issue where ability variables were being defaulted over
eagerly. In general, we want to avoid collecting up variables from the
use of definitions with types like:

View File

@ -1,4 +1,3 @@
Tests that delaying an un-annotated higher-rank type gives a normal
type error, rather than an internal compiler error.

View File

@ -1,4 +1,3 @@
Tests for a loop that was previously occurring in the type checker.
``` unison

View File

@ -1,4 +1,3 @@
Tests for an ability failure that was caused by order dependence of
checking wanted vs. provided abilities. It was necessary to re-check
rows until a fixed point is reached.

View File

@ -1,4 +1,3 @@
Tests an issue with a lack of generality of handlers.
In general, a set of cases:

View File

@ -1,4 +1,3 @@
Tests an issue with a lack of generality of handlers.
In general, a set of cases:

View File

@ -1,4 +1,3 @@
Tests a variable capture problem.
After pattern compilation, the match would end up:

View File

@ -1,4 +1,3 @@
Tests a variable capture problem.
After pattern compilation, the match would end up:

View File

@ -1,4 +1,3 @@
``` unison
loop : List Nat -> Nat -> List Nat
loop l = cases

View File

@ -0,0 +1,23 @@
```ucm
scratch/main> builtins.mergeio
```
Supports fences that are longer than three backticks.
````unison
doc = {{
@typecheck ```
x = 3
```
}}
````
And round-trips properly.
```ucm
scratch/main> add
scratch/main> edit doc
scratch/main> load scratch.u
```

View File

@ -0,0 +1,66 @@
``` ucm
scratch/main> builtins.mergeio
Done.
```
Supports fences that are longer than three backticks.
```` unison
doc = {{
@typecheck ```
x = 3
```
}}
````
``` ucm
Loading changes detected in scratch.u.
I found and typechecked these definitions in scratch.u. If you
do an `add` or `update`, here's how your codebase would
change:
⍟ These new definitions are ok to `add`:
doc : Doc2
```
And round-trips properly.
``` ucm
scratch/main> add
⍟ I've added these definitions:
doc : Doc2
scratch/main> edit doc
☝️
I added 1 definitions to the top of scratch.u
You can edit them there, then run `update` to replace the
definitions currently in this namespace.
scratch/main> load scratch.u
Loading changes detected in scratch.u.
I found and typechecked the definitions in scratch.u. This
file has been previously added to the codebase.
```
```` unison:added-by-ucm scratch.u
doc : Doc2
doc =
{{
@typecheck ```
x = 3
```
}}
````

View File

@ -1,6 +1,6 @@
This bugfix addresses an issue where embedded Unison code in UCM was expected to be present in the active codebase when the `display` command was used render `Doc` values.
First, a few \[hidden] definitions necessary for typechecking a simple Doc2.
First, a few \[hidden\] definitions necessary for typechecking a simple Doc2.
``` ucm
scratch/main> add
@ -18,6 +18,7 @@ scratch/main> add
```
Next, define and display a simple Doc:
``` unison
README = {{
Hi
@ -32,10 +33,8 @@ scratch/main> display README
```
Previously, the error was:
```
⚙️ Processing stanza 5 of 7.ucm: PE [("die",SrcLoc {srcLocPackage = "unison-parser-typechecker-0.0.0-He2Hp1llokT2nN4MnUfUXz", srcLocModule = "Unison.Runtime.Interface", srcLocFile = "src/Unison/Runtime/Interface.hs", srcLocStartLine = 118, srcLocStartCol = 18, srcLocEndLine = 118, srcLocEndCol = 60})] Lit
AnnotatedText (fromList [Segment {segment = "Unknown term reference: #4522d", annotation = Nothing}])
```
but as of this PR, it's okay.

View File

@ -1,4 +1,4 @@
Also fixes #1519 (it's the same issue).
Also fixes \#1519 (it's the same issue).
``` ucm
scratch/main> builtins.merge

View File

@ -1,4 +1,3 @@
Tests ability checking in scenarios where one side is concrete and the other is
a variable. This was supposed to be covered, but the method wasn't actually
symmetric, so doing `equate l r` might work, but not `equate r l`.

Some files were not shown because too many files have changed in this diff Show More