packages feed

disco 0.1.5 → 0.1.6

raw patch · 114 files changed

+10317/−9016 lines, 114 filesdep +prettyprinterdep −oeisdep −prettydep ~algebraic-graphsdep ~arithmoidep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: prettyprinter

Dependencies removed: oeis, pretty

Dependency ranges changed: algebraic-graphs, arithmoi, base, bytestring, fgl, lens, megaparsec, mtl, optparse-applicative, polysemy, pretty-show, tasty, unbound-generics

API changes (from Hackage documentation)

- Disco.Messages: instance GHC.Show.Show Disco.Messages.Message
- Disco.Pretty.DSL: instance Data.String.IsString (Polysemy.Internal.Sem r Text.PrettyPrint.HughesPJ.Doc)
- Disco.Value: prettyTestFailure :: Members '[Input TyDefCtx, LFresh, Reader PA] r => AProperty -> TestResult -> Sem r Doc
- Disco.Value: prettyTestResult :: Members '[Input TyDefCtx, LFresh, Reader PA] r => AProperty -> TestResult -> Sem r Doc
+ Disco.AST.Core: OAnd :: Op
+ Disco.AST.Core: OImpl :: Op
+ Disco.AST.Core: OOr :: Op
+ Disco.AST.Core: OShouldLt :: Type -> Op
+ Disco.AST.Generic: [PNonlinear_] :: Embed (Pattern_ e) -> Name (Term_ e) -> Pattern_ e
+ Disco.AST.Surface: emptyModule :: Module
+ Disco.AST.Surface: pattern PNonlinear :: Pattern -> Name Term -> Pattern
+ Disco.Messages: instance GHC.Show.Show (Disco.Messages.Message ann)
+ Disco.Parser: MissingAscr :: DiscoParseError
+ Disco.Parser: MultiArgLambda :: DiscoParseError
+ Disco.Pretty.DSL: indent :: Functor f => Int -> f (Doc ann) -> f (Doc ann)
+ Disco.Pretty.DSL: instance Data.String.IsString (Polysemy.Internal.Sem r (Prettyprinter.Internal.Doc ann))
+ Disco.Property: prettyTestResult :: Members '[Input TyDefCtx, LFresh, Reader PA] r => AProperty -> TestResult -> Sem r (Doc ann)
+ Disco.Syntax.Operators: ShouldLt :: BOp
+ Disco.Typecheck.Solve: checkBaseEdge :: Members '[Error SolveError] r => (BaseTy, BaseTy) -> Sem r ()
+ Disco.Typecheck.Solve: checkBaseEdges :: Members '[Error SolveError] r => Graph UAtom -> Sem r (Graph UAtom)
+ Disco.Typecheck.Solve: isBaseEdge :: (UAtom, UAtom) -> Either (BaseTy, BaseTy) (UAtom, UAtom)
+ Disco.Typecheck.Util: NonlinearPattern :: Pattern -> Name Term -> TCError
+ Disco.Value: LAnd :: LOp
+ Disco.Value: LImpl :: LOp
+ Disco.Value: LOr :: LOp
+ Disco.Value: TestBin :: LOp -> TestResult -> TestResult -> TestReason_ a
+ Disco.Value: TestLt :: Type -> a -> a -> TestReason_ a
+ Disco.Value: VPBin :: LOp -> ValProp -> ValProp -> ValProp
+ Disco.Value: data LOp
+ Disco.Value: instance GHC.Classes.Eq Disco.Value.LOp
+ Disco.Value: instance GHC.Classes.Ord Disco.Value.LOp
+ Disco.Value: instance GHC.Enum.Bounded Disco.Value.LOp
+ Disco.Value: instance GHC.Enum.Enum Disco.Value.LOp
+ Disco.Value: instance GHC.Show.Show Disco.Value.LOp
+ Disco.Value: interpLOp :: LOp -> Bool -> Bool -> Bool
+ Disco.Value: resultIsCertain :: TestReason -> Bool
- Disco.AST.Surface: prettyTyDecl :: Members '[Reader PA, LFresh] r => Name t -> Type -> Sem r Doc
+ Disco.AST.Surface: prettyTyDecl :: Members '[Reader PA, LFresh] r => Name t -> Type -> Sem r (Doc ann)
- Disco.Effects.Counter: next :: forall r_ajvU. Member Counter r_ajvU => Sem r_ajvU Integer
+ Disco.Effects.Counter: next :: forall r_an6S. Member Counter r_an6S => Sem r_an6S Integer
- Disco.Effects.Fresh: fresh :: forall r_a1KNw x_X1KMJ. Member Fresh r_a1KNw => Name x_X1KMJ -> Sem r_a1KNw (Name x_X1KMJ)
+ Disco.Effects.Fresh: fresh :: forall r_a24bs x_X0. Member Fresh r_a24bs => Name x_X0 -> Sem r_a24bs (Name x_X0)
- Disco.Effects.LFresh: avoid :: forall r_alvJ a_alr7. Member LFresh r_alvJ => [AnyName] -> Sem r_alvJ a_alr7 -> Sem r_alvJ a_alr7
+ Disco.Effects.LFresh: avoid :: forall r_aofS a_aobc. Member LFresh r_aofS => [AnyName] -> Sem r_aofS a_aobc -> Sem r_aofS a_aobc
- Disco.Effects.LFresh: getAvoids :: forall r_alvM. Member LFresh r_alvM => Sem r_alvM (Set AnyName)
+ Disco.Effects.LFresh: getAvoids :: forall r_aofV. Member LFresh r_aofV => Sem r_aofV (Set AnyName)
- Disco.Effects.LFresh: lfresh :: forall r_alvH a_Xlr5. (Member LFresh r_alvH, Typeable a_Xlr5) => Name a_Xlr5 -> Sem r_alvH (Name a_Xlr5)
+ Disco.Effects.LFresh: lfresh :: forall r_aofQ a_X0. (Member LFresh r_aofQ, Typeable a_X0) => Name a_X0 -> Sem r_aofQ (Name a_X0)
- Disco.Effects.Store: assocsStore :: forall v_aomY r_aotn. Member (Store v_aomY) r_aotn => Sem r_aotn [(Int, v_aomY)]
+ Disco.Effects.Store: assocsStore :: forall v_arhL r_arnV. Member (Store v_arhL) r_arnV => Sem r_arnV [(Int, v_arhL)]
- Disco.Effects.Store: clearStore :: forall v_aomO r_aotd. Member (Store v_aomO) r_aotd => Sem r_aotd ()
+ Disco.Effects.Store: clearStore :: forall v_arhB r_arnL. Member (Store v_arhB) r_arnL => Sem r_arnL ()
- Disco.Effects.Store: insertStore :: forall v_aomU r_aoti. Member (Store v_aomU) r_aoti => Int -> v_aomU -> Sem r_aoti ()
+ Disco.Effects.Store: insertStore :: forall v_arhH r_arnQ. Member (Store v_arhH) r_arnQ => Int -> v_arhH -> Sem r_arnQ ()
- Disco.Effects.Store: keepKeys :: forall v_aon0 r_aoto. Member (Store v_aon0) r_aoto => IntSet -> Sem r_aoto ()
+ Disco.Effects.Store: keepKeys :: forall v_arhN r_arnW. Member (Store v_arhN) r_arnW => IntSet -> Sem r_arnW ()
- Disco.Effects.Store: lookupStore :: forall v_aomS r_aotg. Member (Store v_aomS) r_aotg => Int -> Sem r_aotg (Maybe v_aomS)
+ Disco.Effects.Store: lookupStore :: forall v_arhF r_arnO. Member (Store v_arhF) r_arnO => Int -> Sem r_arnO (Maybe v_arhF)
- Disco.Effects.Store: mapStore :: forall v_aomW r_aotl. Member (Store v_aomW) r_aotl => (v_aomW -> v_aomW) -> Sem r_aotl ()
+ Disco.Effects.Store: mapStore :: forall v_arhJ r_arnT. Member (Store v_arhJ) r_arnT => (v_arhJ -> v_arhJ) -> Sem r_arnT ()
- Disco.Effects.Store: new :: forall v_aomQ r_aote. Member (Store v_aomQ) r_aote => v_aomQ -> Sem r_aote Int
+ Disco.Effects.Store: new :: forall v_arhD r_arnM. Member (Store v_arhD) r_arnM => v_arhD -> Sem r_arnM Int
- Disco.Error: outputDiscoErrors :: Member (Output Message) r => Sem (Error DiscoError : r) () -> Sem r ()
+ Disco.Error: outputDiscoErrors :: Member (Output (Message ann)) r => Sem (Error DiscoError : r) () -> Sem r ()
- Disco.Eval: addToREPLModule :: Members '[Error DiscoError, State TopInfo, Random, State Mem, Output Message] r => ModuleInfo -> Sem r ()
+ Disco.Eval: addToREPLModule :: Members '[Error DiscoError, State TopInfo, Random, State Mem, Output (Message ann)] r => ModuleInfo -> Sem r ()
- Disco.Eval: loadDiscoModule :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r => Bool -> Resolver -> FilePath -> Sem r ModuleInfo
+ Disco.Eval: loadDiscoModule :: Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r => Bool -> Resolver -> FilePath -> Sem r ModuleInfo
- Disco.Eval: loadFile :: Members '[Output Message, Embed IO] r => FilePath -> Sem r (Maybe String)
+ Disco.Eval: loadFile :: Members '[Output (Message ann), Embed IO] r => FilePath -> Sem r (Maybe String)
- Disco.Eval: loadParsedDiscoModule :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r => Bool -> Resolver -> ModuleName -> Module -> Sem r ModuleInfo
+ Disco.Eval: loadParsedDiscoModule :: Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r => Bool -> Resolver -> ModuleName -> Module -> Sem r ModuleInfo
- Disco.Eval: setREPLModule :: Members '[State TopInfo, Random, Error EvalError, State Mem, Output Message] r => ModuleInfo -> Sem r ()
+ Disco.Eval: setREPLModule :: Members '[State TopInfo, Random, Error EvalError, State Mem, Output (Message ann)] r => ModuleInfo -> Sem r ()
- Disco.Eval: type EvalEffects = [Error EvalError, Random, LFresh, Output Message, State Mem]
+ Disco.Eval: type EvalEffects = [Error EvalError, Random, LFresh, Output (Message ()), State Mem]
- Disco.Interactive.Commands: handleLoad :: Members (Error DiscoError : (State TopInfo : (Output Message : (Embed IO : EvalEffects)))) r => FilePath -> Sem r Bool
+ Disco.Interactive.Commands: handleLoad :: Members (Error DiscoError : (State TopInfo : (Output (Message ()) : (Embed IO : EvalEffects)))) r => FilePath -> Sem r Bool
- Disco.Interactive.Commands: loadFile :: Members '[Output Message, Embed IO] r => FilePath -> Sem r (Maybe String)
+ Disco.Interactive.Commands: loadFile :: Members '[Output (Message ann), Embed IO] r => FilePath -> Sem r (Maybe String)
- Disco.Messages: Message :: MessageType -> Doc -> Message
+ Disco.Messages: Message :: MessageType -> Doc ann -> Message ann
- Disco.Messages: [_messageType] :: Message -> MessageType
+ Disco.Messages: [_messageType] :: Message ann -> MessageType
- Disco.Messages: [_message] :: Message -> Doc
+ Disco.Messages: [_message] :: Message ann -> Doc ann
- Disco.Messages: data Message
+ Disco.Messages: data Message ann
- Disco.Messages: debug :: Member (Output Message) r => Sem r Doc -> Sem r ()
+ Disco.Messages: debug :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r ()
- Disco.Messages: debugPretty :: (Member (Output Message) r, Pretty t) => t -> Sem r ()
+ Disco.Messages: debugPretty :: (Member (Output (Message ann)) r, Pretty t) => t -> Sem r ()
- Disco.Messages: err :: Member (Output Message) r => Sem r Doc -> Sem r ()
+ Disco.Messages: err :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r ()
- Disco.Messages: handleMsg :: Member (Embed IO) r => (Message -> Bool) -> Message -> Sem r ()
+ Disco.Messages: handleMsg :: Member (Embed IO) r => (Message ann -> Bool) -> Message ann -> Sem r ()
- Disco.Messages: info :: Member (Output Message) r => Sem r Doc -> Sem r ()
+ Disco.Messages: info :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r ()
- Disco.Messages: infoPretty :: (Member (Output Message) r, Pretty t) => t -> Sem r ()
+ Disco.Messages: infoPretty :: (Member (Output (Message ann)) r, Pretty t) => t -> Sem r ()
- Disco.Messages: message :: Lens' Message Doc
+ Disco.Messages: message :: forall ann_aULt ann_aUT4. Lens (Message ann_aULt) (Message ann_aUT4) (Doc ann_aULt) (Doc ann_aUT4)
- Disco.Messages: messageType :: Lens' Message MessageType
+ Disco.Messages: messageType :: forall ann_aULt. Lens' (Message ann_aULt) MessageType
- Disco.Messages: msg :: Member (Output Message) r => MessageType -> Sem r Doc -> Sem r ()
+ Disco.Messages: msg :: Member (Output (Message ann)) r => MessageType -> Sem r (Doc ann) -> Sem r ()
- Disco.Messages: printMsg :: Member (Embed IO) r => Message -> Sem r ()
+ Disco.Messages: printMsg :: Member (Embed IO) r => Message ann -> Sem r ()
- Disco.Messages: warn :: Member (Output Message) r => Sem r Doc -> Sem r ()
+ Disco.Messages: warn :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r ()
- Disco.Parser: parsePattern :: Parser Pattern
+ Disco.Parser: parsePattern :: Bool -> Parser Pattern
- Disco.Pretty: data Doc
+ Disco.Pretty: data Doc ann
- Disco.Pretty: lt :: Member (Reader PA) r => Sem r Doc -> Sem r Doc
+ Disco.Pretty: lt :: Member (Reader PA) r => Sem r (Doc ann) -> Sem r (Doc ann)
- Disco.Pretty: mparens :: Member (Reader PA) r => PA -> Sem r Doc -> Sem r Doc
+ Disco.Pretty: mparens :: Member (Reader PA) r => PA -> Sem r (Doc ann) -> Sem r (Doc ann)
- Disco.Pretty: pretty :: (Pretty t, Members '[Reader PA, LFresh] r) => t -> Sem r Doc
+ Disco.Pretty: pretty :: (Pretty t, Members '[Reader PA, LFresh] r) => t -> Sem r (Doc ann)
- Disco.Pretty: pretty' :: Pretty t => t -> Sem r Doc
+ Disco.Pretty: pretty' :: Pretty t => t -> Sem r (Doc ann)
- Disco.Pretty: rt :: Member (Reader PA) r => Sem r Doc -> Sem r Doc
+ Disco.Pretty: rt :: Member (Reader PA) r => Sem r (Doc ann) -> Sem r (Doc ann)
- Disco.Pretty: withPA :: Member (Reader PA) r => PA -> Sem r Doc -> Sem r Doc
+ Disco.Pretty: withPA :: Member (Reader PA) r => PA -> Sem r (Doc ann) -> Sem r (Doc ann)
- Disco.Pretty.DSL: ($+$) :: Applicative f => f Doc -> f Doc -> f Doc
+ Disco.Pretty.DSL: ($+$) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: (<+>) :: Applicative f => f Doc -> f Doc -> f Doc
+ Disco.Pretty.DSL: (<+>) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: (<>) :: Applicative f => f Doc -> f Doc -> f Doc
+ Disco.Pretty.DSL: (<>) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: bag :: Applicative f => f Doc -> f Doc
+ Disco.Pretty.DSL: bag :: Applicative f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: braces :: Functor f => f Doc -> f Doc
+ Disco.Pretty.DSL: braces :: Functor f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: brackets :: Functor f => f Doc -> f Doc
+ Disco.Pretty.DSL: brackets :: Functor f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: bulletList :: Applicative f => f Doc -> [f Doc] -> f Doc
+ Disco.Pretty.DSL: bulletList :: Applicative f => f (Doc ann) -> [f (Doc ann)] -> f (Doc ann)
- Disco.Pretty.DSL: doubleQuotes :: Functor f => f Doc -> f Doc
+ Disco.Pretty.DSL: doubleQuotes :: Functor f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: empty :: Applicative m => m Doc
+ Disco.Pretty.DSL: empty :: Applicative m => m (Doc ann)
- Disco.Pretty.DSL: hang :: Applicative f => f Doc -> Int -> f Doc -> f Doc
+ Disco.Pretty.DSL: hang :: Applicative f => f (Doc ann) -> Int -> f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: hcat :: Applicative f => [f Doc] -> f Doc
+ Disco.Pretty.DSL: hcat :: Applicative f => [f (Doc ann)] -> f (Doc ann)
- Disco.Pretty.DSL: hsep :: Applicative f => [f Doc] -> f Doc
+ Disco.Pretty.DSL: hsep :: Applicative f => [f (Doc ann)] -> f (Doc ann)
- Disco.Pretty.DSL: integer :: Applicative m => Integer -> m Doc
+ Disco.Pretty.DSL: integer :: Applicative m => Integer -> m (Doc ann)
- Disco.Pretty.DSL: intercalate :: Monad f => f Doc -> [f Doc] -> f Doc
+ Disco.Pretty.DSL: intercalate :: Monad f => f (Doc ann) -> [f (Doc ann)] -> f (Doc ann)
- Disco.Pretty.DSL: nest :: Functor f => Int -> f Doc -> f Doc
+ Disco.Pretty.DSL: nest :: Functor f => Int -> f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: parens :: Functor f => f Doc -> f Doc
+ Disco.Pretty.DSL: parens :: Functor f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: punctuate :: Applicative f => f Doc -> [f Doc] -> f [f Doc]
+ Disco.Pretty.DSL: punctuate :: Applicative f => f (Doc ann) -> [f (Doc ann)] -> f [f (Doc ann)]
- Disco.Pretty.DSL: quotes :: Functor f => f Doc -> f Doc
+ Disco.Pretty.DSL: quotes :: Functor f => f (Doc ann) -> f (Doc ann)
- Disco.Pretty.DSL: renderDoc :: Sem (Reader PA : r) Doc -> Sem r String
+ Disco.Pretty.DSL: renderDoc :: Sem (Reader PA : r) (Doc ann) -> Sem r String
- Disco.Pretty.DSL: renderDoc' :: Doc -> String
+ Disco.Pretty.DSL: renderDoc' :: Doc ann -> String
- Disco.Pretty.DSL: text :: Applicative m => String -> m Doc
+ Disco.Pretty.DSL: text :: Applicative m => String -> m (Doc ann)
- Disco.Pretty.DSL: vcat :: Applicative f => [f Doc] -> f Doc
+ Disco.Pretty.DSL: vcat :: Applicative f => [f (Doc ann)] -> f (Doc ann)
- Disco.Typecheck: checkDefn :: Members '[Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh, Output Message] r => ModuleName -> TermDefn -> Sem r Defn
+ Disco.Typecheck: checkDefn :: Members '[Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh, Output (Message ann)] r => ModuleName -> TermDefn -> Sem r Defn
- Disco.Typecheck: checkModule :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh] r => ModuleName -> Map ModuleName ModuleInfo -> Module -> Sem r ModuleInfo
+ Disco.Typecheck: checkModule :: Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh] r => ModuleName -> Map ModuleName ModuleInfo -> Module -> Sem r ModuleInfo
- Disco.Typecheck: checkProperties :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output Message] r => Ctx Term Docs -> Sem r (Ctx ATerm [AProperty])
+ Disco.Typecheck: checkProperties :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output (Message ann)] r => Ctx Term Docs -> Sem r (Ctx ATerm [AProperty])
- Disco.Typecheck: checkProperty :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output Message] r => Property -> Sem r AProperty
+ Disco.Typecheck: checkProperty :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output (Message ann)] r => Property -> Sem r AProperty
- Disco.Typecheck: checkTop :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r => Term -> PolyType -> Sem r ATerm
+ Disco.Typecheck: checkTop :: Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r => Term -> PolyType -> Sem r ATerm
- Disco.Typecheck: inferTop :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r => Term -> Sem r (ATerm, PolyType)
+ Disco.Typecheck: inferTop :: Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r => Term -> Sem r (ATerm, PolyType)
- Disco.Typecheck.Solve: checkSkolems :: Members '[Error SolveError, Output Message, Input TyDefCtx] r => TyVarInfoMap -> Graph Atom -> Sem r (Graph UAtom, S)
+ Disco.Typecheck.Solve: checkSkolems :: Members '[Error SolveError, Output (Message ann), Input TyDefCtx] r => TyVarInfoMap -> Graph Atom -> Sem r (Graph UAtom, S)
- Disco.Typecheck.Solve: simplify :: Members '[Error SolveError, Output Message, Input TyDefCtx] r => TyVarInfoMap -> [SimpleConstraint] -> Sem r (TyVarInfoMap, [(Atom, Atom)], S)
+ Disco.Typecheck.Solve: simplify :: Members '[Error SolveError, Output (Message ann), Input TyDefCtx] r => TyVarInfoMap -> [SimpleConstraint] -> Sem r (TyVarInfoMap, [(Atom, Atom)], S)
- Disco.Typecheck.Solve: solveConstraint :: Members '[Fresh, Error SolveError, Output Message, Input TyDefCtx] r => Constraint -> Sem r S
+ Disco.Typecheck.Solve: solveConstraint :: Members '[Fresh, Error SolveError, Output (Message ann), Input TyDefCtx] r => Constraint -> Sem r S
- Disco.Typecheck.Solve: solveConstraintChoice :: Members '[Fresh, Error SolveError, Output Message, Input TyDefCtx] r => TyVarInfoMap -> [SimpleConstraint] -> Sem r S
+ Disco.Typecheck.Solve: solveConstraintChoice :: Members '[Fresh, Error SolveError, Output (Message ann), Input TyDefCtx] r => TyVarInfoMap -> [SimpleConstraint] -> Sem r S
- Disco.Typecheck.Solve: solveGraph :: Members '[Fresh, Error SolveError, Output Message] r => TyVarInfoMap -> Graph UAtom -> Sem r S
+ Disco.Typecheck.Solve: solveGraph :: Members '[Fresh, Error SolveError, Output (Message ann)] r => TyVarInfoMap -> Graph UAtom -> Sem r S
- Disco.Typecheck.Util: solve :: Members '[Reader TyDefCtx, Error TCError, Output Message] r => Sem (Writer Constraint : r) a -> Sem r (a, S)
+ Disco.Typecheck.Util: solve :: Members '[Reader TyDefCtx, Error TCError, Output (Message ann)] r => Sem (Writer Constraint : r) a -> Sem r (a, S)
- Disco.Value: prettyValue :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r Doc
+ Disco.Value: prettyValue :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r (Doc ann)
- Disco.Value: prettyValue' :: Member (Input TyDefCtx) r => Type -> Value -> Sem r Doc
+ Disco.Value: prettyValue' :: Member (Input TyDefCtx) r => Type -> Value -> Sem r (Doc ann)

Files

CHANGELOG.md view
@@ -1,3 +1,16 @@+* 0.1.6 (16 December 2023)++  - A bunch more documentation+  - Allow `!=` as a synonym for `/=`+  - Allow `=<` and `=>` as aliases for `<=` and `>=`, respectively+  - Better reporting of test results+  - Load standard library modules immediately on startup+  - `:doc` now works for case expressions, wildcard patterns, `unit`+    and `Unit`+  - Allow optional commas on either side of an ellipsis (#361)+  - Propositions can be combined with binary operators (thanks to+    @cally-cmd)+ * 0.1.5 (12 March 2022)    - Syntax errors in function definitions now result in much more
README.md view
@@ -5,13 +5,96 @@ programming principles in the context of a discrete mathematics course. +Using Disco on replit.com+---------------------------++If you just want to *use* disco (*i.e.* if you are a student, or just+checking out the language), the recommended way is to use it via+`replit.com`.  Simply [visit this+REPL](https://replit.com/@BrentYorgey/Disco#README.md) and follow the+instructions there to fork your own copy, where you will be able to+evaluate Disco expressions, and edit and run your own `.disco` files,+all via your web browser, without installing anything on your computer.++Design principles+-----------------++* Includes those features, and *only* those features, useful in the+  context of a discrete math course. This is *not* intended to be a+  general-purpose language.+* Syntax is as close to standard *mathematical* practice as possible,+  to make it easier for mathematicians to pick up, and to reduce as+  much as possible the incongruity between the language and the+  mathematics being explored and modeled.+* Tooling, error messages, etc. are very important---the language+  needs to be accessible to undergrads with no prior programming+  experience. (However, this principle is, as of yet, only+  that---there is no tooling or nice error messages to speak of.)++Feel free to look around, ask questions, etc.  You can also+[contribute](CONTRIBUTING.md)---collaborators are most welcome.++Community+---------++Check out the disco IRC channel, `#disco-lang` on Libera.Chat.  If+you're not familiar with IRC, you can connect via [this web client](https://kiwiirc.com/nextclient/irc.libera.chat/?nick=Guest?#disco-lang).++Documentation+-------------++Documentation is [hosted on+readthedocs.io](http://disco-lang.readthedocs.io/en/latest/).++Contributing+------------++If you'd like to contribute to disco development, check out+[CONTRIBUTING.md](CONTRIBUTING.md).++Building with stack+-------------------++First, make sure you have+[the `stack` tool](https://docs.haskellstack.org/en/stable/README/)+(the easiest way to install it is via [ghcup](https://www.haskell.org/ghcup/)).+Then open a command prompt, navigate to the root directory of this+repository, and execute++```+stack build+```++After this completes, you should be able to++```+stack exec disco+```++to run the Disco command-line REPL.++While developing, you may want to use a command like++```+stack test --fast --file-watch --ghc-options='-Wall'+```++which will turn on warnings, turn off optimizations for a faster+edit-compile-test cycle, and automatically recompile and run the test+suite every time a source file changes.+ Installation ------------ -If you just want to *use* disco (*i.e.* if you are a student), follow-these instructions.  If you want to *contribute* to disco development,-you should skip to the instructions below about building with stack.+If for some reasons you want to actually install `disco` on your+computer, follow the below instructions.  (If you want to *contribute*+to disco development, you should skip to the instructions about+building with stack.) +**Note**, if you are a student, you should **not** need to do this!+The above instructions about using `disco` on `replit.com` should be+all you need.  The below instructions are kept here for completeness.+ - Follow the instructions to [install   ghcup](https://www.haskell.org/ghcup/) by opening a terminal or   command prompt and copy-pasting the given installation command.  You@@ -110,70 +193,3 @@ workarounds I learn. So even if you encounter a difficulty but figure out the solution youself, let me know --- that way I can include the problem and solution here so others can benefit!--Design principles--------------------* Includes those features, and *only* those features, useful in the-  context of a discrete math course. This is *not* intended to be a-  general-purpose language.-* Syntax is as close to standard *mathematical* practice as possible,-  to make it easier for mathematicians to pick up, and to reduce as-  much as possible the incongruity between the language and the-  mathematics being explored and modeled.-* Tooling, error messages, etc. are very important---the language-  needs to be accessible to undergrads with no prior programming-  experience. (However, this principle is, as of yet, only-  that---there is no tooling or nice error messages to speak of.)--Feel free to look around, ask questions, etc.  You can also-[contribute](CONTRIBUTING.md)---collaborators are most welcome.--Community------------Check out the disco IRC channel, `#disco-lang` on Libera.Chat.  If-you're not familiar with IRC, you can connect via [this web client](https://kiwiirc.com/nextclient/irc.libera.chat/?nick=Guest?#disco-lang).--Documentation----------------Documentation is [hosted on-readthedocs.io](http://disco-lang.readthedocs.io/en/latest/).--Contributing---------------If you'd like to contribute to disco development, check out-[CONTRIBUTING.md](CONTRIBUTING.md).--Building with stack----------------------First, make sure you have-[the `stack` tool](https://docs.haskellstack.org/en/stable/README/)-(the easiest way to install it is via [ghcup](https://www.haskell.org/ghcup/)).-Then open a command prompt, navigate to the root directory of this-repository, and execute--```-stack build-```--After this completes, you should be able to--```-stack exec disco-```--to run the Disco command-line REPL.--While developing, you may want to use a command like--```-stack test --fast --file-watch --ghc-options='-Wall'-```--which will turn on warnings, turn off optimizations for a faster-edit-compile-test cycle, and automatically recompile and run the test-suite every time a source file changes.
disco.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                disco-version:             0.1.5+version:             0.1.6 synopsis:            Functional programming language for teaching discrete math. description:         Disco is a simple functional programming language for use in                      teaching discrete math.  Its syntax is designed to be close@@ -12,14 +12,14 @@ copyright:           Disco team 2016-2022 (see LICENSE) category:            Language -tested-with:         GHC == 8.10.4+tested-with:         GHC ==8.10.7 || ==9.2.5 || ==9.4.3  data-dir:            lib  data-files:          *.disco -extra-source-files:  README.md, CHANGELOG.md, stack.yaml, example/*.disco, repl/*.hs-                     docs/tutorial/example/*.disco+extra-doc-files:     README.md, CHANGELOG.md, example/*.disco, docs/tutorial/example/*.disco+extra-source-files:  stack.yaml, repl/*.hs                      --- TEST FILES BEGIN (updated automatically by add-test-files.hs) ---                      test/README.md                      test/Tests.hs@@ -91,6 +91,8 @@                      test/error-emptycase/input                      test/error-names/expected                      test/error-names/input+                     test/error-nonlinear/expected+                     test/error-nonlinear/input                      test/error-notcon/expected                      test/error-notcon/input                      test/error-notype/expected@@ -132,8 +134,6 @@                      test/interp-strictmatch/bomb.disco                      test/interp-strictmatch/expected                      test/interp-strictmatch/input-                     test/lib-oeis/expected-                     test/lib-oeis/input                      test/list-comp/expected                      test/list-comp/input                      test/list-poly/expected@@ -170,6 +170,8 @@                      test/parse-case-expr/input                      test/parse-nested-list/expected                      test/parse-nested-list/input+                     test/parse-patguard/expected+                     test/parse-patguard/input                      test/parse-quantifiers/expected                      test/parse-quantifiers/input                      test/parse-top-term/expected@@ -189,8 +191,6 @@                      test/pretty-defn/input                      test/pretty-functions/expected                      test/pretty-functions/input-                     test/pretty-issue258/expected-                     test/pretty-issue258/input                      test/pretty-lit/expected                      test/pretty-lit/input                      test/pretty-ops/expected@@ -212,6 +212,8 @@                      test/prop-basic/expected                      test/prop-basic/input                      test/prop-basic/prop-basic.disco+                     test/prop-binary/expected+                     test/prop-binary/input                      test/prop-cmp/expected                      test/prop-cmp/input                      test/prop-fail/bad-tests.disco@@ -280,6 +282,8 @@                      test/syntax-doc/expected                      test/syntax-doc/input                      test/syntax-doc/syntax-doc.disco+                     test/syntax-ellipsis/expected+                     test/syntax-ellipsis/input                      test/syntax-exts/expected                      test/syntax-exts/input                      test/syntax-exts/syntax-exts.disco@@ -315,6 +319,8 @@                      test/types-192/input                      test/types-306/expected                      test/types-306/input+                     test/types-357/expected+                     test/types-357/input                      test/types-bind/expected                      test/types-bind/input                      test/types-char-string/expected@@ -448,34 +454,37 @@   other-modules:       Paths_disco   autogen-modules:     Paths_disco -  build-depends:       base >=4.8 && <4.17,+  build-depends:       base >=4.8 && <4.18,                        filepath,                        directory,-                       mtl >=2.2 && <2.3,-                       megaparsec >= 6.1.1 && < 9.3,+                       mtl >=2.2 && <2.4,+                       megaparsec >= 6.1.1 && < 9.6,                        parser-combinators >= 1.0.0 && < 1.4,-                       pretty >=1.1 && <1.2,+                       prettyprinter >=1.7 && < 1.8,                        split >= 0.2 && < 0.3,                        transformers >= 0.4 && < 0.7,                        containers >=0.5 && <0.7,-                       unbound-generics >= 0.3 && < 0.5,-                       polysemy >= 1.6.0.0 && < 1.8,+                       unbound-generics >= 0.3 && < 0.4.3,+                       -- unbound-generics 0.4.3 added substBvs to+                       -- Subst class which adds a new Generic+                       -- constraint, hence a breaking change+                       polysemy >= 1.6.0.0 && < 1.10,                        polysemy-plugin >= 0.4 && < 0.5,                        polysemy-zoo >= 0.7 && < 0.9,-                       lens >= 4.14 && < 5.2,+                       lens >= 4.14 && < 5.3,                        exact-combinatorics >= 0.2 && < 0.3,-                       arithmoi >= 0.10 && < 0.13,+                       arithmoi >= 0.10 && < 0.14,                        integer-logarithms >= 1.0 && < 1.1,                        simple-enumeration >= 0.2 && < 0.3,                        haskeline >=0.8 && <0.9,                        exceptions >= 0.10 && < 0.11,                        QuickCheck >= 2.9 && < 2.15,                        splitmix >= 0.1 && < 0.2,-                       fgl >= 5.5 && < 5.8,-                       optparse-applicative >= 0.12 && < 0.18,-                       oeis >= 0.3.10,-                       algebraic-graphs >= 0.5,-                       pretty-show >= 1.10+                       fgl >= 5.5 && < 5.9,+                       optparse-applicative >= 0.12 && < 0.19,+                       -- oeis2 < 1.1,+                       algebraic-graphs >= 0.5 && < 0.8,+                       pretty-show >= 1.10 && < 1.11    hs-source-dirs:      src   default-language:    Haskell2010@@ -484,19 +493,20 @@   import:              common   hs-source-dirs:      repl   main-is:             REPL.hs+  ghc-options:         +RTS --io-manager=native -RTS   build-depends:       base,                        disco,                        directory,                        filepath,                        haskeline >=0.8 && <0.9,-                       mtl >=2.2 && <2.3,+                       mtl >=2.2 && <2.4,                        transformers >= 0.4 && < 0.7,-                       megaparsec >= 6.1.1 && < 9.3,+                       megaparsec >= 6.1.1 && < 9.6,                        containers >= 0.5 && < 0.7,-                       unbound-generics >= 0.3 && < 0.5,-                       lens >= 4.14 && < 5.2,-                       optparse-applicative >= 0.12 && < 0.18,-                       oeis >= 0.3.10+                       unbound-generics >= 0.3 && < 0.4.3,+                       lens >= 4.14 && < 5.3,+                       optparse-applicative >= 0.12 && < 0.19+                       -- oeis2 < 1.1    default-language:    Haskell2010 @@ -506,13 +516,13 @@   main-is: Tests.hs   hs-source-dirs: test   ghc-options: -threaded-  build-depends:    base >= 4.7 && < 4.17,-                    tasty >= 0.10 && < 1.5,+  build-depends:    base >= 4.7 && < 4.18,+                    tasty >= 0.10 && < 1.6,                     tasty-golden >= 2.3 && < 2.4,                     directory >= 1.2 && < 1.4,                     filepath >= 1.4 && < 1.5,                     process >= 1.4 && < 1.7,-                    bytestring >= 0.9 && < 0.12,+                    bytestring >= 0.9 && < 0.13,                     disco   default-language: Haskell2010 @@ -522,7 +532,7 @@   main-is: TestExamples.hs   hs-source-dirs: example   ghc-options: -threaded-  build-depends:    base >= 4.7 && < 4.17,+  build-depends:    base >= 4.7 && < 4.18,                     directory >= 1.2 && < 1.4,                     filepath >= 1.4 && < 1.5,                     process >= 1.4 && < 1.7
− example/catalan.disco
@@ -1,19 +0,0 @@-import list-import oeis---- The type of binary tree shapes: empty tree, or a pair of subtrees.-type BT = Unit + BT*BT---- Generate the list of all binary tree shapes of a given size.-treesOfSize : N -> List(BT)-treesOfSize(0)   = [left(■)]-treesOfSize(k+1) =-  [ right (l,r) | x <- [0 .. k], l <- treesOfSize(x), r <- treesOfSize(k .- x) ]---- Compute first few Catalan numbers by brute force.-catalan1 : List(N)-catalan1 = each(\k. length(treesOfSize(k)), [0..4])---- Extend the sequence via the OEIS.-catalan : List(N)-catalan = extendSequence(catalan1)
example/gcd.disco view
@@ -16,6 +16,8 @@ !!! gcd(7,6)   == 1 !!! gcd(12,18) == 6 !!! gcd(0,0)   == 0+!!! forall a:N, b:N. gcd(a,b) divides a /\ gcd(a,b) divides b+!!! forall a:N, b:N, g:N. (g divides a /\ g divides b) ==> g divides gcd(a,b)  gcd : N * N -> N gcd(a,0) = a
example/grid.disco view
@@ -59,12 +59,7 @@ -- in infinite recursion!  One way that would work is to write the -- test as follows: -!!! forall n:Nat. powerIsoPos (powerIsoPos' (n+1)) == (n+1)---- Alternatively, since 'implies' is lazy (i.e. "short-circuiting"),--- we can write--!!! ∀ n : Nat. (n > 0) ==> powerIsoPos (powerIsoPos' n) == n+-- !!! forall n:Nat. powerIsoPos (powerIsoPos' (n+1)) == (n+1)  powerIsoPos : ℕ×ℕ → ℕ powerIsoPos (x,y) = 2^x * (2y + 1)
example/prime.disco view
@@ -47,7 +47,4 @@ !!! isPrime 113  isPrime : N -> Bool-isPrime n =-  {? false      if n <= 1,-     ld n == n  otherwise-  ?}+isPrime n = (n >= 2) and (ld n == n)
example/tree.disco view
@@ -4,7 +4,7 @@ leaf = left(■)  node : N * Tree * Tree -> Tree-node(x, l, r) = right(x, l, r)+node = right  tree1 : Tree tree1 = node(3, node(5, node(1, leaf, leaf), leaf), node(6, node(2, leaf, leaf), node(8, leaf, leaf)))
− lib/oeis.disco
@@ -1,13 +0,0 @@-using Primitives--||| Look up a sequence of integers using https://oeis.org-!!!  lookupSequence [] == left(unit)-!!!  lookupSequence [1,1,2,3] == right "https://oeis.org/A000045"-lookupSequence : List(N) -> Unit + List(Char)-lookupSequence = $lookupSequence--||| Extend a known sequence of integers with data from https://oeis.org-!!!  extendSequence [] == []-!!!  extendSequence [1,1,2,3,5] == [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]-extendSequence : List(N) -> List(N)-extendSequence = $extendSequence
repl/REPL.hs view
@@ -1,5 +1,5 @@ -------------------------------------------------------------------------------- |+-- \| -- Module      :  REPL -- Copyright   :  disco team and contributors -- Maintainer  :  byorgey@gmail.com@@ -7,10 +7,9 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- A text-based REPL for disco.--- ----------------------------------------------------------------------------- -import           Disco.Interactive.CmdLine+import Disco.Interactive.CmdLine  main :: IO () main = discoMain
src/Disco/AST/Core.hs view
@@ -1,10 +1,9 @@-{-# LANGUAGE DeriveAnyClass           #-}-{-# LANGUAGE DeriveDataTypeable       #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE NondecreasingIndentation #-}-{-# LANGUAGE OverloadedStrings        #-}-{-# LANGUAGE UndecidableInstances     #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE UndecidableInstances #-} ------------------------------------------------------------------------------ -- | -- Module      :  Disco.AST.Core -- Copyright   :  disco team and contributors@@ -14,34 +13,35 @@ -- -- Abstract syntax trees representing the desugared, untyped core -- language for Disco.--------------------------------------------------------------------------------module Disco.AST.Core-       ( -- * Core AST-         RationalDisplay(..)-       , Core(..)-       , Op(..), opArity, substQC, substsQC-       )-       where+module Disco.AST.Core (+  -- * Core AST+  RationalDisplay (..),+  Core (..),+  Op (..),+  opArity,+  substQC,+  substsQC,+)+where -import           Control.Lens.Plated-import           Data.Data                        (Data)-import           Data.Data.Lens                   (uniplate)-import qualified Data.Set                         as S-import           GHC.Generics-import           Prelude                          hiding ((<>))-import qualified Prelude                          as P-import           Unbound.Generics.LocallyNameless hiding (LFresh, lunbind)+import Control.Lens.Plated+import Data.Data (Data)+import Data.Data.Lens (uniplate)+import qualified Data.Set as S+import GHC.Generics+import Unbound.Generics.LocallyNameless hiding (LFresh, lunbind)+import Prelude hiding ((<>))+import qualified Prelude as P -import           Disco.Effects.LFresh-import           Polysemy                         (Members, Sem)-import           Polysemy.Reader+import Disco.Effects.LFresh+import Polysemy (Members, Sem)+import Polysemy.Reader -import           Data.Ratio-import           Disco.AST.Generic                (Side, selectSide)-import           Disco.Names                      (QName)-import           Disco.Pretty-import           Disco.Types+import Data.Ratio+import Disco.AST.Generic (Side, selectSide)+import Disco.Names (QName)+import Disco.Pretty+import Disco.Types  -- | A type of flags specifying whether to display a rational number --   as a fraction or a decimal.@@ -51,7 +51,7 @@ instance Semigroup RationalDisplay where   Decimal <> _ = Decimal   _ <> Decimal = Decimal-  _ <> _       = Fraction+  _ <> _ = Fraction  -- | The 'Monoid' instance for 'RationalDisplay' corresponds to the --   idea that the result should be displayed as a decimal if any@@ -231,8 +231,8 @@   | -- | Equality assertion, @=!=@     OShouldEq Type   | -- Other primitives--    -- | Error for non-exhaustive pattern match+    OShouldLt Type+  | -- | Error for non-exhaustive pattern match     OMatchErr   | -- | Crash with a user-supplied message     OCrash@@ -242,6 +242,15 @@     OLookupSeq   | -- | Extend a List via OEIS     OExtendSeq+  | -- | Not the Boolean `And`, but instead a propositional BOp+    -- | Should only be seen and used with Props.+    OAnd+  | -- | Not the Boolean `Or`, but instead a propositional BOp+    -- | Should only be seen and used with Props.+    OOr+  | -- | Not the Boolean `Impl`, but instead a propositional BOp+    -- | Should only be seen and used with Props.+    OImpl   deriving (Show, Generic, Data, Alpha, Eq, Ord)  -- | Get the arity (desired number of arguments) of a function@@ -249,8 +258,8 @@ --   uncurried and hence has arity 1. opArity :: Op -> Int opArity OEmptyGraph = 0-opArity OMatchErr   = 0-opArity _           = 1+opArity OMatchErr = 0+opArity _ = 1  substQC :: QName Core -> Core -> Core -> Core substQC x s = transform $ \case@@ -263,63 +272,66 @@ substsQC xs = transform $ \case   CVar y -> case P.lookup y xs of     Just c -> c-    _      -> CVar y+    _ -> CVar y   t -> t  instance Pretty Core where   pretty = \case-    CVar qn         -> pretty qn+    CVar qn -> pretty qn     CNum _ r       | denominator r == 1 -> text (show (numerator r))-      | otherwise          -> text (show (numerator r)) <> "/" <> text (show (denominator r))+      | otherwise -> text (show (numerator r)) <> "/" <> text (show (denominator r))     CApp (CConst op) (CPair c1 c2)       | isInfix op -> parens (pretty c1 <+> text (opToStr op) <+> pretty c2)     CApp (CConst op) c       | isPrefix op -> text (opToStr op) <> pretty c       | isPostfix op -> pretty c <> text (opToStr op)-    CConst op       -> pretty op-    CInj s c        -> withPA funPA $ selectSide s "left" "right" <+> rt (pretty c)-    CCase c l r     -> do+    CConst op -> pretty op+    CInj s c -> withPA funPA $ selectSide s "left" "right" <+> rt (pretty c)+    CCase c l r -> do       lunbind l $ \(x, lc) -> do-      lunbind r $ \(y, rc) -> do-        "case" <+> pretty c <+> "of {"-          $+$ nest 2 (-            vcat-            [ withPA funPA $ "left" <+> rt (pretty x) <+> "->" <+> pretty lc-            , withPA funPA $ "right" <+> rt (pretty y) <+> "->" <+> pretty rc-            ])-          $+$ "}"-    CUnit           -> "unit"-    CPair c1 c2     -> setPA initPA $ parens (pretty c1 <> ", " <> pretty c2)-    CProj s c       -> withPA funPA $ selectSide s "fst" "snd" <+> rt (pretty c)-    CAbs lam        -> withPA initPA $ do+        lunbind r $ \(y, rc) -> do+          nest 2 $+            "case"+              <+> pretty c+              <+> "of {"+              $+$ vcat+                [ withPA funPA $ "left" <+> rt (pretty x) <+> "->" <+> pretty lc+                , withPA funPA $ "right" <+> rt (pretty y) <+> "->" <+> pretty rc+                ]+              $+$ "}"+    CUnit -> "unit"+    CPair c1 c2 -> setPA initPA $ parens (pretty c1 <> ", " <> pretty c2)+    CProj s c -> withPA funPA $ selectSide s "fst" "snd" <+> rt (pretty c)+    CAbs lam -> withPA initPA $ do       lunbind lam $ \(xs, body) -> "λ" <> intercalate "," (map pretty xs) <> "." <+> lt (pretty body)-    CApp c1 c2      -> withPA funPA $ lt (pretty c1) <+> rt (pretty c2)-    CTest xs c      -> "test" <+> prettyTestVars xs <+> pretty c-    CType ty        -> pretty ty-    CDelay d        -> withPA initPA $ do+    CApp c1 c2 -> withPA funPA $ lt (pretty c1) <+> rt (pretty c2)+    CTest xs c -> "test" <+> prettyTestVars xs <+> pretty c+    CType ty -> pretty ty+    CDelay d -> withPA initPA $ do       lunbind d $ \(xs, bodies) ->         "delay" <+> intercalate "," (map pretty xs) <> "." <+> pretty (toTuple bodies)-    CForce c        -> withPA funPA $ "force" <+> rt (pretty c)+    CForce c -> withPA funPA $ "force" <+> rt (pretty c)  toTuple :: [Core] -> Core toTuple = foldr CPair CUnit -prettyTestVars :: Members '[Reader PA, LFresh] r => [(String, Type, Name Core)] -> Sem r Doc+prettyTestVars :: Members '[Reader PA, LFresh] r => [(String, Type, Name Core)] -> Sem r (Doc ann) prettyTestVars = brackets . intercalate "," . map prettyTestVar-  where-    prettyTestVar (s, ty, n) = parens (intercalate "," [text s, pretty ty, pretty n])+ where+  prettyTestVar (s, ty, n) = parens (intercalate "," [text s, pretty ty, pretty n])  isInfix, isPrefix, isPostfix :: Op -> Bool-isInfix OShouldEq{} = True-isInfix op = op `S.member` S.fromList-  [ OAdd, OMul, ODiv, OExp, OMod, ODivides, OMultinom, OEq, OLt]-+isInfix OShouldEq {} = True+isInfix OShouldLt {} = True+isInfix op =+  op+    `S.member` S.fromList+      [OAdd, OMul, ODiv, OExp, OMod, ODivides, OMultinom, OEq, OLt, OAnd, OOr, OImpl] isPrefix ONeg = True-isPrefix _    = False-+isPrefix _ = False isPostfix OFact = True-isPostfix _     = False+isPostfix _ = False  instance Pretty Op where   pretty (OForall tys) = "∀" <> intercalate "," (map pretty tys) <> "."@@ -328,63 +340,67 @@     | isInfix op = "~" <> text (opToStr op) <> "~"     | isPrefix op = text (opToStr op) <> "~"     | isPostfix op = "~" <> text (opToStr op)-    | otherwise  = text (opToStr op)+    | otherwise = text (opToStr op)  opToStr :: Op -> String opToStr = \case-  OAdd               -> "+"-  ONeg               -> "-"-  OSqrt              -> "sqrt"-  OFloor             -> "floor"-  OCeil              -> "ceil"-  OAbs               -> "abs"-  OMul               -> "*"-  ODiv               -> "/"-  OExp               -> "^"-  OMod               -> "mod"-  ODivides           -> "divides"-  OMultinom          -> "choose"-  OFact              -> "!"-  OEq                -> "=="-  OLt                -> "<"-  OEnum              -> "enumerate"-  OCount             -> "count"-  OPower             -> "power"-  OBagElem           -> "elem_bag"-  OListElem          -> "elem_list"-  OEachBag           -> "each_bag"-  OEachSet           -> "each_set"-  OFilterBag         -> "filter_bag"-  OMerge             -> "merge"-  OBagUnions         -> "unions_bag"-  OSummary           -> "summary"-  OEmptyGraph        -> "emptyGraph"-  OVertex            -> "vertex"-  OOverlay           -> "overlay"-  OConnect           -> "connect"-  OInsert            -> "insert"-  OLookup            -> "lookup"-  OUntil             -> "until"-  OSetToList         -> "set2list"-  OBagToSet          -> "bag2set"-  OBagToList         -> "bag2list"-  OListToSet         -> "list2set"-  OListToBag         -> "list2bag"-  OBagToCounts       -> "bag2counts"-  OCountsToBag       -> "counts2bag"+  OAdd -> "+"+  ONeg -> "-"+  OSqrt -> "sqrt"+  OFloor -> "floor"+  OCeil -> "ceil"+  OAbs -> "abs"+  OMul -> "*"+  ODiv -> "/"+  OExp -> "^"+  OMod -> "mod"+  ODivides -> "divides"+  OMultinom -> "choose"+  OFact -> "!"+  OEq -> "=="+  OLt -> "<"+  OEnum -> "enumerate"+  OCount -> "count"+  OPower -> "power"+  OBagElem -> "elem_bag"+  OListElem -> "elem_list"+  OEachBag -> "each_bag"+  OEachSet -> "each_set"+  OFilterBag -> "filter_bag"+  OMerge -> "merge"+  OBagUnions -> "unions_bag"+  OSummary -> "summary"+  OEmptyGraph -> "emptyGraph"+  OVertex -> "vertex"+  OOverlay -> "overlay"+  OConnect -> "connect"+  OInsert -> "insert"+  OLookup -> "lookup"+  OUntil -> "until"+  OSetToList -> "set2list"+  OBagToSet -> "bag2set"+  OBagToList -> "bag2list"+  OListToSet -> "list2set"+  OListToBag -> "list2bag"+  OBagToCounts -> "bag2counts"+  OCountsToBag -> "counts2bag"   OUnsafeCountsToBag -> "ucounts2bag"-  OMapToSet          -> "map2set"-  OSetToMap          -> "set2map"-  OIsPrime           -> "isPrime"-  OFactor            -> "factor"-  OFrac              -> "frac"-  OHolds             -> "holds"-  ONotProp           -> "not"-  OShouldEq _        -> "=!="-  OMatchErr          -> "matchErr"-  OCrash             -> "crash"-  OId                -> "id"-  OLookupSeq         -> "lookupSeq"-  OExtendSeq         -> "extendSeq"-  OForall{}          -> "∀"-  OExists{}          -> "∃"+  OMapToSet -> "map2set"+  OSetToMap -> "set2map"+  OIsPrime -> "isPrime"+  OFactor -> "factor"+  OFrac -> "frac"+  OHolds -> "holds"+  ONotProp -> "not"+  OShouldEq _ -> "=!="+  OShouldLt _ -> "!<"+  OMatchErr -> "matchErr"+  OCrash -> "crash"+  OId -> "id"+  OLookupSeq -> "lookupSeq"+  OExtendSeq -> "extendSeq"+  OForall {} -> "∀"+  OExists {} -> "∃"+  OAnd -> "and"+  OOr -> "or"+  OImpl -> "implies"
src/Disco/AST/Desugared.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE PatternSynonyms #-} ------------------------------------------------------------------------------ -- | -- Module      :  Disco.AST.Desugared -- Copyright   :  disco team and contributors@@ -10,57 +9,51 @@ -- -- Typed abstract syntax trees representing the typechecked, desugared -- Disco language.-----------------------------------------------------------------------------------module Disco.AST.Desugared-       ( -- * Desugared, type-annotated terms-       DTerm-       , pattern DTVar-       , pattern DTPrim-       , pattern DTUnit-       , pattern DTBool-       , pattern DTChar-       , pattern DTNat-       , pattern DTRat-       , pattern DTAbs-       , pattern DTApp-       , pattern DTPair-       , pattern DTCase-       , pattern DTTyOp-       , pattern DTNil-       , pattern DTTest--       , Container(..)-       , DBinding-       , pattern DBinding-         -- * Branches and guards-       , DBranch--       , DGuard-       , pattern DGPat--       , DPattern-       , pattern DPVar-       , pattern DPWild-       , pattern DPUnit-       , pattern DPPair-       , pattern DPInj+module Disco.AST.Desugared (+  -- * Desugared, type-annotated terms+  DTerm,+  pattern DTVar,+  pattern DTPrim,+  pattern DTUnit,+  pattern DTBool,+  pattern DTChar,+  pattern DTNat,+  pattern DTRat,+  pattern DTAbs,+  pattern DTApp,+  pattern DTPair,+  pattern DTCase,+  pattern DTTyOp,+  pattern DTNil,+  pattern DTTest,+  Container (..),+  DBinding,+  pattern DBinding, -       , DProperty-       )-       where+  -- * Branches and guards+  DBranch,+  DGuard,+  pattern DGPat,+  DPattern,+  pattern DPVar,+  pattern DPWild,+  pattern DPUnit,+  pattern DPPair,+  pattern DPInj,+  DProperty,+)+where -import           GHC.Generics+import GHC.Generics -import           Data.Void-import           Unbound.Generics.LocallyNameless+import Data.Void+import Unbound.Generics.LocallyNameless -import           Disco.AST.Generic-import           Disco.Names                      (QName (..))-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Types+import Disco.AST.Generic+import Disco.Names (QName (..))+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Types  data DS @@ -69,36 +62,37 @@ -- | A @DTerm@ is a term which has been typechecked and desugared, so --   it has fewer constructors and complex features than 'ATerm', but --   still retains typing information.- type DTerm = Term_ DS -type instance X_Binder DS         = Name DTerm+type instance X_Binder DS = Name DTerm -type instance X_TVar DS           = Void -- names are qualified-type instance X_TPrim DS          = Type-type instance X_TLet DS           = Void -- Let gets translated to lambda-type instance X_TUnit DS          = ()-type instance X_TBool DS          = Type-type instance X_TChar DS          = ()-type instance X_TString DS        = Void-type instance X_TNat DS           = Type-type instance X_TRat DS           = ()-type instance X_TAbs DS           = Type -- For lambas this is the function type but-                                         -- for forall/exists it's the argument type-type instance X_TApp DS           = Type-type instance X_TCase DS          = Type-type instance X_TChain DS         = Void -- Chains are translated into conjunctions of-                                         -- binary comparisons-type instance X_TTyOp DS          = Type-type instance X_TContainer DS     = Void -- Literal containers are desugared into-                                         -- conversion functions applied to list literals+type instance X_TVar DS = Void -- names are qualified+type instance X_TPrim DS = Type+type instance X_TLet DS = Void -- Let gets translated to lambda+type instance X_TUnit DS = ()+type instance X_TBool DS = Type+type instance X_TChar DS = ()+type instance X_TString DS = Void+type instance X_TNat DS = Type+type instance X_TRat DS = ()+type instance X_TAbs DS = Type -- For lambas this is the function type but+-- for forall/exists it's the argument type +type instance X_TApp DS = Type+type instance X_TCase DS = Type+type instance X_TChain DS = Void -- Chains are translated into conjunctions of+-- binary comparisons++type instance X_TTyOp DS = Type+type instance X_TContainer DS = Void -- Literal containers are desugared into+-- conversion functions applied to list literals+ type instance X_TContainerComp DS = Void -- Container comprehensions are translated-                                         -- into monadic chains+-- into monadic chains -type instance X_TAscr DS          = Void -- No type ascriptions-type instance X_TTup DS           = Void -- No tuples, only pairs-type instance X_TParens DS        = Void -- No explicit parens+type instance X_TAscr DS = Void -- No type ascriptions+type instance X_TTup DS = Void -- No tuples, only pairs+type instance X_TParens DS = Void -- No explicit parens  -- Extra constructors type instance X_Term DS = X_DTerm@@ -125,7 +119,7 @@ pattern DTBool :: Type -> Bool -> DTerm pattern DTBool ty bool = TBool_ ty bool -pattern DTNat  :: Type -> Integer -> DTerm+pattern DTNat :: Type -> Integer -> DTerm pattern DTNat ty int = TNat_ ty int  pattern DTRat :: Rational -> DTerm@@ -137,7 +131,7 @@ pattern DTAbs :: Quantifier -> Type -> Bind (Name DTerm) DTerm -> DTerm pattern DTAbs q ty lam = TAbs_ q ty lam -pattern DTApp  :: Type -> DTerm -> DTerm -> DTerm+pattern DTApp :: Type -> DTerm -> DTerm -> DTerm pattern DTApp ty term1 term2 = TApp_ ty term1 term2  pattern DTPair :: Type -> DTerm -> DTerm -> DTerm@@ -158,9 +152,22 @@ pattern DTTest :: [(String, Type, Name DTerm)] -> DTerm -> DTerm pattern DTTest ns t = XTerm_ (DTTest_ ns t) -{-# COMPLETE DTVar, DTPrim, DTUnit, DTBool, DTChar, DTNat, DTRat,-             DTAbs, DTApp, DTPair, DTCase, DTTyOp,-             DTNil, DTTest #-}+{-# COMPLETE+  DTVar+  , DTPrim+  , DTUnit+  , DTBool+  , DTChar+  , DTNat+  , DTRat+  , DTAbs+  , DTApp+  , DTPair+  , DTCase+  , DTTyOp+  , DTNil+  , DTTest+  #-}  type instance X_TLink DS = Void @@ -175,9 +182,9 @@  type DGuard = Guard_ DS -type instance X_GBool DS = Void   -- Boolean guards get desugared to pattern-matching-type instance X_GPat  DS = ()-type instance X_GLet  DS = Void   -- Let gets desugared to 'when' with a variable+type instance X_GBool DS = Void -- Boolean guards get desugared to pattern-matching+type instance X_GPat DS = ()+type instance X_GLet DS = Void -- Let gets desugared to 'when' with a variable  pattern DGPat :: Embed DTerm -> DPattern -> DGuard pattern DGPat embedt pat = GPat_ () embedt pat@@ -186,23 +193,23 @@  type DPattern = Pattern_ DS -type instance X_PVar     DS = Embed Type-type instance X_PWild    DS = Embed Type-type instance X_PAscr    DS = Void-type instance X_PUnit    DS = ()-type instance X_PBool    DS = Void-type instance X_PChar    DS = Void-type instance X_PString  DS = Void-type instance X_PTup     DS = Void-type instance X_PInj     DS = Void-type instance X_PNat     DS = Void-type instance X_PCons    DS = Void-type instance X_PList    DS = Void-type instance X_PAdd     DS = Void-type instance X_PMul     DS = Void-type instance X_PSub     DS = Void-type instance X_PNeg     DS = Void-type instance X_PFrac    DS = Void+type instance X_PVar DS = Embed Type+type instance X_PWild DS = Embed Type+type instance X_PAscr DS = Void+type instance X_PUnit DS = ()+type instance X_PBool DS = Void+type instance X_PChar DS = Void+type instance X_PString DS = Void+type instance X_PTup DS = Void+type instance X_PInj DS = Void+type instance X_PNat DS = Void+type instance X_PCons DS = Void+type instance X_PList DS = Void+type instance X_PAdd DS = Void+type instance X_PMul DS = Void+type instance X_PSub DS = Void+type instance X_PNeg DS = Void+type instance X_PFrac DS = Void  -- In the desugared language, constructor patterns (DPPair, DPInj) can -- only contain variables, not nested patterns.  This means that the@@ -210,10 +217,11 @@ -- exploding nested patterns into sequential guards, which makes the -- interpreter simpler. -type instance X_Pattern  DS =-  Either-    (Embed Type, Name DTerm, Name DTerm)     -- DPPair-    (Embed Type, Side, Name DTerm)           -- DPInj+type instance+  X_Pattern DS =+    Either+      (Embed Type, Name DTerm, Name DTerm) -- DPPair+      (Embed Type, Side, Name DTerm) -- DPInj  pattern DPVar :: Type -> Name DTerm -> DPattern pattern DPVar ty name <- PVar_ (unembed -> ty) name@@ -228,19 +236,19 @@ pattern DPUnit :: DPattern pattern DPUnit = PUnit_ () -pattern DPPair  :: Type -> Name DTerm -> Name DTerm -> DPattern+pattern DPPair :: Type -> Name DTerm -> Name DTerm -> DPattern pattern DPPair ty x1 x2 <- XPattern_ (Left (unembed -> ty, x1, x2))   where     DPPair ty x1 x2 = XPattern_ (Left (embed ty, x1, x2)) -pattern DPInj  :: Type -> Side -> Name DTerm -> DPattern+pattern DPInj :: Type -> Side -> Name DTerm -> DPattern pattern DPInj ty s x <- XPattern_ (Right (unembed -> ty, s, x))   where     DPInj ty s x = XPattern_ (Right (embed ty, s, x))  {-# COMPLETE DPVar, DPWild, DPUnit, DPPair, DPInj #-} -type instance X_QBind  DS = Void+type instance X_QBind DS = Void type instance X_QGuard DS = Void  ------------------------------------------------------------@@ -248,25 +256,25 @@ ------------------------------------------------------------  instance HasType DTerm where-  getType (DTVar ty _)     = ty-  getType (DTPrim ty _)    = ty-  getType DTUnit           = TyUnit-  getType (DTBool ty _)    = ty-  getType (DTChar _)       = TyC-  getType (DTNat ty _)     = ty-  getType (DTRat _)        = TyF+  getType (DTVar ty _) = ty+  getType (DTPrim ty _) = ty+  getType DTUnit = TyUnit+  getType (DTBool ty _) = ty+  getType (DTChar _) = TyC+  getType (DTNat ty _) = ty+  getType (DTRat _) = TyF   getType (DTAbs Lam ty _) = ty-  getType DTAbs{}          = TyProp-  getType (DTApp ty _ _)   = ty-  getType (DTPair ty _ _)  = ty-  getType (DTCase ty _)    = ty-  getType (DTTyOp ty _ _)  = ty-  getType (DTNil ty)       = ty-  getType (DTTest _ _)     = TyProp+  getType DTAbs {} = TyProp+  getType (DTApp ty _ _) = ty+  getType (DTPair ty _ _) = ty+  getType (DTCase ty _) = ty+  getType (DTTyOp ty _ _) = ty+  getType (DTNil ty) = ty+  getType (DTTest _ _) = TyProp  instance HasType DPattern where-  getType (DPVar ty _)    = ty-  getType (DPWild ty)     = ty-  getType DPUnit          = TyUnit+  getType (DPVar ty _) = ty+  getType (DPWild ty) = ty+  getType DPUnit = TyUnit   getType (DPPair ty _ _) = ty-  getType (DPInj ty _ _)  = ty+  getType (DPInj ty _ _) = ty
src/Disco/AST/Generic.hs view
@@ -1,15 +1,19 @@-{-# LANGUAGE ConstraintKinds      #-}-{-# LANGUAGE DeriveAnyClass       #-}-{-# LANGUAGE DeriveDataTypeable   #-}-{-# LANGUAGE DeriveTraversable    #-}-{-# LANGUAGE OverloadedStrings    #-}-{-# LANGUAGE StandaloneDeriving   #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE UndecidableInstances #-}- -- Orphan Alpha Void instance {-# OPTIONS_GHC -fno-warn-orphans #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.AST.Generic -- Copyright   :  disco team and contributors@@ -36,128 +40,117 @@ -- underlying type.  Particular instantiations of the generic -- framework here can be found in "Disco.AST.Surface", -- "Disco.AST.Typed", and "Disco.AST.Desugared".---------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.AST.Generic-       ( -- * Telescopes--         Telescope (..), telCons-       , foldTelescope, mapTelescope-       , traverseTelescope-       , toTelescope, fromTelescope--         -- * Utility types--       , Side (..), selectSide, fromSide-       , Container (..)-       , Ellipsis (..)--         -- * Term--       , Term_ (..)--       , X_TVar-       , X_TPrim-       , X_TLet-       , X_TParens-       , X_TUnit-       , X_TBool-       , X_TNat-       , X_TRat-       , X_TChar-       , X_TString-       , X_TAbs-       , X_TApp-       , X_TTup-       , X_TCase-       , X_TChain-       , X_TTyOp-       , X_TContainer-       , X_TContainerComp-       , X_TAscr-       , X_Term--       , ForallTerm--       -- * Link--       , Link_ (..)-       , X_TLink-       , ForallLink--       -- * Qual--       , Qual_ (..)-       , X_QBind-       , X_QGuard-       , ForallQual--       -- * Binding+module Disco.AST.Generic (+  -- * Telescopes+  Telescope (..),+  telCons,+  foldTelescope,+  mapTelescope,+  traverseTelescope,+  toTelescope,+  fromTelescope, -       , Binding_ (..)+  -- * Utility types+  Side (..),+  selectSide,+  fromSide,+  Container (..),+  Ellipsis (..), -       -- * Branch-       , Branch_+  -- * Term+  Term_ (..),+  X_TVar,+  X_TPrim,+  X_TLet,+  X_TParens,+  X_TUnit,+  X_TBool,+  X_TNat,+  X_TRat,+  X_TChar,+  X_TString,+  X_TAbs,+  X_TApp,+  X_TTup,+  X_TCase,+  X_TChain,+  X_TTyOp,+  X_TContainer,+  X_TContainerComp,+  X_TAscr,+  X_Term,+  ForallTerm, -       -- * Guard+  -- * Link+  Link_ (..),+  X_TLink,+  ForallLink, -       , Guard_ (..)-       , X_GBool-       , X_GPat-       , X_GLet-       , ForallGuard+  -- * Qual+  Qual_ (..),+  X_QBind,+  X_QGuard,+  ForallQual, -       -- * Pattern+  -- * Binding+  Binding_ (..), -       , Pattern_ (..)-       , X_PVar-       , X_PWild-       , X_PAscr-       , X_PUnit-       , X_PBool-       , X_PTup-       , X_PInj-       , X_PNat-       , X_PChar-       , X_PString-       , X_PCons-       , X_PList-       , X_PAdd-       , X_PMul-       , X_PSub-       , X_PNeg-       , X_PFrac-       , X_Pattern-       , ForallPattern+  -- * Branch+  Branch_, -       -- * Quantifiers+  -- * Guard+  Guard_ (..),+  X_GBool,+  X_GPat,+  X_GLet,+  ForallGuard, -       , Quantifier(..)-       , Binder_-       , X_Binder+  -- * Pattern+  Pattern_ (..),+  X_PVar,+  X_PWild,+  X_PAscr,+  X_PUnit,+  X_PBool,+  X_PTup,+  X_PInj,+  X_PNat,+  X_PChar,+  X_PString,+  X_PCons,+  X_PList,+  X_PAdd,+  X_PMul,+  X_PSub,+  X_PNeg,+  X_PFrac,+  X_Pattern,+  ForallPattern, -       -- * Property+  -- * Quantifiers+  Quantifier (..),+  Binder_,+  X_Binder, -       , Property_-       )-       where+  -- * Property+  Property_,+)+where -import           Control.Lens.Plated-import           Data.Data                        (Data)-import           Data.Data.Lens                   (uniplate)-import           Data.Typeable-import           GHC.Exts                         (Constraint)-import           GHC.Generics                     (Generic)+import Control.Lens.Plated+import Data.Data (Data)+import Data.Data.Lens (uniplate)+import Data.Typeable+import GHC.Exts (Constraint)+import GHC.Generics (Generic) -import           Data.Void-import           Unbound.Generics.LocallyNameless+import Data.Void+import Unbound.Generics.LocallyNameless -import           Disco.Pretty-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Types+import Disco.Pretty+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Types  ------------------------------------------------------------ -- Telescopes@@ -166,13 +159,11 @@ -- | A telescope is essentially a list, except that each item can bind --   names in the rest of the list. data Telescope b where-   -- | The empty telescope.   TelEmpty :: Telescope b-   -- | A binder of type @b@ followed by zero or more @b@'s.  This @b@   --   can bind variables in the subsequent @b@'s.-  TelCons  :: Rebind b (Telescope b) -> Telescope b+  TelCons :: Rebind b (Telescope b) -> Telescope b   deriving (Show, Generic, Alpha, Subst t, Data)  -- | Add a new item to the beginning of a 'Telescope'.@@ -182,17 +173,19 @@ -- | Fold a telescope given a combining function and a value to use --   for the empty telescope.  Analogous to 'foldr' for lists. foldTelescope :: Alpha b => (b -> r -> r) -> r -> Telescope b -> r-foldTelescope _ z TelEmpty                       = z-foldTelescope f z (TelCons (unrebind -> (b,bs))) = f b (foldTelescope f z bs)+foldTelescope _ z TelEmpty = z+foldTelescope f z (TelCons (unrebind -> (b, bs))) = f b (foldTelescope f z bs)  -- | Apply a function to every item in a telescope. mapTelescope :: (Alpha a, Alpha b) => (a -> b) -> Telescope a -> Telescope b mapTelescope f = toTelescope . map f . fromTelescope  -- | Traverse over a telescope.-traverseTelescope-  :: (Applicative f, Alpha a, Alpha b)-  => (a -> f b) -> Telescope a -> f (Telescope b)+traverseTelescope ::+  (Applicative f, Alpha a, Alpha b) =>+  (a -> f b) ->+  Telescope a ->+  f (Telescope b) traverseTelescope f = foldTelescope (\a ftb -> telCons <$> f a <*> ftb) (pure TelEmpty)  -- | Convert a list to a telescope.@@ -230,8 +223,8 @@ --   lists, bags, and sets. data Container where   ListContainer :: Container-  BagContainer  :: Container-  SetContainer  :: Container+  BagContainer :: Container+  SetContainer :: Container   deriving (Show, Eq, Enum, Generic, Data, Alpha, Subst t)  -- | An ellipsis is an "omitted" part of a literal container (such as@@ -240,7 +233,7 @@ --   containers must be finite. data Ellipsis t where   -- | 'Until' represents an ellipsis with a given endpoint, as in @[3 .. 20]@.-  Until   :: t -> Ellipsis t   -- @.. t@+  Until :: t -> Ellipsis t -- @.. t@   deriving (Show, Generic, Functor, Foldable, Traversable, Alpha, Subst a, Data)  ------------------------------------------------------------@@ -276,43 +269,32 @@ --   example, in the typed phase many constructors store an extra --   type, giving the type of the term. data Term_ e where-   -- | A term variable.-  TVar_   :: X_TVar e -> Name (Term_ e) -> Term_ e-+  TVar_ :: X_TVar e -> Name (Term_ e) -> Term_ e   -- | A primitive, /i.e./ a constant  which is interpreted specially   --   at runtime.  See "Disco.Syntax.Prims".-  TPrim_  :: X_TPrim e -> Prim -> Term_ e-+  TPrim_ :: X_TPrim e -> Prim -> Term_ e   -- | A (non-recursive) let expression, @let x1 = t1, x2 = t2, ... in t@.-  TLet_   :: X_TLet e -> Bind (Telescope (Binding_ e)) (Term_ e) -> Term_ e-+  TLet_ :: X_TLet e -> Bind (Telescope (Binding_ e)) (Term_ e) -> Term_ e   -- | Explicit parentheses.  We need to keep track of these in the   --   surface syntax in order to syntactically distinguish   --   multiplication and function application.  However, note that   --   these disappear after the surface syntax phase.   TParens_ :: X_TParens e -> Term_ e -> Term_ e-   -- | The unit value, (), of type Unit.-  TUnit_  :: X_TUnit e -> Term_ e-+  TUnit_ :: X_TUnit e -> Term_ e   -- | A boolean value.-  TBool_  :: X_TBool e -> Bool -> Term_ e-+  TBool_ :: X_TBool e -> Bool -> Term_ e   -- | A natural number.-  TNat_   :: X_TNat e -> Integer -> Term_ e-+  TNat_ :: X_TNat e -> Integer -> Term_ e   -- | A nonnegative rational number, parsed as a decimal.  (Note   --   syntax like @3/5@ does not parse as a rational, but rather as   --   the application of a division operator to two natural numbers.)-  TRat_   :: X_TRat e -> Rational -> Term_ e-+  TRat_ :: X_TRat e -> Rational -> Term_ e   -- | A literal unicode character, /e.g./ @'d'@.-  TChar_  :: X_TChar e -> Char -> Term_ e-+  TChar_ :: X_TChar e -> Char -> Term_ e   -- | A string literal, /e.g./ @"disco"@.   TString_ :: X_TString e -> [Char] -> Term_ e-   -- | A binding abstraction, of the form @Q vars. expr@ where @Q@ is   --   a quantifier and @vars@ is a list of bound variables and   --   optional type annotations.  In particular, this could be a@@ -320,77 +302,68 @@   --   (y:N). 2x + y@), a universal quantifier (@forall x, (y:N). x^2 +   --   y > 0@), or an existential quantifier (@exists x, (y:N). x^2 + y   --   == 0@).-  TAbs_   :: Quantifier -> X_TAbs e -> Binder_ e (Term_ e) -> Term_ e-+  TAbs_ :: Quantifier -> X_TAbs e -> Binder_ e (Term_ e) -> Term_ e   -- | Function application, @t1 t2@.-  TApp_   :: X_TApp e -> Term_ e -> Term_ e -> Term_ e-+  TApp_ :: X_TApp e -> Term_ e -> Term_ e -> Term_ e   -- | An n-tuple, @(t1, ..., tn)@.-  TTup_   :: X_TTup e -> [Term_ e] -> Term_ e-+  TTup_ :: X_TTup e -> [Term_ e] -> Term_ e   -- | A case expression.-  TCase_  :: X_TCase e -> [Branch_ e] -> Term_ e-+  TCase_ :: X_TCase e -> [Branch_ e] -> Term_ e   -- | A chained comparison, consisting of a term followed by one or   --   more "links", where each link is a comparison operator and   --   another term.   TChain_ :: X_TChain e -> Term_ e -> [Link_ e] -> Term_ e-   -- | An application of a type operator.-  TTyOp_  :: X_TTyOp e -> TyOp -> Type -> Term_ e-+  TTyOp_ :: X_TTyOp e -> TyOp -> Type -> Term_ e   -- | A containter literal (set, bag, or list).   TContainer_ :: X_TContainer e -> Container -> [(Term_ e, Maybe (Term_ e))] -> Maybe (Ellipsis (Term_ e)) -> Term_ e-   -- | A container comprehension.   TContainerComp_ :: X_TContainerComp e -> Container -> Bind (Telescope (Qual_ e)) (Term_ e) -> Term_ e-   -- | Type ascription, @(Term_ e : type)@.-  TAscr_  :: X_TAscr e -> Term_ e -> PolyType -> Term_ e-+  TAscr_ :: X_TAscr e -> Term_ e -> PolyType -> Term_ e   -- | A data constructor with an extension descriptor that a "concrete"   --   implementation of a generic AST may use to carry extra information.-  XTerm_   :: X_Term e -> Term_ e+  XTerm_ :: X_Term e -> Term_ e   deriving (Generic)  -- A type that abstracts over constraints for generic data constructors. -- This makes it easier to derive typeclass instances for generic types.-type ForallTerm (a :: * -> Constraint) e-  = ( a (X_TVar e)-    , a (X_TPrim e)-    , a (X_TLet e)-    , a (X_TParens e)-    , a (X_TUnit e)-    , a (X_TBool e)-    , a (X_TNat e)-    , a (X_TRat e)-    , a (X_TChar e)-    , a (X_TString e)-    , a (X_TAbs e)-    , a (X_TApp e)-    , a (X_TCase e)-    , a (X_TTup e)-    , a (X_TChain e)-    , a (X_TTyOp e)-    , a (X_TContainer e)-    , a (X_TContainerComp e)-    , a (X_TAscr e)-    , a (X_Term e)-    , a (Qual_ e)-    , a (Guard_ e)-    , a (Link_ e)-    , a (Binding_ e)-    , a (Pattern_ e)-    , a (Binder_ e (Term_ e))-    )+type ForallTerm (a :: * -> Constraint) e =+  ( a (X_TVar e)+  , a (X_TPrim e)+  , a (X_TLet e)+  , a (X_TParens e)+  , a (X_TUnit e)+  , a (X_TBool e)+  , a (X_TNat e)+  , a (X_TRat e)+  , a (X_TChar e)+  , a (X_TString e)+  , a (X_TAbs e)+  , a (X_TApp e)+  , a (X_TCase e)+  , a (X_TTup e)+  , a (X_TChain e)+  , a (X_TTyOp e)+  , a (X_TContainer e)+  , a (X_TContainerComp e)+  , a (X_TAscr e)+  , a (X_Term e)+  , a (Qual_ e)+  , a (Guard_ e)+  , a (Link_ e)+  , a (Binding_ e)+  , a (Pattern_ e)+  , a (Binder_ e (Term_ e))+  )  deriving instance ForallTerm Show e => Show (Term_ e) instance   ( Typeable e   , ForallTerm (Subst Type) e   , ForallTerm Alpha e-  )-  => Subst Type (Term_ e)+  ) =>+  Subst Type (Term_ e) instance (Typeable e, ForallTerm Alpha e) => Alpha (Term_ e) deriving instance (Data e, Typeable e, ForallTerm Data e) => Data (Term_ e) @@ -407,19 +380,18 @@ --   followed by a sequence of links makes up a comparison chain, such --   as @2 < x < y < 10@. data Link_ e where-   -- | Note that although the type of 'TLink_' says it can hold any   --   'BOp', it should really only hold comparison operators.   TLink_ :: X_TLink e -> BOp -> Term_ e -> Link_ e-  deriving Generic+  deriving (Generic) -type ForallLink (a :: * -> Constraint) e-  = ( a (X_TLink e)-    , a (Term_ e)-    )+type ForallLink (a :: * -> Constraint) e =+  ( a (X_TLink e)+  , a (Term_ e)+  ) -deriving instance ForallLink Show e         => Show       (Link_ e)-instance          ForallLink (Subst Type) e => Subst Type (Link_ e)+deriving instance ForallLink Show e => Show (Link_ e)+instance ForallLink (Subst Type) e => Subst Type (Link_ e) instance (Typeable e, Show (Link_ e), ForallLink Alpha e) => Alpha (Link_ e) deriving instance (Typeable e, Data e, ForallLink Data e) => Data (Link_ e) @@ -434,23 +406,20 @@ --   of qualifiers. Each qualifier either binds a variable to some --   collection or consists of a boolean guard. data Qual_ e where-   -- | A binding qualifier (i.e. @x in t@).-  QBind_   :: X_QBind e -> Name (Term_ e) -> Embed (Term_ e) -> Qual_ e-+  QBind_ :: X_QBind e -> Name (Term_ e) -> Embed (Term_ e) -> Qual_ e   -- | A boolean guard qualfier (i.e. @x + y > 4@).-  QGuard_  :: X_QGuard e -> Embed (Term_ e) -> Qual_ e--  deriving Generic+  QGuard_ :: X_QGuard e -> Embed (Term_ e) -> Qual_ e+  deriving (Generic) -type ForallQual (a :: * -> Constraint) e-  = ( a (X_QBind e)-    , a (X_QGuard e)-    , a (Term_ e)-    )+type ForallQual (a :: * -> Constraint) e =+  ( a (X_QBind e)+  , a (X_QGuard e)+  , a (Term_ e)+  ) -deriving instance ForallQual Show         e => Show       (Qual_ e)-instance          ForallQual (Subst Type) e => Subst Type (Qual_ e)+deriving instance ForallQual Show e => Show (Qual_ e)+instance ForallQual (Subst Type) e => Subst Type (Qual_ e) instance (Typeable e, ForallQual Alpha e) => Alpha (Qual_ e) deriving instance (Typeable e, Data e, ForallQual Data e) => Data (Qual_ e) @@ -463,7 +432,7 @@ data Binding_ e = Binding_ (Maybe (Embed PolyType)) (Name (Term_ e)) (Embed (Term_ e))   deriving (Generic) -deriving instance ForallTerm Show  e => Show (Binding_ e)+deriving instance ForallTerm Show e => Show (Binding_ e) instance Subst Type (Term_ e) => Subst Type (Binding_ e) instance (Typeable e, Show (Binding_ e), Alpha (Term_ e)) => Alpha (Binding_ e) deriving instance (Typeable e, Data e, ForallTerm Data e) => Data (Binding_ e)@@ -475,7 +444,6 @@ -- | A branch of a case is a list of guards with an accompanying term. --   The guards scope over the term.  Additionally, each guard scopes --   over subsequent guards.- type Branch_ e = Bind (Telescope (Guard_ e)) (Term_ e)  ------------------------------------------------------------@@ -488,29 +456,25 @@  -- | Guards in case expressions. data Guard_ e where-   -- | Boolean guard (@if <test>@)   GBool_ :: X_GBool e -> Embed (Term_ e) -> Guard_ e-   -- | Pattern guard (@when term = pat@)-  GPat_  :: X_GPat e -> Embed (Term_ e) -> Pattern_ e -> Guard_ e-+  GPat_ :: X_GPat e -> Embed (Term_ e) -> Pattern_ e -> Guard_ e   -- | Let (@let x = term@)-  GLet_  :: X_GLet e -> Binding_ e -> Guard_ e--  deriving Generic+  GLet_ :: X_GLet e -> Binding_ e -> Guard_ e+  deriving (Generic) -type ForallGuard (a :: * -> Constraint) e-  = ( a (X_GBool e)-    , a (X_GPat  e)-    , a (X_GLet  e)-    , a (Term_ e)-    , a (Pattern_ e)-    , a (Binding_ e)-    )+type ForallGuard (a :: * -> Constraint) e =+  ( a (X_GBool e)+  , a (X_GPat e)+  , a (X_GLet e)+  , a (Term_ e)+  , a (Pattern_ e)+  , a (Binding_ e)+  ) -deriving instance ForallGuard Show         e => Show       (Guard_ e)-instance          ForallGuard (Subst Type) e => Subst Type (Guard_ e)+deriving instance ForallGuard Show e => Show (Guard_ e)+instance ForallGuard (Subst Type) e => Subst Type (Guard_ e) instance (Typeable e, Show (Guard_ e), ForallGuard Alpha e) => Alpha (Guard_ e) deriving instance (Typeable e, Data e, ForallGuard Data e) => Data (Guard_ e) @@ -539,87 +503,72 @@  -- | Patterns. data Pattern_ e where-   -- | Variable pattern: matches anything and binds the variable.-  PVar_  :: X_PVar e -> Name (Term_ e) -> Pattern_ e-+  PVar_ :: X_PVar e -> Name (Term_ e) -> Pattern_ e   -- | Wildcard pattern @_@: matches anything.   PWild_ :: X_PWild e -> Pattern_ e-   -- | Type ascription pattern @pat : ty@.   PAscr_ :: X_PAscr e -> Pattern_ e -> Type -> Pattern_ e-   -- | Unit pattern @()@: matches @()@.   PUnit_ :: X_PUnit e -> Pattern_ e-   -- | Literal boolean pattern.   PBool_ :: X_PBool e -> Bool -> Pattern_ e-   -- | Tuple pattern @(pat1, .. , patn)@.-  PTup_  :: X_PTup e -> [Pattern_ e] -> Pattern_ e-+  PTup_ :: X_PTup e -> [Pattern_ e] -> Pattern_ e   -- | Injection pattern (@inl pat@ or @inr pat@).-  PInj_  :: X_PInj e -> Side -> Pattern_ e -> Pattern_ e-+  PInj_ :: X_PInj e -> Side -> Pattern_ e -> Pattern_ e   -- | Literal natural number pattern.-  PNat_  :: X_PNat e -> Integer -> Pattern_ e-+  PNat_ :: X_PNat e -> Integer -> Pattern_ e   -- | Unicode character pattern   PChar_ :: X_PChar e -> Char -> Pattern_ e-   -- | String pattern.   PString_ :: X_PString e -> String -> Pattern_ e-   -- | Cons pattern @p1 :: p2@.   PCons_ :: X_PCons e -> Pattern_ e -> Pattern_ e -> Pattern_ e-   -- | List pattern @[p1, .., pn]@.   PList_ :: X_PList e -> [Pattern_ e] -> Pattern_ e-   -- | Addition pattern, @p + t@ or @t + p@-  PAdd_  :: X_PAdd e -> Side -> Pattern_ e -> Term_ e -> Pattern_ e-+  PAdd_ :: X_PAdd e -> Side -> Pattern_ e -> Term_ e -> Pattern_ e   -- | Multiplication pattern, @p * t@ or @t * p@-  PMul_  :: X_PMul e -> Side -> Pattern_ e -> Term_ e -> Pattern_ e-+  PMul_ :: X_PMul e -> Side -> Pattern_ e -> Term_ e -> Pattern_ e   -- | Subtraction pattern, @p - t@-  PSub_  :: X_PSub e -> Pattern_ e -> Term_ e -> Pattern_ e-+  PSub_ :: X_PSub e -> Pattern_ e -> Term_ e -> Pattern_ e   -- | Negation pattern, @-p@-  PNeg_  :: X_PNeg e -> Pattern_ e -> Pattern_ e-+  PNeg_ :: X_PNeg e -> Pattern_ e -> Pattern_ e   -- | Fraction pattern, @p1/p2@   PFrac_ :: X_PFrac e -> Pattern_ e -> Pattern_ e -> Pattern_ e-+  -- | A special placeholder node for a nonlinear occurrence of a+  --   variable; we can only detect this at parse time but need to+  --   generate an error later.+  PNonlinear_ :: Embed (Pattern_ e) -> Name (Term_ e) -> Pattern_ e   -- | Expansion slot.   XPattern_ :: X_Pattern e -> Pattern_ e-   deriving (Generic) -type ForallPattern (a :: * -> Constraint) e-      = ( a (X_PVar e)-        , a (X_PWild e)-        , a (X_PAscr e)-        , a (X_PUnit e)-        , a (X_PBool e)-        , a (X_PNat e)-        , a (X_PChar e)-        , a (X_PString e)-        , a (X_PTup e)-        , a (X_PInj e)-        , a (X_PCons e)-        , a (X_PList e)-        , a (X_PAdd e)-        , a (X_PMul e)-        , a (X_PSub e)-        , a (X_PNeg e)-        , a (X_PFrac e)-        , a (X_Pattern e)-        , a (Term_ e)-        )+type ForallPattern (a :: * -> Constraint) e =+  ( a (X_PVar e)+  , a (X_PWild e)+  , a (X_PAscr e)+  , a (X_PUnit e)+  , a (X_PBool e)+  , a (X_PNat e)+  , a (X_PChar e)+  , a (X_PString e)+  , a (X_PTup e)+  , a (X_PInj e)+  , a (X_PCons e)+  , a (X_PList e)+  , a (X_PAdd e)+  , a (X_PMul e)+  , a (X_PSub e)+  , a (X_PNeg e)+  , a (X_PFrac e)+  , a (X_Pattern e)+  , a (Term_ e)+  ) -deriving instance ForallPattern Show         e => Show       (Pattern_ e)-instance          ForallPattern (Subst Type) e => Subst Type (Pattern_ e)+deriving instance ForallPattern Show e => Show (Pattern_ e)+instance ForallPattern (Subst Type) e => Subst Type (Pattern_ e) instance (Typeable e, Show (Pattern_ e), ForallPattern Alpha e) => Alpha (Pattern_ e) deriving instance (Typeable e, Data e, ForallPattern Data e) => Data (Pattern_ e) 
src/Disco/AST/Surface.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE PatternSynonyms      #-}-{-# LANGUAGE StandaloneDeriving   #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE UndecidableInstances #-} ------------------------------------------------------------------------------ -- | -- Module      :  Disco.AST.Surface -- Copyright   :  disco team and contributors@@ -12,147 +11,173 @@ -- -- Abstract syntax trees representing the surface syntax of the Disco -- language.-----------------------------------------------------------------------------------module Disco.AST.Surface-       ( -- * Modules-         Module(..), TopLevel(..)-         -- ** Documentation-       , Docs, DocThing(..), Property-         -- ** Declarations-       , TypeDecl(..), TermDefn(..), TypeDefn(..)-       , Decl(..), partitionDecls, prettyTyDecl--         -- * Terms-       , UD-       , Term-       , pattern TVar-       , pattern TPrim-       , pattern TUn-       , pattern TBin-       , pattern TLet-       , pattern TParens-       , pattern TUnit-       , pattern TBool-       , pattern TChar-       , pattern TString-       , pattern TNat-       , pattern TRat-       , pattern TAbs-       , pattern TApp-       , pattern TTup-       , pattern TCase-       , pattern TChain-       , pattern TTyOp-       , pattern TContainerComp-       , pattern TContainer-       , pattern TAscr-       , pattern TWild-       , pattern TList-       , pattern TListComp--       , Quantifier(..)--         -- ** Telescopes-       , Telescope(..), foldTelescope, mapTelescope, toTelescope, fromTelescope-         -- ** Expressions-       , Side(..)--       , Link-       , pattern TLink--       , Binding+module Disco.AST.Surface (+  -- * Modules+  Module (..),+  emptyModule,+  TopLevel (..), -         -- ** Lists-       , Qual-       , pattern QBind-       , pattern QGuard+  -- ** Documentation+  Docs,+  DocThing (..),+  Property, -       , Container(..)+  -- ** Declarations+  TypeDecl (..),+  TermDefn (..),+  TypeDefn (..),+  Decl (..),+  partitionDecls,+  prettyTyDecl, -       , Ellipsis(..)+  -- * Terms+  UD,+  Term,+  pattern TVar,+  pattern TPrim,+  pattern TUn,+  pattern TBin,+  pattern TLet,+  pattern TParens,+  pattern TUnit,+  pattern TBool,+  pattern TChar,+  pattern TString,+  pattern TNat,+  pattern TRat,+  pattern TAbs,+  pattern TApp,+  pattern TTup,+  pattern TCase,+  pattern TChain,+  pattern TTyOp,+  pattern TContainerComp,+  pattern TContainer,+  pattern TAscr,+  pattern TWild,+  pattern TList,+  pattern TListComp,+  Quantifier (..), -         -- ** Case expressions and patterns-       , Branch+  -- ** Telescopes+  Telescope (..),+  foldTelescope,+  mapTelescope,+  toTelescope,+  fromTelescope, -       , Guard-       , pattern GBool-       , pattern GPat-       , pattern GLet+  -- ** Expressions+  Side (..),+  Link,+  pattern TLink,+  Binding, -       , Pattern-       , pattern PVar-       , pattern PWild-       , pattern PAscr-       , pattern PUnit-       , pattern PBool-       , pattern PChar-       , pattern PString-       , pattern PTup-       , pattern PInj-       , pattern PNat-       , pattern PCons-       , pattern PList-       , pattern PAdd-       , pattern PMul-       , pattern PSub-       , pattern PNeg-       , pattern PFrac+  -- ** Lists+  Qual,+  pattern QBind,+  pattern QGuard,+  Container (..),+  Ellipsis (..), -       , pattern Binding-       )-       where+  -- ** Case expressions and patterns+  Branch,+  Guard,+  pattern GBool,+  pattern GPat,+  pattern GLet,+  Pattern,+  pattern PVar,+  pattern PWild,+  pattern PAscr,+  pattern PUnit,+  pattern PBool,+  pattern PChar,+  pattern PString,+  pattern PTup,+  pattern PInj,+  pattern PNat,+  pattern PCons,+  pattern PList,+  pattern PAdd,+  pattern PMul,+  pattern PSub,+  pattern PNeg,+  pattern PFrac,+  pattern PNonlinear,+  pattern Binding,+)+where -import           Prelude                          hiding ((<>))+import Prelude hiding ((<>)) -import           Control.Lens                     (_1, _2, _3, (%~))-import           Data.Char                        (toLower)-import qualified Data.Map                         as M-import           Data.Set                         (Set)-import           Data.Void+import Control.Lens ((%~), _1, _2, _3)+import Data.Char (toLower)+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S+import Data.Void -import           Disco.Effects.LFresh-import           Polysemy                         hiding (Embed)-import           Polysemy.Reader+import Disco.Effects.LFresh+import Polysemy hiding (Embed, embed)+import Polysemy.Reader -import           Disco.AST.Generic-import           Disco.Extensions-import           Disco.Pretty-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Types-import           Unbound.Generics.LocallyNameless hiding (LFresh (..), lunbind)+import Disco.AST.Generic+import Disco.Extensions+import Disco.Pretty+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Types+import Unbound.Generics.LocallyNameless hiding (LFresh (..), lunbind)  -- | The extension descriptor for Surface specific AST types. data UD  -- | A module contains all the information from one disco source file. data Module = Module-  { modExts    :: Set Ext             -- ^ Enabled extensions-  , modImports :: [String]            -- ^ Module imports-  , modDecls   :: [Decl]              -- ^ Declarations-  , modDocs    :: [(Name Term, Docs)] -- ^ Documentation-  , modTerms   :: [Term]              -- ^ Top-level (bare) terms+  { modExts :: Set Ext+  -- ^ Enabled extensions+  , modImports :: [String]+  -- ^ Module imports+  , modDecls :: [Decl]+  -- ^ Declarations+  , modDocs :: [(Name Term, Docs)]+  -- ^ Documentation+  , modTerms :: [Term]+  -- ^ Top-level (bare) terms   }-deriving instance ForallTerm Show  UD => Show Module +deriving instance ForallTerm Show UD => Show Module++emptyModule :: Module+emptyModule =+  Module+    { modExts = S.empty+    , modImports = []+    , modDecls = []+    , modDocs = []+    , modTerms = []+    }+ -- | A @TopLevel@ is either documentation (a 'DocThing') or a --   declaration ('Decl'). data TopLevel = TLDoc DocThing | TLDecl Decl | TLExpr Term-deriving instance ForallTerm Show  UD => Show TopLevel +deriving instance ForallTerm Show UD => Show TopLevel+ -- | Convenient synonym for a list of 'DocThing's. type Docs = [DocThing]  -- | An item of documentation. data DocThing-  = DocString   [String]    -- ^ A documentation string, i.e. a block-                            --   of @||| text@ items-  | DocProperty Property    -- ^ An example/doctest/property of the-                            --   form @!!! forall (x1:ty1) ... . property@-deriving instance ForallTerm Show  UD => Show DocThing+  = -- | A documentation string, i.e. a block+    --   of @||| text@ items+    DocString [String]+  | -- | An example/doctest/property of the+    --   form @!!! forall (x1:ty1) ... . property@+    DocProperty Property +deriving instance ForallTerm Show UD => Show DocThing+ -- | A property is a universally quantified term of the form --   @forall v1 : T1, v2 : T2. term@. type Property = Property_ UD@@ -169,24 +194,24 @@ -- --   @type T arg1 arg2 ... = body data TypeDefn = TypeDefn String [String] Type-  deriving Show+  deriving (Show)  -- | A declaration is either a type declaration, a term definition, or --   a type definition. data Decl where-  DType  :: TypeDecl -> Decl-  DDefn  :: TermDefn -> Decl+  DType :: TypeDecl -> Decl+  DDefn :: TermDefn -> Decl   DTyDef :: TypeDefn -> Decl -deriving instance ForallTerm Show  UD => Show TypeDecl-deriving instance ForallTerm Show  UD => Show TermDefn-deriving instance ForallTerm Show  UD => Show Decl+deriving instance ForallTerm Show UD => Show TypeDecl+deriving instance ForallTerm Show UD => Show TermDefn+deriving instance ForallTerm Show UD => Show Decl  partitionDecls :: [Decl] -> ([TypeDecl], [TermDefn], [TypeDefn])-partitionDecls (DType  tyDecl : ds) = (_1 %~ (tyDecl:)) (partitionDecls ds)-partitionDecls (DDefn  def    : ds) = (_2 %~ (def:))    (partitionDecls ds)-partitionDecls (DTyDef def    : ds) = (_3 %~ (def:))    (partitionDecls ds)-partitionDecls []                   = ([], [], [])+partitionDecls (DType tyDecl : ds) = (_1 %~ (tyDecl :)) (partitionDecls ds)+partitionDecls (DDefn def : ds) = (_2 %~ (def :)) (partitionDecls ds)+partitionDecls (DTyDef def : ds) = (_3 %~ (def :)) (partitionDecls ds)+partitionDecls [] = ([], [], [])  ------------------------------------------------------------ -- Pretty-printing top-level declarations@@ -196,10 +221,10 @@  instance Pretty Decl where   pretty = \case-    DType  (TypeDecl x ty) -> pretty x <+> text ":" <+> pretty ty+    DType (TypeDecl x ty) -> pretty x <+> text ":" <+> pretty ty     DTyDef (TypeDefn x args body) ->       text "type" <+> text x <+> hsep (map text args) <+> text "=" <+> pretty body-    DDefn  (TermDefn x bs) -> vcat $ map (pretty . (x,)) bs+    DDefn (TermDefn x bs) -> vcat $ map (pretty . (x,)) bs  -- | Pretty-print a single clause in a definition. instance Pretty (Name a, Bind [Pattern] Term) where@@ -207,7 +232,7 @@     pretty x <> hcat (map prettyPatternP ps) <+> text "=" <+> setPA initPA (pretty t)  -- | Pretty-print a type declaration.-prettyTyDecl :: Members '[Reader PA, LFresh] r => Name t -> Type -> Sem r Doc+prettyTyDecl :: Members '[Reader PA, LFresh] r => Name t -> Type -> Sem r (Doc ann) prettyTyDecl x ty = hsep [pretty x, text ":", pretty ty]  ------------------------------------------------------------@@ -219,28 +244,28 @@ -- (nonempty) list of patterns. Each pattern might contain any -- number of variables, and might have type annotations on some -- of its components.-type instance X_Binder          UD = [Pattern]+type instance X_Binder UD = [Pattern] -type instance X_TVar            UD = ()-type instance X_TPrim           UD = ()-type instance X_TLet            UD = ()-type instance X_TParens         UD = ()-type instance X_TUnit           UD = ()-type instance X_TBool           UD = ()-type instance X_TNat            UD = ()-type instance X_TRat            UD = ()-type instance X_TChar           UD = ()-type instance X_TString         UD = ()-type instance X_TAbs            UD = ()-type instance X_TApp            UD = ()-type instance X_TTup            UD = ()-type instance X_TCase           UD = ()-type instance X_TChain          UD = ()-type instance X_TTyOp           UD = ()-type instance X_TContainer      UD = ()-type instance X_TContainerComp  UD = ()-type instance X_TAscr           UD = ()-type instance X_Term            UD = ()  -- TWild+type instance X_TVar UD = ()+type instance X_TPrim UD = ()+type instance X_TLet UD = ()+type instance X_TParens UD = ()+type instance X_TUnit UD = ()+type instance X_TBool UD = ()+type instance X_TNat UD = ()+type instance X_TRat UD = ()+type instance X_TChar UD = ()+type instance X_TString UD = ()+type instance X_TAbs UD = ()+type instance X_TApp UD = ()+type instance X_TTup UD = ()+type instance X_TCase UD = ()+type instance X_TChain UD = ()+type instance X_TTyOp UD = ()+type instance X_TContainer UD = ()+type instance X_TContainerComp UD = ()+type instance X_TAscr UD = ()+type instance X_Term UD = () -- TWild  pattern TVar :: Name Term -> Term pattern TVar name = TVar_ () name@@ -258,7 +283,7 @@ pattern TLet bind = TLet_ () bind  pattern TParens :: Term -> Term-pattern TParens term  = TParens_ () term+pattern TParens term = TParens_ () term  pattern TUnit :: Term pattern TUnit = TUnit_ ()@@ -266,7 +291,7 @@ pattern TBool :: Bool -> Term pattern TBool bool = TBool_ () bool -pattern TNat  :: Integer -> Term+pattern TNat :: Integer -> Term pattern TNat int = TNat_ () int  pattern TRat :: Rational -> Term@@ -281,7 +306,7 @@ pattern TAbs :: Quantifier -> Bind [Pattern] Term -> Term pattern TAbs q bind = TAbs_ q () bind -pattern TApp  :: Term -> Term -> Term+pattern TApp :: Term -> Term -> Term pattern TApp term1 term2 = TApp_ () term1 term2  pattern TTup :: [Term] -> Term@@ -311,9 +336,28 @@ pattern TWild :: Term pattern TWild = XTerm_ () -{-# COMPLETE TVar, TPrim, TLet, TParens, TUnit, TBool, TNat, TRat, TChar,-             TString, TAbs, TApp, TTup, TCase, TChain, TTyOp,-             TContainer, TContainerComp, TAscr, TWild #-}+{-# COMPLETE+  TVar+  , TPrim+  , TLet+  , TParens+  , TUnit+  , TBool+  , TNat+  , TRat+  , TChar+  , TString+  , TAbs+  , TApp+  , TTup+  , TCase+  , TChain+  , TTyOp+  , TContainer+  , TContainerComp+  , TAscr+  , TWild+  #-}  pattern TList :: [Term] -> Maybe (Ellipsis Term) -> Term pattern TList ts e <- TContainer_ () ListContainer (map fst -> ts) e@@ -357,8 +401,8 @@ type Guard = Guard_ UD  type instance X_GBool UD = ()-type instance X_GPat  UD = ()-type instance X_GLet  UD = ()+type instance X_GPat UD = ()+type instance X_GLet UD = ()  pattern GBool :: Embed Term -> Guard pattern GBool embedt = GBool_ () embedt@@ -373,23 +417,23 @@  type Pattern = Pattern_ UD -type instance X_PVar UD    = ()-type instance X_PWild UD   = ()-type instance X_PAscr UD   = ()-type instance X_PUnit UD   = ()-type instance X_PBool UD   = ()-type instance X_PTup UD    = ()-type instance X_PInj UD    = ()-type instance X_PNat UD    = ()-type instance X_PChar UD   = ()+type instance X_PVar UD = ()+type instance X_PWild UD = ()+type instance X_PAscr UD = ()+type instance X_PUnit UD = ()+type instance X_PBool UD = ()+type instance X_PTup UD = ()+type instance X_PInj UD = ()+type instance X_PNat UD = ()+type instance X_PChar UD = () type instance X_PString UD = ()-type instance X_PCons UD   = ()-type instance X_PList UD   = ()-type instance X_PAdd UD    = ()-type instance X_PMul UD    = ()-type instance X_PSub UD    = ()-type instance X_PNeg UD    = ()-type instance X_PFrac UD   = ()+type instance X_PCons UD = ()+type instance X_PList UD = ()+type instance X_PAdd UD = ()+type instance X_PMul UD = ()+type instance X_PSub UD = ()+type instance X_PNeg UD = ()+type instance X_PFrac UD = () type instance X_Pattern UD = Void  pattern PVar :: Name Term -> Pattern@@ -398,8 +442,8 @@ pattern PWild :: Pattern pattern PWild = PWild_ () - -- (?) TAscr uses a PolyType, but without higher rank types- -- I think we can't possibly need that here.+-- (?) TAscr uses a PolyType, but without higher rank types+-- I think we can't possibly need that here. pattern PAscr :: Pattern -> Type -> Pattern pattern PAscr p ty = PAscr_ () p ty @@ -407,7 +451,7 @@ pattern PUnit = PUnit_ ()  pattern PBool :: Bool -> Pattern-pattern PBool  b = PBool_ () b+pattern PBool b = PBool_ () b  pattern PChar :: Char -> Pattern pattern PChar c = PChar_ () c@@ -415,17 +459,17 @@ pattern PString :: String -> Pattern pattern PString s = PString_ () s -pattern PTup  :: [Pattern] -> Pattern+pattern PTup :: [Pattern] -> Pattern pattern PTup lp = PTup_ () lp -pattern PInj  :: Side -> Pattern -> Pattern+pattern PInj :: Side -> Pattern -> Pattern pattern PInj s p = PInj_ () s p -pattern PNat  :: Integer -> Pattern+pattern PNat :: Integer -> Pattern pattern PNat n = PNat_ () n  pattern PCons :: Pattern -> Pattern -> Pattern-pattern PCons  p1 p2 = PCons_ () p1 p2+pattern PCons p1 p2 = PCons_ () p1 p2  pattern PList :: [Pattern] -> Pattern pattern PList lp = PList_ () lp@@ -445,9 +489,31 @@ pattern PFrac :: Pattern -> Pattern -> Pattern pattern PFrac p1 p2 = PFrac_ () p1 p2 -{-# COMPLETE PVar, PWild, PAscr, PUnit, PBool, PTup, PInj, PNat,-             PChar, PString, PCons, PList, PAdd, PMul, PSub, PNeg, PFrac #-}+pattern PNonlinear :: Pattern -> Name Term -> Pattern+pattern PNonlinear p x <- PNonlinear_ (unembed -> p) x+  where+    PNonlinear p x = PNonlinear_ (embed p) x +{-# COMPLETE+  PVar+  , PWild+  , PAscr+  , PUnit+  , PBool+  , PTup+  , PInj+  , PNat+  , PChar+  , PString+  , PCons+  , PList+  , PAdd+  , PMul+  , PSub+  , PNeg+  , PFrac+  #-}+ ------------------------------------------------------------ -- Pretty-printing for surface-syntax terms --@@ -456,86 +522,88 @@ -- term (e.g. via the :doc REPL command).  -- | Pretty-print a term with guaranteed parentheses.-prettyTermP :: Members '[LFresh, Reader PA] r => Term -> Sem r Doc-prettyTermP t@TTup{} = setPA initPA $ pretty t+prettyTermP :: Members '[LFresh, Reader PA] r => Term -> Sem r (Doc ann)+prettyTermP t@TTup {} = setPA initPA $ pretty t -- prettyTermP t@TContainer{} = setPA initPA $ "" <+> prettyTerm t-prettyTermP t        = withPA initPA $ pretty t+prettyTermP t = withPA initPA $ pretty t  instance Pretty Term where   pretty = \case-    TVar x      -> pretty x+    TVar x -> pretty x     TPrim (PrimUOp uop) ->       case M.lookup uop uopMap of-        Just (OpInfo (UOpF Pre _) (syn:_) _)  -> text syn <> text "~"-        Just (OpInfo (UOpF Post _) (syn:_) _) -> text "~" <> text syn+        Just (OpInfo (UOpF Pre _) (syn : _) _) -> text syn <> text "~"+        Just (OpInfo (UOpF Post _) (syn : _) _) -> text "~" <> text syn         _ -> error $ "pretty @Term: " ++ show uop ++ " is not in the uopMap!"     TPrim (PrimBOp bop) -> text "~" <> pretty bop <> text "~"     TPrim p ->       case M.lookup p primMap of-        Just (PrimInfo _ nm True)  -> text nm+        Just (PrimInfo _ nm True) -> text nm         Just (PrimInfo _ nm False) -> text "$" <> text nm         Nothing -> error $ "pretty @Term: Prim " ++ show p ++ " is not in the primMap!"-    TParens t   -> pretty t-    TUnit       -> text "■"-    (TBool b)     -> text (map toLower $ show b)-    TChar c     -> text (show c)-    TString cs  -> doubleQuotes $ text cs-    TAbs q bnd  -> withPA initPA $+    TParens t -> pretty t+    TUnit -> text "■"+    (TBool b) -> text (map toLower $ show b)+    TChar c -> text (show c)+    TString cs -> doubleQuotes $ text cs+    TAbs q bnd -> withPA initPA $       lunbind bnd $ \(args, body) ->-      prettyQ q-        <> (hsep =<< punctuate (text ",") (map pretty args))-        <> text "."-        <+> lt (pretty body)-      where-        prettyQ Lam = text "λ"-        prettyQ All = text "∀"-        prettyQ Ex  = text "∃"+        prettyQ q+          <> (hsep =<< punctuate (text ",") (map pretty args))+          <> text "."+          <+> lt (pretty body)+     where+      prettyQ Lam = text "λ"+      prettyQ All = text "∀"+      prettyQ Ex = text "∃"      -- special case for fully applied unary operators     TApp (TPrim (PrimUOp uop)) t ->       case M.lookup uop uopMap of-        Just (OpInfo (UOpF Post _) _ _) -> withPA (ugetPA uop) $-          lt (pretty t) <> pretty uop-        Just (OpInfo (UOpF Pre  _) _ _) -> withPA (ugetPA uop) $-          pretty uop <> rt (pretty t)+        Just (OpInfo (UOpF Post _) _ _) ->+          withPA (ugetPA uop) $+            lt (pretty t) <> pretty uop+        Just (OpInfo (UOpF Pre _) _ _) ->+          withPA (ugetPA uop) $+            pretty uop <> rt (pretty t)         _ -> error $ "pretty @Term: uopMap doesn't contain " ++ show uop-     -- special case for fully applied binary operators-    TApp (TPrim (PrimBOp bop)) (TTup [t1, t2]) -> withPA (getPA bop) $-      hsep-      [ lt (pretty t1)-      , pretty bop-      , rt (pretty t2)-      ]-+    TApp (TPrim (PrimBOp bop)) (TTup [t1, t2]) ->+      withPA (getPA bop) $+        hsep+          [ lt (pretty t1)+          , pretty bop+          , rt (pretty t2)+          ]     -- Always pretty-print function applications with parentheses-    TApp t1 t2  -> withPA funPA $-      lt (pretty t1) <> prettyTermP t2--    TTup ts     -> setPA initPA $ do+    TApp t1 t2 ->+      withPA funPA $+        lt (pretty t1) <> prettyTermP t2+    TTup ts -> setPA initPA $ do       ds <- punctuate (text ",") (map pretty ts)       parens (hsep ds)-    TContainer c ts e  -> setPA initPA $ do+    TContainer c ts e -> setPA initPA $ do       ds <- punctuate (text ",") (map prettyCount ts)       let pe = case e of-                 Nothing        -> []-                 Just (Until t) -> [text "..", pretty t]+            Nothing -> []+            Just (Until t) -> [text "..", pretty t]       containerDelims c (hsep (ds ++ pe))-      where-        prettyCount (t, Nothing) = pretty t-        prettyCount (t, Just n)  = lt (pretty t) <+> text "#" <+> rt (pretty n)+     where+      prettyCount (t, Nothing) = pretty t+      prettyCount (t, Just n) = lt (pretty t) <+> text "#" <+> rt (pretty n)     TContainerComp c bqst ->-      lunbind bqst $ \(qs,t) ->-      setPA initPA $ containerDelims c (hsep [pretty t, text "|", pretty qs])-    TNat n       -> integer n-    TChain t lks -> withPA (getPA Eq) . hsep $+      lunbind bqst $ \(qs, t) ->+        setPA initPA $ containerDelims c (hsep [pretty t, text "|", pretty qs])+    TNat n -> integer n+    TChain t lks ->+      withPA (getPA Eq) . hsep $         lt (pretty t)-        : concatMap prettyLink lks-      where-        prettyLink (TLink op t2) =-          [ pretty op-          , setPA (getPA op) . rt $ pretty t2-          ]+          : concatMap prettyLink lks+     where+      prettyLink (TLink op t2) =+        [ pretty op+        , setPA (getPA op) . rt $ pretty t2+        ]     TLet bnd -> withPA initPA $       lunbind bnd $ \(bs, t2) -> do         ds <- punctuate (text ",") (map pretty (fromTelescope bs))@@ -545,46 +613,47 @@           , text "in"           , pretty t2           ]--    TCase b    -> withPA initPA $-      (text "{?" <+> prettyBranches b) $+$ text "?}"-    TAscr t ty -> withPA ascrPA $-      lt (pretty t) <+> text ":" <+> rt (pretty ty)-    TRat  r    -> text (prettyDecimal r)-    TTyOp op ty  -> withPA funPA $-      pretty op <+> pretty ty+    TCase b ->+      withPA initPA $+        (text "{?" <+> prettyBranches b) $+$ text "?}"+    TAscr t ty ->+      withPA ascrPA $+        lt (pretty t) <+> text ":" <+> rt (pretty ty)+    TRat r -> text (prettyDecimal r)+    TTyOp op ty ->+      withPA funPA $+        pretty op <+> pretty ty     TWild -> text "_"  -- | Print appropriate delimiters for a container literal.-containerDelims :: Member (Reader PA) r => Container -> (Sem r Doc -> Sem r Doc)+containerDelims :: Member (Reader PA) r => Container -> (Sem r (Doc ann) -> Sem r (Doc ann)) containerDelims ListContainer = brackets-containerDelims BagContainer  = bag-containerDelims SetContainer  = braces+containerDelims BagContainer = bag+containerDelims SetContainer = braces -prettyBranches :: Members '[Reader PA, LFresh] r => [Branch] -> Sem r Doc+prettyBranches :: Members '[Reader PA, LFresh] r => [Branch] -> Sem r (Doc ann) prettyBranches = \case   [] -> error "Empty branches are disallowed."-  b:bs ->+  b : bs ->     pretty b-    $+$-    foldr (($+$) . (text "," <+>) . pretty) empty bs+      $+$ foldr (($+$) . (text "," <+>) . pretty) empty bs  -- | Pretty-print a single branch in a case expression. instance Pretty Branch where-  pretty br = lunbind br $ \(gs,t) ->+  pretty br = lunbind br $ \(gs, t) ->     pretty t <+> pretty gs  -- | Pretty-print the guards in a single branch of a case expression. instance Pretty (Telescope Guard) where   pretty = \case     TelEmpty -> text "otherwise"-    gs       -> foldr (\g r -> pretty g <+> r) (text "") (fromTelescope gs)+    gs -> foldr (\g r -> pretty g <+> r) (text "") (fromTelescope gs)  instance Pretty Guard where   pretty = \case-    GBool et  -> text "if" <+> pretty (unembed et)+    GBool et -> text "if" <+> pretty (unembed et)     GPat et p -> text "when" <+> pretty (unembed et) <+> text "is" <+> pretty p-    GLet b    -> text "let" <+> pretty b+    GLet b -> text "let" <+> pretty b  -- | Pretty-print a binding, i.e. a pairing of a name (with optional --   type annotation) and term.@@ -605,12 +674,12 @@ instance Pretty Qual where   pretty = \case     QBind x (unembed -> t) -> hsep [pretty x, text "in", pretty t]-    QGuard (unembed -> t)  -> pretty t+    QGuard (unembed -> t) -> pretty t  -- | Pretty-print a pattern with guaranteed parentheses.-prettyPatternP :: Members '[LFresh, Reader PA] r => Pattern -> Sem r Doc-prettyPatternP p@PTup{} = setPA initPA $ pretty p-prettyPatternP p        = withPA initPA $ pretty p+prettyPatternP :: Members '[LFresh, Reader PA] r => Pattern -> Sem r (Doc ann)+prettyPatternP p@PTup {} = setPA initPA $ pretty p+prettyPatternP p = withPA initPA $ pretty p  -- We could probably alternatively write a function to turn a pattern -- back into a term, and pretty-print that instead of the below.@@ -618,37 +687,46 @@  instance Pretty Pattern where   pretty = \case-    PVar x      -> pretty x-    PWild       -> text "_"-    PAscr p ty  -> withPA ascrPA $-      lt (pretty p) <+> text ":" <+> rt (pretty ty)-    PUnit       -> text "■"-    PBool b     -> text $ map toLower $ show b-    PChar c     -> text (show c)-    PString s   -> text (show s)-    PTup ts     -> setPA initPA $ do+    PVar x -> pretty x+    PWild -> text "_"+    PAscr p ty ->+      withPA ascrPA $+        lt (pretty p) <+> text ":" <+> rt (pretty ty)+    PUnit -> text "■"+    PBool b -> text $ map toLower $ show b+    PChar c -> text (show c)+    PString s -> text (show s)+    PTup ts -> setPA initPA $ do       ds <- punctuate (text ",") (map pretty ts)       parens (hsep ds)-    PInj s p    -> withPA funPA $-      pretty s <> prettyPatternP p-    PNat n      -> integer n-    PCons p1 p2 -> withPA (getPA Cons) $-      lt (pretty p1) <+> text "::" <+> rt (pretty p2)-    PList ps    -> setPA initPA $ do+    PInj s p ->+      withPA funPA $+        pretty s <> prettyPatternP p+    PNat n -> integer n+    PCons p1 p2 ->+      withPA (getPA Cons) $+        lt (pretty p1) <+> text "::" <+> rt (pretty p2)+    PList ps -> setPA initPA $ do       ds <- punctuate (text ",") (map pretty ps)       brackets (hsep ds)-    PAdd L p t  -> withPA (getPA Add) $-      lt (pretty p) <+> text "+" <+> rt (pretty t)-    PAdd R p t  -> withPA (getPA Add) $-      lt (pretty t) <+> text "+" <+> rt (pretty p)-    PMul L p t  -> withPA (getPA Mul) $-      lt (pretty p) <+> text "*" <+> rt (pretty t)-    PMul R p t  -> withPA (getPA Mul) $-      lt (pretty t) <+> text "*" <+> rt (pretty p)-    PSub p t    -> withPA (getPA Sub) $-      lt (pretty p) <+> text "-" <+> rt (pretty t)-    PNeg p      -> withPA (ugetPA Neg) $-      text "-" <> rt (pretty p)-    PFrac p1 p2 -> withPA (getPA Div) $-      lt (pretty p1) <+> text "/" <+> rt (pretty p2)-+    PAdd L p t ->+      withPA (getPA Add) $+        lt (pretty p) <+> text "+" <+> rt (pretty t)+    PAdd R p t ->+      withPA (getPA Add) $+        lt (pretty t) <+> text "+" <+> rt (pretty p)+    PMul L p t ->+      withPA (getPA Mul) $+        lt (pretty p) <+> text "*" <+> rt (pretty t)+    PMul R p t ->+      withPA (getPA Mul) $+        lt (pretty t) <+> text "*" <+> rt (pretty p)+    PSub p t ->+      withPA (getPA Sub) $+        lt (pretty p) <+> text "-" <+> rt (pretty t)+    PNeg p ->+      withPA (ugetPA Neg) $+        text "-" <> rt (pretty p)+    PFrac p1 p2 ->+      withPA (getPA Div) $+        lt (pretty p1) <+> text "/" <+> rt (pretty p2)
src/Disco/AST/Typed.hs view
@@ -1,7 +1,10 @@ {-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE PatternSynonyms    #-}+{-# LANGUAGE PatternSynonyms #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.AST.Typed -- Copyright   :  disco team and contributors@@ -12,100 +15,91 @@ -- Typed abstract syntax trees representing the typechecked surface -- syntax of the Disco language.  Each tree node is annotated with the -- type of its subtree.-----------------------------------------------------------------------------------module Disco.AST.Typed-       ( -- * Type-annotated terms-         ATerm-       , pattern ATVar-       , pattern ATPrim-       , pattern ATLet-       , pattern ATUnit-       , pattern ATBool-       , pattern ATNat-       , pattern ATRat-       , pattern ATChar-       , pattern ATString-       , pattern ATAbs-       , pattern ATApp-       , pattern ATTup-       , pattern ATCase-       , pattern ATChain-       , pattern ATTyOp-       , pattern ATContainer-       , pattern ATContainerComp-       , pattern ATList-       , pattern ATListComp-       , pattern ATTest--       , ALink-       , pattern ATLink--       , Container(..)-       , ABinding-         -- * Branches and guards-       , ABranch--       , AGuard-       , pattern AGBool-       , pattern AGPat-       , pattern AGLet--       , AQual-       , pattern AQBind-       , pattern AQGuard--       , APattern-       , pattern APVar-       , pattern APWild-       , pattern APUnit-       , pattern APBool-       , pattern APTup-       , pattern APInj-       , pattern APNat-       , pattern APChar-       , pattern APString-       , pattern APCons-       , pattern APList-       , pattern APAdd-       , pattern APMul-       , pattern APSub-       , pattern APNeg-       , pattern APFrac+module Disco.AST.Typed (+  -- * Type-annotated terms+  ATerm,+  pattern ATVar,+  pattern ATPrim,+  pattern ATLet,+  pattern ATUnit,+  pattern ATBool,+  pattern ATNat,+  pattern ATRat,+  pattern ATChar,+  pattern ATString,+  pattern ATAbs,+  pattern ATApp,+  pattern ATTup,+  pattern ATCase,+  pattern ATChain,+  pattern ATTyOp,+  pattern ATContainer,+  pattern ATContainerComp,+  pattern ATList,+  pattern ATListComp,+  pattern ATTest,+  ALink,+  pattern ATLink,+  Container (..),+  ABinding, -       , pattern ABinding-         -- * Utilities-       , varsBound-       , getType-       , setType-       , substQT+  -- * Branches and guards+  ABranch,+  AGuard,+  pattern AGBool,+  pattern AGPat,+  pattern AGLet,+  AQual,+  pattern AQBind,+  pattern AQGuard,+  APattern,+  pattern APVar,+  pattern APWild,+  pattern APUnit,+  pattern APBool,+  pattern APTup,+  pattern APInj,+  pattern APNat,+  pattern APChar,+  pattern APString,+  pattern APCons,+  pattern APList,+  pattern APAdd,+  pattern APMul,+  pattern APSub,+  pattern APNeg,+  pattern APFrac,+  pattern ABinding, -       , AProperty-       )-       where+  -- * Utilities+  varsBound,+  getType,+  setType,+  substQT,+  AProperty,+)+where -import           Unbound.Generics.LocallyNameless-import           Unbound.Generics.LocallyNameless.Unsafe+import Unbound.Generics.LocallyNameless+import Unbound.Generics.LocallyNameless.Unsafe -import           Control.Arrow                           ((***))-import           Data.Coerce                             (coerce)-import           Data.Data                               (Data)-import           Data.Void+import Control.Arrow ((***))+import Data.Coerce (coerce)+import Data.Data (Data)+import Data.Void -import           Control.Lens.Plated                     (transform)-import           Disco.AST.Generic-import           Disco.AST.Surface-import           Disco.Names-import           Disco.Pretty-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Types+import Control.Lens.Plated (transform)+import Disco.AST.Generic+import Disco.AST.Surface+import Disco.Names+import Disco.Pretty+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Types  -- | The extension descriptor for Typed specific AST types.- data TY-  deriving Data+  deriving (Data)  type AProperty = Property_ TY @@ -116,34 +110,33 @@  -- | An @ATerm@ is a typechecked term where every node in the tree has --   been annotated with the type of the subterm rooted at that node.- type ATerm = Term_ TY -type instance X_Binder          TY = [APattern]+type instance X_Binder TY = [APattern] -type instance X_TVar            TY = Void -- Names are now qualified-type instance X_TPrim           TY = Type-type instance X_TLet            TY = Type-type instance X_TUnit           TY = ()-type instance X_TBool           TY = Type-type instance X_TNat            TY = Type-type instance X_TRat            TY = ()-type instance X_TChar           TY = ()-type instance X_TString         TY = ()-type instance X_TAbs            TY = Type-type instance X_TApp            TY = Type-type instance X_TCase           TY = Type-type instance X_TChain          TY = Type-type instance X_TTyOp           TY = Type-type instance X_TContainer      TY = Type-type instance X_TContainerComp  TY = Type-type instance X_TAscr           TY = Void -- No more type ascriptions in typechecked terms-type instance X_TTup            TY = Type-type instance X_TParens         TY = Void -- No more explicit parens+type instance X_TVar TY = Void -- Names are now qualified+type instance X_TPrim TY = Type+type instance X_TLet TY = Type+type instance X_TUnit TY = ()+type instance X_TBool TY = Type+type instance X_TNat TY = Type+type instance X_TRat TY = ()+type instance X_TChar TY = ()+type instance X_TString TY = ()+type instance X_TAbs TY = Type+type instance X_TApp TY = Type+type instance X_TCase TY = Type+type instance X_TChain TY = Type+type instance X_TTyOp TY = Type+type instance X_TContainer TY = Type+type instance X_TContainerComp TY = Type+type instance X_TAscr TY = Void -- No more type ascriptions in typechecked terms+type instance X_TTup TY = Type+type instance X_TParens TY = Void -- No more explicit parens - -- A test frame for reporting counterexamples in a test. These don't appear- -- in source programs, but because the deugarer manipulates partly-desugared- -- terms it helps to be able to represent these in 'ATerm'.+-- A test frame for reporting counterexamples in a test. These don't appear+-- in source programs, but because the deugarer manipulates partly-desugared+-- terms it helps to be able to represent these in 'ATerm'. type instance X_Term TY = Either ([(String, Type, Name ATerm)], ATerm) (Type, QName ATerm)  pattern ATVar :: Type -> QName ATerm -> ATerm@@ -161,7 +154,7 @@ pattern ATBool :: Type -> Bool -> ATerm pattern ATBool ty bool = TBool_ ty bool -pattern ATNat  :: Type -> Integer -> ATerm+pattern ATNat :: Type -> Integer -> ATerm pattern ATNat ty int = TNat_ ty int  pattern ATRat :: Rational -> ATerm@@ -176,7 +169,7 @@ pattern ATAbs :: Quantifier -> Type -> Bind [APattern] ATerm -> ATerm pattern ATAbs q ty bind = TAbs_ q ty bind -pattern ATApp  :: Type -> ATerm -> ATerm -> ATerm+pattern ATApp :: Type -> ATerm -> ATerm -> ATerm pattern ATApp ty term1 term2 = TApp_ ty term1 term2  pattern ATTup :: Type -> [ATerm] -> ATerm@@ -200,9 +193,26 @@ pattern ATTest :: [(String, Type, Name ATerm)] -> ATerm -> ATerm pattern ATTest ns t = XTerm_ (Left (ns, t)) -{-# COMPLETE ATVar, ATPrim, ATLet, ATUnit, ATBool, ATNat, ATRat, ATChar,-             ATString, ATAbs, ATApp, ATTup, ATCase, ATChain, ATTyOp,-             ATContainer, ATContainerComp, ATTest #-}+{-# COMPLETE+  ATVar+  , ATPrim+  , ATLet+  , ATUnit+  , ATBool+  , ATNat+  , ATRat+  , ATChar+  , ATString+  , ATAbs+  , ATApp+  , ATTup+  , ATCase+  , ATChain+  , ATTyOp+  , ATContainer+  , ATContainerComp+  , ATTest+  #-}  pattern ATList :: Type -> [ATerm] -> Maybe (Ellipsis ATerm) -> ATerm pattern ATList t xs e <- ATContainer t ListContainer (map fst -> xs) e@@ -221,13 +231,11 @@  {-# COMPLETE ATLink #-} - type AQual = Qual_ TY  type instance X_QBind TY = () type instance X_QGuard TY = () - pattern AQBind :: Name ATerm -> Embed ATerm -> AQual pattern AQBind namet embedt = QBind_ () namet embedt @@ -248,8 +256,8 @@ type AGuard = Guard_ TY  type instance X_GBool TY = ()-type instance X_GPat  TY = ()-type instance X_GLet  TY = ()   -- ??? Type?+type instance X_GPat TY = ()+type instance X_GLet TY = () -- ??? Type?  pattern AGBool :: Embed ATerm -> AGuard pattern AGBool embedt = GBool_ () embedt@@ -267,25 +275,25 @@ -- We have to use Embed Type because we don't want any type variables -- inside the types being treated as binders! -type instance X_PVar     TY = Embed Type-type instance X_PWild    TY = Embed Type-type instance X_PAscr    TY = Void -- No more ascriptions in typechecked patterns.-type instance X_PUnit    TY = ()-type instance X_PBool    TY = ()-type instance X_PChar    TY = ()-type instance X_PString  TY = ()-type instance X_PTup     TY = Embed Type-type instance X_PInj     TY = Embed Type-type instance X_PNat     TY = Embed Type-type instance X_PCons    TY = Embed Type-type instance X_PList    TY = Embed Type-type instance X_PAdd     TY = Embed Type-type instance X_PMul     TY = Embed Type-type instance X_PSub     TY = Embed Type-type instance X_PNeg     TY = Embed Type-type instance X_PFrac    TY = Embed Type+type instance X_PVar TY = Embed Type+type instance X_PWild TY = Embed Type+type instance X_PAscr TY = Void -- No more ascriptions in typechecked patterns.+type instance X_PUnit TY = ()+type instance X_PBool TY = ()+type instance X_PChar TY = ()+type instance X_PString TY = ()+type instance X_PTup TY = Embed Type+type instance X_PInj TY = Embed Type+type instance X_PNat TY = Embed Type+type instance X_PCons TY = Embed Type+type instance X_PList TY = Embed Type+type instance X_PAdd TY = Embed Type+type instance X_PMul TY = Embed Type+type instance X_PSub TY = Embed Type+type instance X_PNeg TY = Embed Type+type instance X_PFrac TY = Embed Type -type instance X_Pattern  TY = ()+type instance X_Pattern TY = ()  pattern APVar :: Type -> Name ATerm -> APattern pattern APVar ty name <- PVar_ (unembed -> ty) name@@ -301,25 +309,25 @@ pattern APUnit = PUnit_ ()  pattern APBool :: Bool -> APattern-pattern APBool  b = PBool_ () b+pattern APBool b = PBool_ () b  pattern APChar :: Char -> APattern-pattern APChar  c = PChar_ () c+pattern APChar c = PChar_ () c  pattern APString :: String -> APattern pattern APString s = PString_ () s -pattern APTup  :: Type -> [APattern] -> APattern+pattern APTup :: Type -> [APattern] -> APattern pattern APTup ty lp <- PTup_ (unembed -> ty) lp   where     APTup ty lp = PTup_ (embed ty) lp -pattern APInj  :: Type -> Side -> APattern -> APattern+pattern APInj :: Type -> Side -> APattern -> APattern pattern APInj ty s p <- PInj_ (unembed -> ty) s p   where     APInj ty s p = PInj_ (embed ty) s p -pattern APNat  :: Type -> Integer -> APattern+pattern APNat :: Type -> Integer -> APattern pattern APNat ty n <- PNat_ (unembed -> ty) n   where     APNat ty n = PNat_ (embed ty) n@@ -359,87 +367,103 @@   where     APFrac ty p1 p2 = PFrac_ (embed ty) p1 p2 -{-# COMPLETE APVar, APWild, APUnit, APBool, APChar, APString,-    APTup, APInj, APNat, APCons, APList, APAdd, APMul, APSub, APNeg, APFrac #-}+{-# COMPLETE+  APVar+  , APWild+  , APUnit+  , APBool+  , APChar+  , APString+  , APTup+  , APInj+  , APNat+  , APCons+  , APList+  , APAdd+  , APMul+  , APSub+  , APNeg+  , APFrac+  #-}  varsBound :: APattern -> [(Name ATerm, Type)]-varsBound (APVar ty n)    = [(n, ty)]-varsBound (APWild _)      = []-varsBound APUnit          = []-varsBound (APBool _)      = []-varsBound (APChar _)      = []-varsBound (APString _)    = []-varsBound (APTup _ ps)    = varsBound =<< ps-varsBound (APInj _ _ p)   = varsBound p-varsBound (APNat _ _)     = []-varsBound (APCons _ p q)  = varsBound p ++ varsBound q-varsBound (APList _ ps)   = varsBound =<< ps+varsBound (APVar ty n) = [(n, ty)]+varsBound (APWild _) = []+varsBound APUnit = []+varsBound (APBool _) = []+varsBound (APChar _) = []+varsBound (APString _) = []+varsBound (APTup _ ps) = varsBound =<< ps+varsBound (APInj _ _ p) = varsBound p+varsBound (APNat _ _) = []+varsBound (APCons _ p q) = varsBound p ++ varsBound q+varsBound (APList _ ps) = varsBound =<< ps varsBound (APAdd _ _ p _) = varsBound p varsBound (APMul _ _ p _) = varsBound p-varsBound (APSub _ p _)   = varsBound p-varsBound (APNeg _ p)     = varsBound p-varsBound (APFrac _ p q)  = varsBound p ++ varsBound q+varsBound (APSub _ p _) = varsBound p+varsBound (APNeg _ p) = varsBound p+varsBound (APFrac _ p q) = varsBound p ++ varsBound q  ------------------------------------------------------------ -- getType ------------------------------------------------------------  instance HasType ATerm where-  getType (ATVar ty _)             = ty-  getType (ATPrim ty _)            = ty-  getType ATUnit                   = TyUnit-  getType (ATBool ty _)            = ty-  getType (ATNat ty _)             = ty-  getType (ATRat _)                = TyF-  getType (ATChar _)               = TyC-  getType (ATString _)             = TyList TyC-  getType (ATAbs _ ty _)           = ty-  getType (ATApp ty _ _)           = ty-  getType (ATTup ty _)             = ty-  getType (ATTyOp ty _ _)          = ty-  getType (ATChain ty _ _)         = ty-  getType (ATContainer ty _ _ _)   = ty+  getType (ATVar ty _) = ty+  getType (ATPrim ty _) = ty+  getType ATUnit = TyUnit+  getType (ATBool ty _) = ty+  getType (ATNat ty _) = ty+  getType (ATRat _) = TyF+  getType (ATChar _) = TyC+  getType (ATString _) = TyList TyC+  getType (ATAbs _ ty _) = ty+  getType (ATApp ty _ _) = ty+  getType (ATTup ty _) = ty+  getType (ATTyOp ty _ _) = ty+  getType (ATChain ty _ _) = ty+  getType (ATContainer ty _ _ _) = ty   getType (ATContainerComp ty _ _) = ty-  getType (ATLet ty _)             = ty-  getType (ATCase ty _)            = ty-  getType (ATTest _ _ )            = TyProp+  getType (ATLet ty _) = ty+  getType (ATCase ty _) = ty+  getType (ATTest _ _) = TyProp -  setType ty (ATVar _ x      )       = ATVar ty x-  setType ty (ATPrim _ x     )       = ATPrim ty x-  setType _  ATUnit                  = ATUnit-  setType ty (ATBool _ b)            = ATBool ty b-  setType ty (ATNat _ x      )       = ATNat ty x-  setType _  (ATRat r)               = ATRat r-  setType _ (ATChar c)               = ATChar c-  setType _ (ATString cs)            = ATString cs-  setType ty (ATAbs q _ x    )       = ATAbs q ty x-  setType ty (ATApp _ x y    )       = ATApp ty x y-  setType ty (ATTup _ x      )       = ATTup ty x-  setType ty (ATTyOp _ x y   )       = ATTyOp ty x y-  setType ty (ATChain _ x y  )       = ATChain ty x y-  setType ty (ATContainer _ x y z)   = ATContainer ty x y z+  setType ty (ATVar _ x) = ATVar ty x+  setType ty (ATPrim _ x) = ATPrim ty x+  setType _ ATUnit = ATUnit+  setType ty (ATBool _ b) = ATBool ty b+  setType ty (ATNat _ x) = ATNat ty x+  setType _ (ATRat r) = ATRat r+  setType _ (ATChar c) = ATChar c+  setType _ (ATString cs) = ATString cs+  setType ty (ATAbs q _ x) = ATAbs q ty x+  setType ty (ATApp _ x y) = ATApp ty x y+  setType ty (ATTup _ x) = ATTup ty x+  setType ty (ATTyOp _ x y) = ATTyOp ty x y+  setType ty (ATChain _ x y) = ATChain ty x y+  setType ty (ATContainer _ x y z) = ATContainer ty x y z   setType ty (ATContainerComp _ x y) = ATContainerComp ty x y-  setType ty (ATLet _ x      )       = ATLet ty x-  setType ty (ATCase _ x     )       = ATCase ty x-  setType _ (ATTest vs x)            = ATTest vs x+  setType ty (ATLet _ x) = ATLet ty x+  setType ty (ATCase _ x) = ATCase ty x+  setType _ (ATTest vs x) = ATTest vs x  instance HasType APattern where-  getType (APVar ty _)     = ty-  getType (APWild ty)      = ty-  getType APUnit           = TyUnit-  getType (APBool _)       = TyBool-  getType (APChar _)       = TyC-  getType (APString _)     = TyList TyC-  getType (APTup ty _)     = ty-  getType (APInj ty _ _)   = ty-  getType (APNat ty _)     = ty-  getType (APCons ty _ _)  = ty-  getType (APList ty _)    = ty+  getType (APVar ty _) = ty+  getType (APWild ty) = ty+  getType APUnit = TyUnit+  getType (APBool _) = TyBool+  getType (APChar _) = TyC+  getType (APString _) = TyList TyC+  getType (APTup ty _) = ty+  getType (APInj ty _ _) = ty+  getType (APNat ty _) = ty+  getType (APCons ty _ _) = ty+  getType (APList ty _) = ty   getType (APAdd ty _ _ _) = ty   getType (APMul ty _ _ _) = ty-  getType (APSub ty _ _)   = ty-  getType (APNeg ty _)     = ty-  getType (APFrac ty _ _)  = ty+  getType (APSub ty _ _) = ty+  getType (APNeg ty _) = ty+  getType (APFrac ty _ _) = ty  instance HasType ABranch where   getType = getType . snd . unsafeUnbind@@ -451,7 +475,7 @@ substQT :: QName ATerm -> ATerm -> ATerm -> ATerm substQT x s = transform $ \case   t@(ATVar _ y)-    | x == y    -> s+    | x == y -> s     | otherwise -> t   t -> t @@ -464,32 +488,34 @@  explode :: ATerm -> Term explode = \case-  ATVar ty x             -> TAscr (TVar (coerce (qname x))) (toPolyType ty)-  ATPrim ty x            -> TAscr (TPrim x) (toPolyType ty)-  ATLet ty tel           -> TAscr (TLet (explodeTelescope explodeBinding tel)) (toPolyType ty)-  ATUnit                 -> TUnit-  ATBool _ty b           -> TBool b-  ATNat ty x             -> TAscr (TNat x) (toPolyType ty)-  ATRat r                -> TRat r-  ATChar c               -> TChar c-  ATString cs            -> TString cs-  ATAbs q ty a           -> TAscr (TAbs q (explodeAbs a)) (toPolyType ty)-  ATApp ty x y           -> TAscr (TApp (explode x) (explode y)) (toPolyType ty)-  ATTup ty xs            -> TAscr (TTup (map explode xs)) (toPolyType ty)-  ATCase ty bs           -> TAscr (TCase (map explodeBranch bs)) (toPolyType ty)-  ATChain ty t ls        -> TAscr (TChain (explode t) (map explodeLink ls)) (toPolyType ty)-  ATTyOp ty x y          -> TAscr (TTyOp x y) (toPolyType ty)+  ATVar ty x -> TAscr (TVar (coerce (qname x))) (toPolyType ty)+  ATPrim ty x -> TAscr (TPrim x) (toPolyType ty)+  ATLet ty tel -> TAscr (TLet (explodeTelescope explodeBinding tel)) (toPolyType ty)+  ATUnit -> TUnit+  ATBool _ty b -> TBool b+  ATNat ty x -> TAscr (TNat x) (toPolyType ty)+  ATRat r -> TRat r+  ATChar c -> TChar c+  ATString cs -> TString cs+  ATAbs q ty a -> TAscr (TAbs q (explodeAbs a)) (toPolyType ty)+  ATApp ty x y -> TAscr (TApp (explode x) (explode y)) (toPolyType ty)+  ATTup ty xs -> TAscr (TTup (map explode xs)) (toPolyType ty)+  ATCase ty bs -> TAscr (TCase (map explodeBranch bs)) (toPolyType ty)+  ATChain ty t ls -> TAscr (TChain (explode t) (map explodeLink ls)) (toPolyType ty)+  ATTyOp ty x y -> TAscr (TTyOp x y) (toPolyType ty)   ATContainer ty c ts el ->     TAscr       (TContainer c (map (explode *** fmap explode) ts) (fmap (fmap explode) el))       (toPolyType ty)   ATContainerComp ty c b -> TAscr (TContainerComp c (explodeTelescope explodeQual b)) (toPolyType ty)-  ATTest _vs x           -> TAscr (explode x) (toPolyType TyProp)+  ATTest _vs x -> TAscr (explode x) (toPolyType TyProp) -explodeTelescope-  :: (Alpha a, Alpha b)-  => (a -> b) -> Bind (Telescope a) ATerm -> Bind (Telescope b) Term-explodeTelescope explodeBinder (unsafeUnbind -> (xs,at)) = bind (mapTelescope explodeBinder xs) (explode at)+explodeTelescope ::+  (Alpha a, Alpha b) =>+  (a -> b) ->+  Bind (Telescope a) ATerm ->+  Bind (Telescope b) Term+explodeTelescope explodeBinder (unsafeUnbind -> (xs, at)) = bind (mapTelescope explodeBinder xs) (explode at)  explodeBinding :: ABinding -> Binding explodeBinding (ABinding m b (unembed -> n)) = Binding m (coerce b) (embed (explode n))@@ -499,34 +525,34 @@  explodePattern :: APattern -> Pattern explodePattern = \case-  APVar ty x      -> PAscr (PVar (coerce x)) ty-  APWild ty       -> PAscr PWild ty-  APUnit          -> PUnit-  APBool b        -> PBool b-  APChar c        -> PChar c-  APString s      -> PString s-  APTup ty ps     -> PAscr (PTup (map explodePattern ps)) ty-  APInj ty s p    -> PAscr (PInj s (explodePattern p)) ty-  APNat ty n      -> PAscr (PNat n) ty+  APVar ty x -> PAscr (PVar (coerce x)) ty+  APWild ty -> PAscr PWild ty+  APUnit -> PUnit+  APBool b -> PBool b+  APChar c -> PChar c+  APString s -> PString s+  APTup ty ps -> PAscr (PTup (map explodePattern ps)) ty+  APInj ty s p -> PAscr (PInj s (explodePattern p)) ty+  APNat ty n -> PAscr (PNat n) ty   APCons ty p1 p2 -> PAscr (PCons (explodePattern p1) (explodePattern p2)) ty-  APList ty ps    -> PAscr (PList (map explodePattern ps)) ty-  APAdd ty s p t  -> PAscr (PAdd s (explodePattern p) (explode t)) ty-  APMul ty s p t  -> PAscr (PMul s (explodePattern p) (explode t)) ty-  APSub ty p t    -> PAscr (PSub (explodePattern p) (explode t)) ty-  APNeg ty p      -> PAscr (PNeg (explodePattern p)) ty-  APFrac ty p q   -> PAscr (PFrac (explodePattern p) (explodePattern q)) ty+  APList ty ps -> PAscr (PList (map explodePattern ps)) ty+  APAdd ty s p t -> PAscr (PAdd s (explodePattern p) (explode t)) ty+  APMul ty s p t -> PAscr (PMul s (explodePattern p) (explode t)) ty+  APSub ty p t -> PAscr (PSub (explodePattern p) (explode t)) ty+  APNeg ty p -> PAscr (PNeg (explodePattern p)) ty+  APFrac ty p q -> PAscr (PFrac (explodePattern p) (explodePattern q)) ty  explodeBranch :: ABranch -> Branch explodeBranch = explodeTelescope explodeGuard  explodeGuard :: AGuard -> Guard-explodeGuard (AGBool (unembed -> at))   = GBool (embed (explode at))+explodeGuard (AGBool (unembed -> at)) = GBool (embed (explode at)) explodeGuard (AGPat (unembed -> at) ap) = GPat (embed (explode at)) (explodePattern ap)-explodeGuard (AGLet ab)                 = GLet (explodeBinding ab)+explodeGuard (AGLet ab) = GLet (explodeBinding ab)  explodeLink :: ALink -> Link explodeLink (ATLink bop at) = TLink bop (explode at)  explodeQual :: AQual -> Qual explodeQual (AQBind x (unembed -> at)) = QBind (coerce x) (embed (explode at))-explodeQual (AQGuard (unembed -> at))  = QGuard (embed (explode at))+explodeQual (AQGuard (unembed -> at)) = QGuard (embed (explode at))
src/Disco/Compile.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Compile -- Copyright   :  disco team and contributors@@ -8,37 +11,39 @@ -- -- Compiling the typechecked, desugared AST to the untyped core -- language.------------------------------------------------------------------------------- module Disco.Compile where -import           Control.Monad                    ((<=<))-import           Data.Bool                        (bool)-import           Data.Coerce-import qualified Data.Map                         as M-import           Data.Ratio-import           Data.Set                         (Set)-import qualified Data.Set                         as S-import           Data.Set.Lens                    (setOf)+import Control.Monad ((<=<))+import Data.Bool (bool)+import Data.Coerce+import qualified Data.Map as M+import Data.Ratio+import Data.Set (Set)+import qualified Data.Set as S+import Data.Set.Lens (setOf) -import           Disco.Effects.Fresh-import           Polysemy                         (Member, Sem, run)-import           Unbound.Generics.LocallyNameless (Name, bind, string2Name,-                                                   unembed)+import Disco.Effects.Fresh+import Polysemy (Member, Sem, run)+import Unbound.Generics.LocallyNameless (+  Name,+  bind,+  string2Name,+  unembed,+ ) -import           Disco.AST.Core-import           Disco.AST.Desugared-import           Disco.AST.Generic-import           Disco.AST.Typed-import           Disco.Context                    as Ctx-import           Disco.Desugar-import           Disco.Module-import           Disco.Names-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import qualified Disco.Typecheck.Graph            as G-import           Disco.Types-import           Disco.Util+import Disco.AST.Core+import Disco.AST.Desugared+import Disco.AST.Generic+import Disco.AST.Typed+import Disco.Context as Ctx+import Disco.Desugar+import Disco.Module+import Disco.Names+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import qualified Disco.Typecheck.Graph as G+import Disco.Types+import Disco.Util  ------------------------------------------------------------ -- Convenience operations@@ -106,19 +111,18 @@   -- have   --   --   fT = force (delay fL. [force fL / fT] body)-  | f `S.member` setOf fvQ defn = return . (:[]) $-    (fT, CForce (CProj L (CDelay (bind [qname fL] [substQC fT (CForce (CVar fL)) cdefn]))))-+  | f `S.member` setOf fvQ defn =+      return . (: []) $+        (fT, CForce (CProj L (CDelay (bind [qname fL] [substQC fT (CForce (CVar fL)) cdefn]))))   -- A non-recursive definition just compiles simply.   | otherwise =-    return [(fT, cdefn)]--  where-    fT, fL :: QName Core-    fT = coerce f-    fL = localName (coerce (qname f))+      return [(fT, cdefn)]+ where+  fT, fL :: QName Core+  fT = coerce f+  fL = localName (coerce (qname f)) -    cdefn = compileThing desugarDefn defn+  cdefn = compileThing desugarDefn defn  -- A group of mutually recursive definitions  {f = fbody, g = gbody, ...} -- compiles to@@ -140,12 +144,12 @@       bodies' :: [Core]       bodies' = map (substsQC forceVars . compileThing desugarDefn) bodies   return $-    (grp, CDelay (bind (map qname varsL) bodies')) :-    zip varsT (for [0 ..] $ CForce . flip proj (CVar grp))-  where-    proj :: Int -> Core -> Core-    proj 0 = CProj L-    proj n = proj (n -1) . CProj R+    (grp, CDelay (bind (map qname varsL) bodies'))+      : zip varsT (for [0 ..] $ CForce . flip proj (CVar grp))+ where+  proj :: Int -> Core -> Core+  proj 0 = CProj L+  proj n = proj (n - 1) . CProj R  ------------------------------------------------------------ -- Compiling terms@@ -166,22 +170,22 @@   cbody <- compileDTerm body   case q of     Lam -> return $ abstract xs cbody-    Ex  -> return $ quantify (OExists tys) (abstract xs cbody)+    Ex -> return $ quantify (OExists tys) (abstract xs cbody)     All -> return $ quantify (OForall tys) (abstract xs cbody)-  where-    -- Gather nested abstractions with the same quantifier.-    unbindDeep :: Member Fresh r => DTerm -> Sem r ([Name DTerm], [Type], DTerm)-    unbindDeep (DTAbs q' ty l) | q == q' = do-      (name, inner) <- unbind l-      (ns, tys, body) <- unbindDeep inner-      return (name : ns, ty : tys, body)-    unbindDeep t = return ([], [], t)+ where+  -- Gather nested abstractions with the same quantifier.+  unbindDeep :: Member Fresh r => DTerm -> Sem r ([Name DTerm], [Type], DTerm)+  unbindDeep (DTAbs q' ty l) | q == q' = do+    (name, inner) <- unbind l+    (ns, tys, body) <- unbindDeep inner+    return (name : ns, ty : tys, body)+  unbindDeep t = return ([], [], t) -    abstract :: [Name DTerm] -> Core -> Core-    abstract xs body = CAbs (bind (map coerce xs) body)+  abstract :: [Name DTerm] -> Core -> Core+  abstract xs body = CAbs (bind (map coerce xs) body) -    quantify :: Op -> Core -> Core-    quantify op = CApp (CConst op)+  quantify :: Op -> Core -> Core+  quantify op = CApp (CConst op)  -- Special case for Cons, which compiles to a constructor application -- rather than a function application.@@ -197,12 +201,12 @@   CPair <$> compileDTerm t1 <*> compileDTerm t2 compileDTerm (DTCase _ bs) = CApp <$> compileCase bs <*> pure CUnit compileDTerm (DTTyOp _ op ty) = return $ CApp (CConst (tyOps ! op)) (CType ty)-  where-    tyOps =-      M.fromList-        [ Enumerate ==> OEnum,-          Count ==> OCount-        ]+ where+  tyOps =+    M.fromList+      [ Enumerate ==> OEnum+      , Count ==> OCount+      ] compileDTerm (DTNil _) = return $ CInj L CUnit compileDTerm (DTTest info t) = CTest (coerce info) <$> compileDTerm t @@ -231,27 +235,27 @@   hd <- fresh (string2Name "hd")   tl <- fresh (string2Name "tl")   return $ CAbs $ bind [hd, tl] $ CInj R (CPair (CVar (localName hd)) (CVar (localName tl)))- compilePrim _ PrimLeft = do   a <- fresh (string2Name "a")   return $ CAbs $ bind [a] $ CInj L (CVar (localName a))- compilePrim _ PrimRight = do   a <- fresh (string2Name "a")   return $ CAbs $ bind [a] $ CInj R (CVar (localName a))- compilePrim (ty1 :*: ty2 :->: resTy) (PrimBOp bop) = return $ compileBOp ty1 ty2 resTy bop compilePrim ty p@(PrimBOp _) = compilePrimErr p ty compilePrim _ PrimSqrt = return $ CConst OSqrt compilePrim _ PrimFloor = return $ CConst OFloor compilePrim _ PrimCeil = return $ CConst OCeil-compilePrim (TySet _ :->: _) PrimAbs = return $-  CVar (Named Stdlib "container" .- string2Name "setSize")-compilePrim (TyBag _ :->: _) PrimAbs = return $-  CVar (Named Stdlib "container" .- string2Name "bagSize")-compilePrim (TyList _ :->: _) PrimAbs = return $-  CVar (Named Stdlib "list" .- string2Name "length")-compilePrim _                PrimAbs = return $ CConst OAbs+compilePrim (TySet _ :->: _) PrimAbs =+  return $+    CVar (Named Stdlib "container" .- string2Name "setSize")+compilePrim (TyBag _ :->: _) PrimAbs =+  return $+    CVar (Named Stdlib "container" .- string2Name "bagSize")+compilePrim (TyList _ :->: _) PrimAbs =+  return $+    CVar (Named Stdlib "list" .- string2Name "length")+compilePrim _ PrimAbs = return $ CConst OAbs compilePrim (TySet _ :->: _) PrimPower = return $ CConst OPower compilePrim (TyBag _ :->: _) PrimPower = return $ CConst OPower compilePrim ty PrimPower = compilePrimErr PrimPower ty@@ -284,34 +288,38 @@ compilePrim ty PrimConnect = compilePrimErr PrimConnect ty compilePrim _ PrimInsert = return $ CConst OInsert compilePrim _ PrimLookup = return $ CConst OLookup-compilePrim (_ :*: TyList _ :->: _) PrimEach = return $-  CVar (Named Stdlib "list" .- string2Name "eachlist")+compilePrim (_ :*: TyList _ :->: _) PrimEach =+  return $+    CVar (Named Stdlib "list" .- string2Name "eachlist") compilePrim (_ :*: TyBag _ :->: TyBag _) PrimEach = return $ CConst OEachBag compilePrim (_ :*: TySet _ :->: TySet _) PrimEach = return $ CConst OEachSet compilePrim ty PrimEach = compilePrimErr PrimEach ty compilePrim (_ :*: _ :*: TyList _ :->: _) PrimReduce =   return $ CVar (Named Stdlib "list" .- string2Name "foldr")-compilePrim (_ :*: _ :*: TyBag _ :->: _) PrimReduce = return $-  CVar (Named Stdlib "container" .- string2Name "reducebag")-compilePrim (_ :*: _ :*: TySet _ :->: _) PrimReduce = return $-  CVar (Named Stdlib "container" .- string2Name "reduceset")+compilePrim (_ :*: _ :*: TyBag _ :->: _) PrimReduce =+  return $+    CVar (Named Stdlib "container" .- string2Name "reducebag")+compilePrim (_ :*: _ :*: TySet _ :->: _) PrimReduce =+  return $+    CVar (Named Stdlib "container" .- string2Name "reduceset") compilePrim ty PrimReduce = compilePrimErr PrimReduce ty-compilePrim (_ :*: TyList _ :->: _) PrimFilter = return $-  CVar (Named Stdlib "list" .- string2Name "filterlist")+compilePrim (_ :*: TyList _ :->: _) PrimFilter =+  return $+    CVar (Named Stdlib "list" .- string2Name "filterlist") compilePrim (_ :*: TyBag _ :->: _) PrimFilter = return $ CConst OFilterBag compilePrim (_ :*: TySet _ :->: _) PrimFilter = return $ CConst OFilterBag compilePrim ty PrimFilter = compilePrimErr PrimFilter ty-compilePrim (_ :->: TyList _) PrimJoin = return $-  CVar (Named Stdlib "list" .- string2Name "concat")+compilePrim (_ :->: TyList _) PrimJoin =+  return $+    CVar (Named Stdlib "list" .- string2Name "concat") compilePrim (_ :->: TyBag _) PrimJoin = return $ CConst OBagUnions-compilePrim (_ :->: TySet _) PrimJoin = return $-  CVar (Named Stdlib "container" .- string2Name "unions")+compilePrim (_ :->: TySet _) PrimJoin =+  return $+    CVar (Named Stdlib "container" .- string2Name "unions") compilePrim ty PrimJoin = compilePrimErr PrimJoin ty- compilePrim (_ :*: TyBag _ :*: _ :->: _) PrimMerge = return $ CConst OMerge compilePrim (_ :*: TySet _ :*: _ :->: _) PrimMerge = return $ CConst OMerge-compilePrim ty                           PrimMerge = compilePrimErr PrimMerge ty-+compilePrim ty PrimMerge = compilePrimErr PrimMerge ty compilePrim _ PrimIsPrime = return $ CConst OIsPrime compilePrim _ PrimFactor = return $ CConst OFactor compilePrim _ PrimFrac = return $ CConst OFrac@@ -418,13 +426,14 @@   UOp ->   Core compileUOp _ op = CConst (coreUOps ! op)-  where-    -- Just look up the corresponding core operator.-    coreUOps =-      M.fromList-        [ Neg ==> ONeg,-          Fact ==> OFact-        ]+ where+  -- Just look up the corresponding core operator.+  coreUOps =+    M.fromList+      [ Neg ==> ONeg+      , Fact ==> OFact+      , Not ==> ONotProp+      ]  -- | Compile a binary operator.  This function needs to know the types --   of the arguments and result since some operators are overloaded@@ -462,13 +471,13 @@ -- addition and multiplication. compileBOp (TyGraph _) (TyGraph _) (TyGraph _) op   | op `elem` [Add, Mul] =-    CConst (regularOps ! op)-  where-    regularOps =-      M.fromList-        [ Add ==> OOverlay,-          Mul ==> OConnect-        ]+      CConst (regularOps ! op)+ where+  regularOps =+    M.fromList+      [ Add ==> OOverlay+      , Mul ==> OConnect+      ]  -- The Cartesian product operator just compiles to library function calls. compileBOp (TySet _) _ _ CartProd =@@ -477,28 +486,31 @@   CVar (Named Stdlib "container" .- string2Name "bagCP") compileBOp (TyList _) _ _ CartProd =   CVar (Named Stdlib "list" .- string2Name "listCP")- -- Some regular arithmetic operations that just translate straightforwardly. compileBOp _ _ _ op   | op `M.member` regularOps = CConst (regularOps ! op)-  where-    regularOps =-      M.fromList-        [ Add ==> OAdd,-          Mul ==> OMul,-          Div ==> ODiv,-          Exp ==> OExp,-          Mod ==> OMod,-          Divides ==> ODivides,-          Choose ==> OMultinom,-          Eq ==> OEq,-          Lt ==> OLt-        ]+ where+  regularOps =+    M.fromList+      [ Add ==> OAdd+      , Mul ==> OMul+      , Div ==> ODiv+      , Exp ==> OExp+      , Mod ==> OMod+      , Divides ==> ODivides+      , Choose ==> OMultinom+      , Eq ==> OEq+      , Lt ==> OLt+      , And ==> OAnd+      , Or ==> OOr+      , Impl ==> OImpl+      ]  -- ShouldEq needs to know the type at which the comparison is -- occurring, so values can be correctly pretty-printed if the test -- fails. compileBOp ty _ _ ShouldEq = CConst (OShouldEq ty)+compileBOp ty _ _ ShouldLt = CConst (OShouldLt ty) compileBOp _ty (TyList _) _ Elem = CConst OListElem compileBOp _ty _ _ Elem = CConst OBagElem compileBOp ty1 ty2 resTy op =
src/Disco/Context.hs view
@@ -1,6 +1,11 @@ {-# LANGUAGE DeriveTraversable #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Context -- Copyright   :  disco team and contributors@@ -9,81 +14,80 @@ -- A *context* is a mapping from names to other things (such as types -- or values).  This module defines a generic type of contexts which -- is used in many different places throughout the disco codebase.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Context-       ( -- * Context type-         Ctx--         -- * Construction-       , emptyCtx-       , singleCtx-       , fromList-       , ctxForModule-       , localCtx+module Disco.Context (+  -- * Context type+  Ctx, -       -- * Insertion-       , insert-       , extend-       , extends+  -- * Construction+  emptyCtx,+  singleCtx,+  fromList,+  ctxForModule,+  localCtx, -       -- * Query-       , null-       , lookup, lookup'-       , lookupNonLocal, lookupNonLocal'-       , lookupAll, lookupAll'+  -- * Insertion+  insert,+  extend,+  extends, -       -- * Conversion-       , names-       , elems-       , assocs-       , keysSet+  -- * Query+  null,+  lookup,+  lookup',+  lookupNonLocal,+  lookupNonLocal',+  lookupAll,+  lookupAll', -       -- * Traversal-       , coerceKeys-       , restrictKeys+  -- * Conversion+  names,+  elems,+  assocs,+  keysSet, -       -- * Combination-       , joinCtx-       , joinCtxs+  -- * Traversal+  coerceKeys,+  restrictKeys, -       -- * Filter-       , filter+  -- * Combination+  joinCtx,+  joinCtxs, -       ) where+  -- * Filter+  filter,+) where -import           Control.Monad                    ((<=<))-import           Data.Bifunctor                   (first, second)-import           Data.Coerce-import           Data.Map                         (Map)-import qualified Data.Map                         as M-import           Data.Map.Merge.Lazy              as MM-import           Data.Set                         (Set)-import qualified Data.Set                         as S-import           Prelude                          hiding (filter, lookup, null)+import Control.Monad ((<=<))+import Data.Bifunctor (first, second)+import Data.Coerce+import Data.Map (Map)+import qualified Data.Map as M+import Data.Map.Merge.Lazy as MM+import Data.Set (Set)+import qualified Data.Set as S+import Prelude hiding (filter, lookup, null) -import           Unbound.Generics.LocallyNameless (Name)+import Unbound.Generics.LocallyNameless (Name) -import           Polysemy-import           Polysemy.Reader+import Polysemy+import Polysemy.Reader -import           Disco.Names                      (ModuleName,-                                                   NameProvenance (..),-                                                   QName (..))+import Disco.Names (+  ModuleName,+  NameProvenance (..),+  QName (..),+ )  -- | A context maps qualified names to things.  In particular a @Ctx a --   b@ maps qualified names for @a@s to values of type @b@.-newtype Ctx a b = Ctx { getCtx :: M.Map NameProvenance (M.Map (Name a) b) }+newtype Ctx a b = Ctx {getCtx :: M.Map NameProvenance (M.Map (Name a) b)}   deriving (Eq, Show, Functor, Foldable, Traversable) -  -- Note that we implement a context as a nested map from-  -- NameProvenance to Name to b, rather than as a Map QName b.  They-  -- are isomorphic, but this way it is easier to do name resolution,-  -- because given an (unqualified) Name, we can look it up in each-  -- inner map corresponding to modules that are in scope.+-- Note that we implement a context as a nested map from+-- NameProvenance to Name to b, rather than as a Map QName b.  They+-- are isomorphic, but this way it is easier to do name resolution,+-- because given an (unqualified) Name, we can look it up in each+-- inner map corresponding to modules that are in scope.  instance Semigroup (Ctx a b) where   (<>) = joinCtx@@ -159,8 +163,8 @@ -- | Look up all the non-local bindings of a name in a context. lookupNonLocal' :: Name a -> Ctx a b -> [(ModuleName, b)] lookupNonLocal' n = nonLocal . lookupAll' n-  where-    nonLocal bs = [(m,b) | (QName (QualifiedName m) _, b) <- bs]+ where+  nonLocal bs = [(m, b) | (QName (QualifiedName m) _, b) <- bs]  -- | Look up all the bindings of an (unqualified) name in an ambient context. lookupAll :: Member (Reader (Ctx a b)) r => Name a -> Sem r [(QName a, b)]@@ -186,9 +190,9 @@ --   context. assocs :: Ctx a b -> [(QName a, b)] assocs = concatMap (uncurry modAssocs) . M.assocs . getCtx-  where-    modAssocs :: NameProvenance -> Map (Name a) b -> [(QName a, b)]-    modAssocs p = map (first (QName p)) . M.assocs+ where+  modAssocs :: NameProvenance -> Map (Name a) b -> [(QName a, b)]+  modAssocs p = map (first (QName p)) . M.assocs  -- | Return a set of all qualified names in the context. keysSet :: Ctx a b -> Set (QName a)@@ -205,9 +209,9 @@ -- | Restrict a context to only the keys in the given set. restrictKeys :: Ctx a b -> Set (QName a) -> Ctx a b restrictKeys ctx xs = Ctx . restrict m . getCtx $ ctx-  where-    restrict = MM.merge MM.dropMissing MM.dropMissing (MM.zipWithMatched (\_ ns m' -> M.restrictKeys m' ns))-    m = M.fromListWith S.union . map (\(QName p n) -> (p, S.singleton n)) . S.toList $ xs+ where+  restrict = MM.merge MM.dropMissing MM.dropMissing (MM.zipWithMatched (\_ ns m' -> M.restrictKeys m' ns))+  m = M.fromListWith S.union . map (\(QName p n) -> (p, S.singleton n)) . S.toList $ xs  ------------------------------------------------------------ -- Combination@@ -217,7 +221,7 @@ --   exists in both contexts, the result will use the value from the --   first context, and throw away the value from the second.). joinCtx :: Ctx a b -> Ctx a b -> Ctx a b-joinCtx a b = joinCtxs [a,b]+joinCtx a b = joinCtxs [a, b]  -- | Join a list of contexts (left-biased). joinCtxs :: [Ctx a b] -> Ctx a b
src/Disco/Data.hs view
@@ -1,25 +1,25 @@-{-# OPTIONS_GHC -Wno-orphans #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -Wno-orphans #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Data -- Copyright   :  disco team and contributors -- Maintainer  :  byorgey@gmail.com -- -- Some orphan 'Data' instances.---------------------------------------------------------------------------------- module Disco.Data where -import           Unbound.Generics.LocallyNameless.Bind-import           Unbound.Generics.LocallyNameless.Embed-import           Unbound.Generics.LocallyNameless.Name+import Unbound.Generics.LocallyNameless.Bind+import Unbound.Generics.LocallyNameless.Embed+import Unbound.Generics.LocallyNameless.Name -import           Data.Data                               (Data)-import           Unbound.Generics.LocallyNameless.Rebind+import Data.Data (Data)+import Unbound.Generics.LocallyNameless.Rebind  ------------------------------------------------------------ -- Some orphan instances@@ -29,4 +29,3 @@ deriving instance Data t => Data (Embed t) deriving instance (Data a, Data b) => Data (Rebind a b) deriving instance Data a => Data (Name a)-
src/Disco/Desugar.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Desugar -- Copyright   :  disco team and contributors@@ -8,43 +11,49 @@ -- -- Desugaring the typechecked surface language to a (still typed) -- simpler language.-----------------------------------------------------------------------------------module Disco.Desugar-       ( -- * Running desugaring computations-         runDesugar+module Disco.Desugar (+  -- * Running desugaring computations+  runDesugar, -         -- * Programs, terms, and properties-       , desugarDefn, desugarTerm, desugarProperty+  -- * Programs, terms, and properties+  desugarDefn,+  desugarTerm,+  desugarProperty, -         -- * Case expressions and patterns-       , desugarBranch, desugarGuards-       )-       where+  -- * Case expressions and patterns+  desugarBranch,+  desugarGuards,+)+where -import           Control.Monad.Cont-import           Data.Bool                               (bool)-import           Data.Coerce-import           Data.Maybe                              (fromMaybe, isJust)+import Control.Monad.Cont+import Data.Bool (bool)+import Data.Coerce+import Data.Maybe (fromMaybe, isJust) -import           Disco.AST.Desugared-import           Disco.AST.Surface-import           Disco.AST.Typed-import           Disco.Module-import           Disco.Names-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Typecheck                         (containerTy)-import           Disco.Types+import Disco.AST.Desugared+import Disco.AST.Surface+import Disco.AST.Typed+import Disco.Module+import Disco.Names+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Typecheck (containerTy)+import Disco.Types -import           Disco.Effects.Fresh-import           Polysemy                                (Member, Sem, run)-import           Unbound.Generics.LocallyNameless        (Bind, Name, bind,-                                                          embed, name2String,-                                                          string2Name, unembed,-                                                          unrebind)-import           Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)+import Disco.Effects.Fresh+import Polysemy (Member, Sem, run)+import Unbound.Generics.LocallyNameless (+  Bind,+  Name,+  bind,+  embed,+  name2String,+  string2Name,+  unembed,+  unrebind,+ )+import Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)  ------------------------------------------------------------ -- Running desugaring computations@@ -53,11 +62,12 @@ -- | Run a desugaring computation. runDesugar :: Sem '[Fresh] a -> a runDesugar = run . runFresh1-  -- Using runFresh1 is a bit of a hack; that way we won't-  -- ever pick a name with #0 (which is what is generated by default-  -- by string2Name), hence won't conflict with any existing free-  -- variables which came from the parser. +-- Using runFresh1 is a bit of a hack; that way we won't+-- ever pick a name with #0 (which is what is generated by default+-- by string2Name), hence won't conflict with any existing free+-- variables which came from the parser.+ ------------------------------------------------------------ -- ATerm DSL ------------------------------------------------------------@@ -71,20 +81,20 @@  tapp :: ATerm -> ATerm -> ATerm tapp t1 t2 = ATApp resTy t1 t2-  where-    resTy = case getType t1 of-      (_ :->: r) -> r-      ty         -> error $ "Impossible! Got non-function type " ++ show ty ++ " in tapp"+ where+  resTy = case getType t1 of+    (_ :->: r) -> r+    ty -> error $ "Impossible! Got non-function type " ++ show ty ++ " in tapp"  mkBin :: Type -> BOp -> ATerm -> ATerm -> ATerm-mkBin resTy bop t1 t2-  = tapp (ATPrim (getType t1 :*: getType t2 :->: resTy) (PrimBOp bop)) (mkPair t1 t2)+mkBin resTy bop t1 t2 =+  tapp (ATPrim (getType t1 :*: getType t2 :->: resTy) (PrimBOp bop)) (mkPair t1 t2)  mkUn :: Type -> UOp -> ATerm -> ATerm mkUn resTy uop t = tapp (ATPrim (getType t :->: resTy) (PrimUOp uop)) t  mkPair :: ATerm -> ATerm -> ATerm-mkPair t1 t2 = mkTup [t1,t2]+mkPair t1 t2 = mkTup [t1, t2]  mkTup :: [ATerm] -> ATerm mkTup ts = ATTup (foldr1 (:*:) (map getType ts)) ts@@ -151,14 +161,14 @@  dtapp :: DTerm -> DTerm -> DTerm dtapp t1 t2 = DTApp resTy t1 t2-  where-    resTy = case getType t1 of-      (_ :->: r) -> r-      ty         -> error $ "Impossible! Got non-function type " ++ show ty ++ " in dtapp"+ where+  resTy = case getType t1 of+    (_ :->: r) -> r+    ty -> error $ "Impossible! Got non-function type " ++ show ty ++ " in dtapp"  dtbin :: Type -> Prim -> DTerm -> DTerm -> DTerm-dtbin resTy p dt1 dt2-  = dtapp (DTPrim (getType dt1 :*: getType dt2 :->: resTy) p) (mkDTPair dt1 dt2)+dtbin resTy p dt1 dt2 =+  dtapp (DTPrim (getType dt1 :*: getType dt2 :->: resTy) p) (mkDTPair dt1 dt2)  mkDTPair :: DTerm -> DTerm -> DTerm mkDTPair dt1 dt2 = DTPair (getType dt1 :*: getType dt2) dt1 dt2@@ -181,15 +191,14 @@ --   with their corresponding patterns. Definitions are abstractions --   (which happen to be named), and source-level lambdas are also --   abstractions (which happen to have only one clause).- desugarAbs :: Member Fresh r => Quantifier -> Type -> [Clause] -> Sem r DTerm -- Special case for compiling a single lambda with no pattern matching directly to a lambda desugarAbs Lam ty [cl@(unsafeUnbind -> ([APVar _ _], _))] = do   (ps, at) <- unbind cl   d <- desugarTerm at   return $ DTAbs Lam ty (bind (getVar (head ps)) d)-  where-    getVar (APVar _ x) = coerce x+ where+  getVar (APVar _ x) = coerce x -- General case desugarAbs quant overallTy body = do   clausePairs <- unbindClauses body@@ -205,36 +214,37 @@   let branches = zipWith (mkBranch (zip args patTys)) bodies pats   dcase <- desugarTerm $ ATCase bodyTy branches   return $ mkAbs quant overallTy patTys (coerce args) dcase--  where-    mkBranch :: [(Name ATerm, Type)] -> ATerm -> [APattern] -> ABranch-    mkBranch xs b ps = bind (mkGuards xs ps) b+ where+  mkBranch :: [(Name ATerm, Type)] -> ATerm -> [APattern] -> ABranch+  mkBranch xs b ps = bind (mkGuards xs ps) b -    mkGuards :: [(Name ATerm, Type)] -> [APattern] -> Telescope AGuard-    mkGuards xs ps = toTelescope $ zipWith AGPat (map (\(x,ty) -> embed (atVar ty x)) xs) ps+  mkGuards :: [(Name ATerm, Type)] -> [APattern] -> Telescope AGuard+  mkGuards xs ps = toTelescope $ zipWith AGPat (map (\(x, ty) -> embed (atVar ty x)) xs) ps -    -- To make searches fairer, we lift up directly nested abstractions-    -- with the same quantifier when there's only a single clause. That-    -- way, we generate a chain of abstractions followed by a case, instead-    -- of a bunch of alternating abstractions and cases.-    unbindClauses :: Member Fresh r => [Clause] -> Sem r [([APattern], ATerm)]-    unbindClauses [c] | quant `elem` [All, Ex] = do-      (ps, t) <- liftClause c-      return [(ps, addDbgInfo ps t)]-    unbindClauses cs  = mapM unbind cs+  -- To make searches fairer, we lift up directly nested abstractions+  -- with the same quantifier when there's only a single clause. That+  -- way, we generate a chain of abstractions followed by a case, instead+  -- of a bunch of alternating abstractions and cases.+  unbindClauses :: Member Fresh r => [Clause] -> Sem r [([APattern], ATerm)]+  unbindClauses [c] | quant `elem` [All, Ex] = do+    (ps, t) <- liftClause c+    return [(ps, addDbgInfo ps t)]+  unbindClauses cs = mapM unbind cs -    liftClause :: Member Fresh r => Bind [APattern] ATerm -> Sem r ([APattern], ATerm)-    liftClause c = unbind c >>= \case+  liftClause :: Member Fresh r => Bind [APattern] ATerm -> Sem r ([APattern], ATerm)+  liftClause c =+    unbind c >>= \case       (ps, ATAbs q _ c') | q == quant -> do         (ps', b) <- liftClause c'         return (ps ++ ps', b)       (ps, b) -> return (ps, b) -    -- Wrap a term in a test frame to report the values of all variables-    -- bound in the patterns.-    addDbgInfo :: [APattern] -> ATerm -> ATerm-    addDbgInfo ps t = ATTest (map withName $ concatMap varsBound ps) t-      where withName (n, ty) = (name2String n, ty, n)+  -- Wrap a term in a test frame to report the values of all variables+  -- bound in the patterns.+  addDbgInfo :: [APattern] -> ATerm -> ATerm+  addDbgInfo ps t = ATTest (map withName $ concatMap varsBound ps) t+   where+    withName (n, ty) = (name2String n, ty, n)  ------------------------------------------------------------ -- Term desugaring@@ -245,11 +255,14 @@ desugarCList2B :: Member Fresh r => Prim -> Type -> Type -> Type -> Sem r DTerm desugarCList2B p ty cts b = do   c <- fresh (string2Name "c")-  body <- desugarTerm $-    tapp (ATPrim (TyBag cts :->: TyBag b) p)-      (tapp (ATPrim (TyList cts :->: TyBag cts) PrimBag)-        (atVar (TyList cts) c)-      )+  body <-+    desugarTerm $+      tapp+        (ATPrim (TyBag cts :->: TyBag b) p)+        ( tapp+            (ATPrim (TyList cts :->: TyBag cts) PrimBag)+            (atVar (TyList cts) c)+        )   return $ mkLambda ty [c] body  -- | Desugar a typechecked term.@@ -261,43 +274,33 @@   | bopDesugars ty1 ty2 resTy bop = desugarPrimBOp ty1 ty2 resTy bop desugarTerm (ATPrim ty@(TyList cts :->: TyBag b) PrimC2B) = desugarCList2B PrimC2B ty cts b desugarTerm (ATPrim ty@(TyList cts :->: TyBag b) PrimUC2B) = desugarCList2B PrimUC2B ty cts b--desugarTerm (ATPrim ty x)        = return $ DTPrim ty x-desugarTerm ATUnit               = return DTUnit-desugarTerm (ATBool ty b)        = return $ DTBool ty b-desugarTerm (ATChar c)           = return $ DTChar c-desugarTerm (ATString cs)        =+desugarTerm (ATPrim ty x) = return $ DTPrim ty x+desugarTerm ATUnit = return DTUnit+desugarTerm (ATBool ty b) = return $ DTBool ty b+desugarTerm (ATChar c) = return $ DTChar c+desugarTerm (ATString cs) =   desugarContainer (TyList TyC) ListContainer (map (\c -> (ATChar c, Nothing)) cs) Nothing-desugarTerm (ATAbs q ty lam)     = desugarAbs q ty [lam]-+desugarTerm (ATAbs q ty lam) = desugarAbs q ty [lam] -- Special cases for fully applied operators desugarTerm (ATApp resTy (ATPrim _ (PrimUOp uop)) t)   | uopDesugars (getType t) resTy uop = desugarUnApp resTy uop t-desugarTerm (ATApp resTy (ATPrim _ (PrimBOp bop)) (ATTup _ [t1,t2]))+desugarTerm (ATApp resTy (ATPrim _ (PrimBOp bop)) (ATTup _ [t1, t2]))   | bopDesugars (getType t1) (getType t2) resTy bop = desugarBinApp resTy bop t1 t2--desugarTerm (ATApp ty t1 t2)     =+desugarTerm (ATApp ty t1 t2) =   DTApp ty <$> desugarTerm t1 <*> desugarTerm t2-desugarTerm (ATTup ty ts)        = desugarTuples ty ts-desugarTerm (ATNat ty n)         = return $ DTNat ty n-desugarTerm (ATRat r)            = return $ DTRat r--desugarTerm (ATTyOp ty op t)      = return $ DTTyOp ty op t--desugarTerm (ATChain _ t1 links)  = desugarTerm $ expandChain t1 links-+desugarTerm (ATTup ty ts) = desugarTuples ty ts+desugarTerm (ATNat ty n) = return $ DTNat ty n+desugarTerm (ATRat r) = return $ DTRat r+desugarTerm (ATTyOp ty op t) = return $ DTTyOp ty op t+desugarTerm (ATChain _ t1 links) = desugarTerm $ expandChain t1 links desugarTerm (ATContainer ty c es mell) = desugarContainer ty c es mell- desugarTerm (ATContainerComp _ ctr bqt) = do   (qs, t) <- unbind bqt   desugarComp ctr t qs- desugarTerm (ATLet _ t) = do   (bs, t2) <- unbind t   desugarLet (fromTelescope bs) t2- desugarTerm (ATCase ty bs) = DTCase ty <$> mapM desugarBranch bs- desugarTerm (ATTest info t) = DTTest (coerce info) <$> desugarTerm t  -- | Desugar a property by wrapping its corresponding term in a test@@ -313,7 +316,8 @@ --   desugared, given the type of the argument and result. uopDesugars :: Type -> Type -> UOp -> Bool -- uopDesugars _ (TyFin _) Neg = True-uopDesugars _ _         uop = uop == Not+uopDesugars TyProp TyProp Not = False+uopDesugars _ _ uop = uop == Not  desugarPrimUOp :: Member Fresh r => Type -> Type -> UOp -> Sem r DTerm desugarPrimUOp argTy resTy op = do@@ -324,16 +328,34 @@ -- | Test whether a given binary operator is one that needs to be --   desugared, given the two types of the arguments and the type of the result. bopDesugars :: Type -> Type -> Type -> BOp -> Bool-bopDesugars _   TyN _ Choose = True+bopDesugars _ TyN _ Choose = True -- bopDesugars _   _   (TyFin _) bop | bop `elem` [Add, Mul] = True-bopDesugars _   _   _ bop = bop `elem`-  [ And, Or, Impl, Iff-  , Neq, Gt, Leq, Geq, Min, Max-  , IDiv-  , Sub, SSub-  , Inter, Diff, Union, Subset-  ] +-- And, Or, Impl for Props don't desugar because they are primitive+-- Prop constructors.  On the other hand, logical operations on Bool+-- can desugar in terms of more primitive conditional expressions.+bopDesugars _ _ TyProp bop | bop `elem` [And, Or, Impl] = False+bopDesugars _ _ _ bop =+  bop+    `elem` [ And+           , Or+           , Impl+           , Iff+           , Neq+           , Gt+           , Leq+           , Geq+           , Min+           , Max+           , IDiv+           , Sub+           , SSub+           , Inter+           , Diff+           , Union+           , Subset+           ]+ -- | Desugar a primitive binary operator at the given type. desugarPrimBOp :: Member Fresh r => Type -> Type -> Type -> BOp -> Sem r DTerm desugarPrimBOp ty1 ty2 resTy op = do@@ -342,17 +364,18 @@   y <- fresh (string2Name "arg2")   let argsTy = ty1 :*: ty2   body <- desugarBinApp resTy op (atVar ty1 x) (atVar ty2 y)-  return $ mkLambda (argsTy :->: resTy) [p] $-    DTCase resTy-    [ bind-        (toTelescope [DGPat (embed (dtVar argsTy (coerce p))) (DPPair argsTy (coerce x) (coerce y))])-        body-    ]+  return $+    mkLambda (argsTy :->: resTy) [p] $+      DTCase+        resTy+        [ bind+            (toTelescope [DGPat (embed (dtVar argsTy (coerce p))) (DPPair argsTy (coerce x) (coerce y))])+            body+        ]  -- | Desugar a saturated application of a unary operator. --   The first argument is the type of the result. desugarUnApp :: Member Fresh r => Type -> UOp -> ATerm -> Sem r DTerm- -- Desugar negation on TyFin to a negation on TyZ followed by a mod. -- See the comments below re: Add and Mul on TyFin. -- desugarUnApp (TyFin n) Neg t =@@ -360,83 +383,84 @@  -- XXX This should be turned into a standard library definition. -- not t ==> {? false if t, true otherwise ?}-desugarUnApp _ Not t = desugarTerm $-  ATCase TyBool-    [ fls <==. [AGBool (embed t)]-    , tru <==. []-    ]-+desugarUnApp _ Not t =+  desugarTerm $+    ATCase+      TyBool+      [ fls <==. [AGBool (embed t)]+      , tru <==. []+      ] desugarUnApp ty uop t = error $ "Impossible! desugarUnApp " ++ show ty ++ " " ++ show uop ++ " " ++ show t  -- | Desugar a saturated application of a binary operator. --   The first argument is the type of the result. desugarBinApp :: Member Fresh r => Type -> BOp -> ATerm -> ATerm -> Sem r DTerm- -- Implies, and, or should all be turned into a standard library -- definition.  This will require first (1) adding support for -- modules/a standard library, including (2) the ability to define -- infix operators.  -- t1 and t2 ==> {? t2 if t1, false otherwise ?}-desugarBinApp _ And t1 t2 = desugarTerm $-  ATCase TyBool-    [ t2  <==. [tif t1]-    , fls <==. []-    ]-+desugarBinApp _ And t1 t2 =+  desugarTerm $+    ATCase+      TyBool+      [ t2 <==. [tif t1]+      , fls <==. []+      ] -- (t1 implies t2) ==> (not t1 or t2) desugarBinApp _ Impl t1 t2 = desugarTerm $ tnot t1 ||. t2- -- (t1 iff t2) ==> (t1 == t2) desugarBinApp _ Iff t1 t2 = desugarTerm $ t1 ==. t2- -- t1 or t2 ==> {? true if t1, t2 otherwise ?})-desugarBinApp _ Or t1 t2 = desugarTerm $-  ATCase TyBool-    [ tru <==. [tif t1]-    , t2  <==. []-    ]-+desugarBinApp _ Or t1 t2 =+  desugarTerm $+    ATCase+      TyBool+      [ tru <==. [tif t1]+      , t2 <==. []+      ] desugarBinApp _ Neq t1 t2 = desugarTerm $ tnot (t1 ==. t2)-desugarBinApp _ Gt  t1 t2 = desugarTerm $ t2 <. t1+desugarBinApp _ Gt t1 t2 = desugarTerm $ t2 <. t1 desugarBinApp _ Leq t1 t2 = desugarTerm $ tnot (t2 <. t1) desugarBinApp _ Geq t1 t2 = desugarTerm $ tnot (t1 <. t2)- -- XXX sharing!-desugarBinApp ty Min t1 t2 = desugarTerm $-  ATCase ty-    [ t1 <==. [tif (t1 <. t2)]-    , t2 <==. []-    ]--desugarBinApp ty Max t1 t2 = desugarTerm $-  ATCase ty-    [ t1 <==. [tif (t2 <. t1)]-    , t2 <==. []-    ]-+desugarBinApp ty Min t1 t2 =+  desugarTerm $+    ATCase+      ty+      [ t1 <==. [tif (t1 <. t2)]+      , t2 <==. []+      ]+desugarBinApp ty Max t1 t2 =+  desugarTerm $+    ATCase+      ty+      [ t1 <==. [tif (t2 <. t1)]+      , t2 <==. []+      ] -- t1 // t2 ==> floor (t1 / t2)-desugarBinApp resTy IDiv t1 t2 = desugarTerm $-  ATApp resTy (ATPrim (getType t1 :->: resTy) PrimFloor) (mkBin (getType t1) Div t1 t2)-+desugarBinApp resTy IDiv t1 t2 =+  desugarTerm $+    ATApp resTy (ATPrim (getType t1 :->: resTy) PrimFloor) (mkBin (getType t1) Div t1 t2) -- Desugar normal binomial coefficient (n choose k) to a multinomial -- coefficient with a singleton list, (n choose [k]). -- Note this will only be called when (getType t2 == TyN); see bopDesugars.-desugarBinApp _ Choose t1 t2-  = desugarTerm $ mkBin TyN Choose t1 (ctrSingleton ListContainer t2)--desugarBinApp ty Sub  t1 t2 = desugarTerm $ mkBin ty Add t1 (mkUn ty Neg t2)-desugarBinApp ty SSub t1 t2 = desugarTerm $-  -- t1 -. t2 ==> {? 0 if t1 < t2, t1 - t2 otherwise ?}-  ATCase ty-    [ ATNat ty 0         <==. [tif (t1 <. t2)]-    , mkBin ty Sub t1 t2 <==. []+desugarBinApp _ Choose t1 t2 =+  desugarTerm $ mkBin TyN Choose t1 (ctrSingleton ListContainer t2)+desugarBinApp ty Sub t1 t2 = desugarTerm $ mkBin ty Add t1 (mkUn ty Neg t2)+desugarBinApp ty SSub t1 t2 =+  desugarTerm $+    -- t1 -. t2 ==> {? 0 if t1 < t2, t1 - t2 otherwise ?}+    ATCase+      ty+      [ ATNat ty 0 <==. [tif (t1 <. t2)]+      , mkBin ty Sub t1 t2 <==. []       -- NOTE, the above is slightly bogus since the whole point of SSub is       -- because we can't subtract naturals.  However, this will       -- immediately desugar to a DTerm.  When we write a linting       -- typechecker for DTerms we should allow subtraction on TyN!-    ]-+      ] -- Addition and multiplication on TyFin just desugar to the operation -- followed by a call to mod. -- desugarBinApp (TyFin n) op t1 t2@@ -444,57 +468,60 @@ --       mkBin (TyFin n) Mod --         (mkBin TyN op t1 t2) --         (ATNat TyN n)-    -- Note the typing of this is a bit funny: t1 and t2 presumably-    -- have type (TyFin n), and now we are saying that applying 'op'-    -- to them results in TyN, then applying 'mod' results in a TyFin-    -- n again.  Using TyN as the intermediate result is necessary so-    -- we don't fall into an infinite desugaring loop, and intuitively-    -- makes sense because the idea is that we first do the operation-    -- as a normal operation in "natural land" and then do a mod.-    ---    -- We will have to think carefully about how the linting-    -- typechecker for DTerms should treat TyN and TyFin.  Probably-    -- something like this will work: TyFin is a subtype of TyN, and-    -- TyN can be turned into TyFin with mod.  (We don't want such-    -- typing rules in the surface disco language itself because-    -- implicit coercions from TyFin -> N don't commute with-    -- operations like addition and multiplication, e.g. 3+3 yields 1-    -- if we add them in Z5 and then coerce to Nat, but 6 if we first-    -- coerce both and then add.+-- Note the typing of this is a bit funny: t1 and t2 presumably+-- have type (TyFin n), and now we are saying that applying 'op'+-- to them results in TyN, then applying 'mod' results in a TyFin+-- n again.  Using TyN as the intermediate result is necessary so+-- we don't fall into an infinite desugaring loop, and intuitively+-- makes sense because the idea is that we first do the operation+-- as a normal operation in "natural land" and then do a mod.+--+-- We will have to think carefully about how the linting+-- typechecker for DTerms should treat TyN and TyFin.  Probably+-- something like this will work: TyFin is a subtype of TyN, and+-- TyN can be turned into TyFin with mod.  (We don't want such+-- typing rules in the surface disco language itself because+-- implicit coercions from TyFin -> N don't commute with+-- operations like addition and multiplication, e.g. 3+3 yields 1+-- if we add them in Z5 and then coerce to Nat, but 6 if we first+-- coerce both and then add.  -- Intersection, difference, and union all desugar to an application -- of 'merge' with an appropriate combining operation. desugarBinApp ty op t1 t2   | op `elem` [Inter, Diff, Union] =-    desugarTerm $-    tapps (ATPrim ((TyN :*: TyN :->: TyN) :*: ty :*: ty :->: ty) PrimMerge)-      [ ATPrim (TyN :*: TyN :->: TyN) (mergeOp ty op)-      , t1-      , t2-      ]-  where-    mergeOp _         Inter = PrimBOp Min-    mergeOp _         Diff  = PrimBOp SSub-    mergeOp (TySet _) Union = PrimBOp Max-    mergeOp (TyBag _) Union = PrimBOp Add-    mergeOp _         _     = error $ "Impossible! mergeOp " ++ show ty ++ " " ++ show op+      desugarTerm $+        tapps+          (ATPrim ((TyN :*: TyN :->: TyN) :*: ty :*: ty :->: ty) PrimMerge)+          [ ATPrim (TyN :*: TyN :->: TyN) (mergeOp ty op)+          , t1+          , t2+          ]+ where+  mergeOp _ Inter = PrimBOp Min+  mergeOp _ Diff = PrimBOp SSub+  mergeOp (TySet _) Union = PrimBOp Max+  mergeOp (TyBag _) Union = PrimBOp Add+  mergeOp _ _ = error $ "Impossible! mergeOp " ++ show ty ++ " " ++ show op  -- A ⊆ B  <==>  (A ⊔ B = B) --   where ⊔ denotes 'merge max'. --   Note it is NOT union, since this doesn't work for bags. --   e.g.  bag [1] union bag [1,2] =  bag [1,1,2] /= bag [1,2].-desugarBinApp _ Subset t1 t2 = desugarTerm $-  tapps (ATPrim (ty :*: ty :->: TyBool) (PrimBOp Eq))-  [ tapps (ATPrim ((TyN :*: TyN :->: TyN) :*: ty :*: ty :->: ty) PrimMerge)-    [ ATPrim (TyN :*: TyN :->: TyN) (PrimBOp Max)-    , t1-    , t2-    ]-  , t2   -- XXX sharing-  ]-  where-    ty = getType t1-+desugarBinApp _ Subset t1 t2 =+  desugarTerm $+    tapps+      (ATPrim (ty :*: ty :->: TyBool) (PrimBOp Eq))+      [ tapps+          (ATPrim ((TyN :*: TyN :->: TyN) :*: ty :*: ty :->: ty) PrimMerge)+          [ ATPrim (TyN :*: TyN :->: TyN) (PrimBOp Max)+          , t1+          , t2+          ]+      , t2 -- XXX sharing+      ]+ where+  ty = getType t1 desugarBinApp ty bop t1 t2 = error $ "Impossible! desugarBinApp " ++ show ty ++ " " ++ show bop ++ " " ++ show t1 ++ " " ++ show t2  ------------------------------------------------------------@@ -508,36 +535,37 @@  -- | Expand a container comprehension into an equivalent ATerm. expandComp :: Member Fresh r => Container -> ATerm -> Telescope AQual -> Sem r ATerm- -- [ t | ] = [ t ] expandComp ctr t TelEmpty = return $ ctrSingleton ctr t- -- [ t | q, qs ] = ...-expandComp ctr t (TelCons (unrebind -> (q,qs)))-  = case q of-      -- [ t | x in l, qs ] = join (map (\x -> [t | qs]) l)-      AQBind x (unembed -> lst) -> do-        tqs <- expandComp ctr t qs-        let c      = containerTy ctr-            tTy    = getType t-            xTy    = case getType lst of-                       TyContainer _ e -> e-                       _ -> error "Impossible! Not a container in expandComp"-            joinTy = c (c tTy) :->: c tTy-            mapTy  = (xTy :->: c tTy) :*: c xTy :->: c (c tTy)-        return $ tapp (ATPrim joinTy PrimJoin) $+expandComp ctr t (TelCons (unrebind -> (q, qs))) =+  case q of+    -- [ t | x in l, qs ] = join (map (\x -> [t | qs]) l)+    AQBind x (unembed -> lst) -> do+      tqs <- expandComp ctr t qs+      let c = containerTy ctr+          tTy = getType t+          xTy = case getType lst of+            TyContainer _ e -> e+            _ -> error "Impossible! Not a container in expandComp"+          joinTy = c (c tTy) :->: c tTy+          mapTy = (xTy :->: c tTy) :*: c xTy :->: c (c tTy)+      return $+        tapp (ATPrim joinTy PrimJoin) $           tapp             (ATPrim mapTy PrimEach)-            (mkPair-              (ATAbs Lam (xTy :->: c tTy) (bind [APVar xTy x] tqs))-              lst+            ( mkPair+                (ATAbs Lam (xTy :->: c tTy) (bind [APVar xTy x] tqs))+                lst             ) -      -- [ t | g, qs ] = if g then [ t | qs ] else []-      AQGuard (unembed -> g)    -> do-        tqs <- expandComp ctr t qs-        return $ ATCase (containerTy ctr (getType t))-          [ tqs                    <==. [tif g]+    -- [ t | g, qs ] = if g then [ t | qs ] else []+    AQGuard (unembed -> g) -> do+      tqs <- expandComp ctr t qs+      return $+        ATCase+          (containerTy ctr (getType t))+          [ tqs <==. [tif g]           , ctrNil ctr (getType t) <==. []           ] @@ -553,8 +581,8 @@ desugarLet [] t = desugarTerm t desugarLet ((ABinding _ x (unembed -> t1)) : ls) t =   dtapp-    <$> (DTAbs Lam (getType t1 :->: getType t)-          <$> (bind (coerce x) <$> desugarLet ls t)+    <$> ( DTAbs Lam (getType t1 :->: getType t)+            <$> (bind (coerce x) <$> desugarLet ls t)         )     <*> desugarTerm t1 @@ -569,26 +597,26 @@ --     @\x. \y. \z. q@ mkLambda :: Type -> [Name ATerm] -> DTerm -> DTerm mkLambda funty args c = go funty args-  where-    go _ []                    = c-    go ty@(_ :->: ty2) (x:xs) = DTAbs Lam ty (bind (coerce x) (go ty2 xs))-    go ty as = error $ "Impossible! mkLambda.go " ++ show ty ++ " " ++ show as+ where+  go _ [] = c+  go ty@(_ :->: ty2) (x : xs) = DTAbs Lam ty (bind (coerce x) (go ty2 xs))+  go ty as = error $ "Impossible! mkLambda.go " ++ show ty ++ " " ++ show as  mkQuant :: Quantifier -> [Type] -> [Name ATerm] -> DTerm -> DTerm mkQuant q argtys args c = foldr quantify c (zip args argtys)  where-   quantify (x, ty) body = DTAbs q ty (bind (coerce x) body)+  quantify (x, ty) body = DTAbs q ty (bind (coerce x) body)  mkAbs :: Quantifier -> Type -> [Type] -> [Name ATerm] -> DTerm -> DTerm mkAbs Lam funty _ args c = mkLambda funty args c-mkAbs q _ argtys args c  = mkQuant q argtys args c+mkAbs q _ argtys args c = mkQuant q argtys args c  -- | Desugar a tuple to nested pairs, /e.g./ @(a,b,c,d) ==> (a,(b,(c,d)))@.a desugarTuples :: Member Fresh r => Type -> [ATerm] -> Sem r DTerm-desugarTuples _ [t]                    = desugarTerm t-desugarTuples ty@(_ :*: ty2) (t:ts) = DTPair ty <$> desugarTerm t <*> desugarTuples ty2 ts-desugarTuples ty ats-  = error $ "Impossible! desugarTuples " ++ show ty ++ " " ++ show ats+desugarTuples _ [t] = desugarTerm t+desugarTuples ty@(_ :*: ty2) (t : ts) = DTPair ty <$> desugarTerm t <*> desugarTuples ty2 ts+desugarTuples ty ats =+  error $ "Impossible! desugarTuples " ++ show ty ++ " " ++ show ats  -- | Expand a chain of comparisons into a sequence of binary --   comparisons combined with @and@.  Note we only expand it into@@ -600,7 +628,9 @@ expandChain _ [] = error "Can't happen! expandChain _ []" expandChain t1 [ATLink op t2] = mkBin TyBool op t1 t2 expandChain t1 (ATLink op t2 : links) =-  mkBin TyBool And+  mkBin+    TyBool+    And     (mkBin TyBool op t1 t2)     (expandChain t2 links) @@ -609,7 +639,7 @@ desugarBranch b = do   (ags, at) <- unbind b   dgs <- desugarGuards ags-  d   <- desugarTerm at+  d <- desugarTerm at   return $ bind dgs d  -- | Desugar the list of guards in one branch of a case expression.@@ -617,215 +647,208 @@ --   turned into pattern guards which match against @true@. desugarGuards :: Member Fresh r => Telescope AGuard -> Sem r (Telescope DGuard) desugarGuards = fmap (toTelescope . concat) . mapM desugarGuard . fromTelescope-  where-    desugarGuard :: Member Fresh r => AGuard -> Sem r [DGuard]--    -- A Boolean guard is desugared to a pattern-match on @true = right(unit)@.-    desugarGuard (AGBool (unembed -> at)) = do-      dt <- desugarTerm at-      desugarMatch dt (APInj TyBool R APUnit)--    -- 'let x = t' is desugared to 'when t is x'.-    desugarGuard (AGLet (ABinding _ x (unembed -> at))) = do-      dt <- desugarTerm at-      varMatch dt (coerce x)--    -- Desugaring 'when t is p' is the most complex case; we have to-    -- break down the pattern and match it incrementally.-    desugarGuard (AGPat (unembed -> at) p) = do-      dt <- desugarTerm at-      desugarMatch dt p+ where+  desugarGuard :: Member Fresh r => AGuard -> Sem r [DGuard] -    -- Desugar a guard of the form 'when dt is p'.  An entire match is-    -- the right unit to desugar --- as opposed to, say, writing a-    -- function to desugar a pattern --- since a match may desugar to-    -- multiple matches, and on recursive calls we need to know what-    -- term/variable should be bound to the pattern.-    ---    -- A match may desugar to multiple matches for two reasons:-    ---    --   1. Nested patterns 'explode' into a 'telescope' matching one-    --   constructor at a time, for example, 'when t is (x,y,3)'-    --   becomes 'when t is (x,x0) when x0 is (y,x1) when x1 is 3'.-    --   This makes the order of matching explicit and enables lazy-    --   matching without requiring special support from the-    --   interpreter other than WHNF reduction.-    ---    --   2. Matches against arithmetic patterns desugar to a-    --   combination of matching, computation, and boolean checks.-    --   For example, 'when t is (y+1)' becomes 'when t is x0 if x0 >=-    --   1 let y = x0-1'.-    desugarMatch :: Member Fresh r => DTerm -> APattern -> Sem r [DGuard]-    desugarMatch dt (APVar ty x)      = mkMatch dt (DPVar ty (coerce x))-    desugarMatch _  (APWild _)        = return []-    desugarMatch dt APUnit            = mkMatch dt DPUnit-    desugarMatch dt (APBool b)        = desugarMatch dt (APInj TyBool (bool L R b) APUnit)-    desugarMatch dt (APNat ty n)      = desugarMatch (dtbin TyBool (PrimBOp Eq) dt (DTNat ty n)) (APBool True)-    desugarMatch dt (APChar c)        = desugarMatch (dtbin TyBool (PrimBOp Eq) dt (DTChar c)) (APBool True)-    desugarMatch dt (APString s)      = desugarMatch dt (APList (TyList TyC) (map APChar s))-    desugarMatch dt (APTup tupTy pat) = desugarTuplePats tupTy dt pat-      where-        desugarTuplePats :: Member Fresh r => Type -> DTerm -> [APattern] -> Sem r [DGuard]-        desugarTuplePats _ _  [] = error "Impossible! desugarTuplePats []"-        desugarTuplePats _ t [p] = desugarMatch t p-        desugarTuplePats ty@(_ :*: ty2) t (p:ps) = do-          (x1,gs1) <- varForPat p-          (x2,gs2) <- case ps of-            [APVar _ px2] -> return (coerce px2, [])-            _             -> do-              x <- fresh (string2Name "x")-              (x,) <$> desugarTuplePats ty2 (dtVar ty2 x) ps-          fmap concat . sequence $-            [ mkMatch t $ DPPair ty x1 x2-            , return gs1-            , return gs2-            ]-        desugarTuplePats ty _ _-          = error $ "Impossible! desugarTuplePats with non-pair type " ++ show ty+  -- A Boolean guard is desugared to a pattern-match on @true = right(unit)@.+  desugarGuard (AGBool (unembed -> at)) = do+    dt <- desugarTerm at+    desugarMatch dt (APInj TyBool R APUnit) -    desugarMatch dt (APInj ty s p) = do-      (x,gs) <- varForPat p-      fmap concat . sequence $-        [ mkMatch dt $ DPInj ty s x-        , return gs-        ]+  -- 'let x = t' is desugared to 'when t is x'.+  desugarGuard (AGLet (ABinding _ x (unembed -> at))) = do+    dt <- desugarTerm at+    varMatch dt (coerce x) -    desugarMatch dt (APCons ty p1 p2) = do-      y <- fresh (string2Name "y")-      (x1, gs1) <- varForPat p1-      (x2, gs2) <- varForPat p2+  -- Desugaring 'when t is p' is the most complex case; we have to+  -- break down the pattern and match it incrementally.+  desugarGuard (AGPat (unembed -> at) p) = do+    dt <- desugarTerm at+    desugarMatch dt p -      let eltTy = getType p1-          unrolledTy = eltTy :*: ty+  -- Desugar a guard of the form 'when dt is p'.  An entire match is+  -- the right unit to desugar --- as opposed to, say, writing a+  -- function to desugar a pattern --- since a match may desugar to+  -- multiple matches, and on recursive calls we need to know what+  -- term/variable should be bound to the pattern.+  --+  -- A match may desugar to multiple matches for two reasons:+  --+  --   1. Nested patterns 'explode' into a 'telescope' matching one+  --   constructor at a time, for example, 'when t is (x,y,3)'+  --   becomes 'when t is (x,x0) when x0 is (y,x1) when x1 is 3'.+  --   This makes the order of matching explicit and enables lazy+  --   matching without requiring special support from the+  --   interpreter other than WHNF reduction.+  --+  --   2. Matches against arithmetic patterns desugar to a+  --   combination of matching, computation, and boolean checks.+  --   For example, 'when t is (y+1)' becomes 'when t is x0 if x0 >=+  --   1 let y = x0-1'.+  desugarMatch :: Member Fresh r => DTerm -> APattern -> Sem r [DGuard]+  desugarMatch dt (APVar ty x) = mkMatch dt (DPVar ty (coerce x))+  desugarMatch _ (APWild _) = return []+  desugarMatch dt APUnit = mkMatch dt DPUnit+  desugarMatch dt (APBool b) = desugarMatch dt (APInj TyBool (bool L R b) APUnit)+  desugarMatch dt (APNat ty n) = desugarMatch (dtbin TyBool (PrimBOp Eq) dt (DTNat ty n)) (APBool True)+  desugarMatch dt (APChar c) = desugarMatch (dtbin TyBool (PrimBOp Eq) dt (DTChar c)) (APBool True)+  desugarMatch dt (APString s) = desugarMatch dt (APList (TyList TyC) (map APChar s))+  desugarMatch dt (APTup tupTy pat) = desugarTuplePats tupTy dt pat+   where+    desugarTuplePats :: Member Fresh r => Type -> DTerm -> [APattern] -> Sem r [DGuard]+    desugarTuplePats _ _ [] = error "Impossible! desugarTuplePats []"+    desugarTuplePats _ t [p] = desugarMatch t p+    desugarTuplePats ty@(_ :*: ty2) t (p : ps) = do+      (x1, gs1) <- varForPat p+      (x2, gs2) <- case ps of+        [APVar _ px2] -> return (coerce px2, [])+        _ -> do+          x <- fresh (string2Name "x")+          (x,) <$> desugarTuplePats ty2 (dtVar ty2 x) ps       fmap concat . sequence $-        [ mkMatch dt (DPInj ty R y)-        , mkMatch (dtVar unrolledTy y) (DPPair unrolledTy x1 x2)+        [ mkMatch t $ DPPair ty x1 x2         , return gs1         , return gs2         ]--    desugarMatch dt (APList ty []) = desugarMatch dt (APInj ty L APUnit)-    desugarMatch dt (APList ty ps) =-      desugarMatch dt $ foldr (APCons ty) (APList ty []) ps--    -- when dt is (p + t) ==> when dt is x0; let v = t; [if x0 >= v]; when x0-v is p-    desugarMatch dt (APAdd ty _ p t) = arithBinMatch posRestrict (-.) dt ty p t-      where-        posRestrict plusty-          | plusty `elem` [TyN, TyF] = Just (>=.)-          | otherwise                = Nothing--    -- when dt is (p * t) ==> when dt is x0; let v = t; [if v divides x0]; when x0 / v is p-    desugarMatch dt (APMul ty _ p t) = arithBinMatch intRestrict (/.) dt ty p t-      where-        intRestrict plusty-          | plusty `elem` [TyN, TyZ] = Just (flip (|.))-          | otherwise                = Nothing--    -- when dt is (p - t) ==> when dt is x0; let v = t; when x0 + v is p-    desugarMatch dt (APSub ty p t)  = arithBinMatch (const Nothing) (+.) dt ty p t--    -- when dt is (p/q) ==> when $frac(dt) is (p, q)-    desugarMatch dt (APFrac _ p q)-      = desugarMatch-          (dtapp (DTPrim (TyQ :->: TyZ :*: TyN) PrimFrac) dt)-          (APTup (TyZ :*: TyN) [p, q])+    desugarTuplePats ty _ _ =+      error $ "Impossible! desugarTuplePats with non-pair type " ++ show ty+  desugarMatch dt (APInj ty s p) = do+    (x, gs) <- varForPat p+    fmap concat . sequence $+      [ mkMatch dt $ DPInj ty s x+      , return gs+      ]+  desugarMatch dt (APCons ty p1 p2) = do+    y <- fresh (string2Name "y")+    (x1, gs1) <- varForPat p1+    (x2, gs2) <- varForPat p2 -    -- when dt is (-p) ==> when dt is x0; if x0 < 0; when -x0 is p-    desugarMatch dt (APNeg ty p) = do+    let eltTy = getType p1+        unrolledTy = eltTy :*: ty+    fmap concat . sequence $+      [ mkMatch dt (DPInj ty R y)+      , mkMatch (dtVar unrolledTy y) (DPPair unrolledTy x1 x2)+      , return gs1+      , return gs2+      ]+  desugarMatch dt (APList ty []) = desugarMatch dt (APInj ty L APUnit)+  desugarMatch dt (APList ty ps) =+    desugarMatch dt $ foldr (APCons ty) (APList ty []) ps+  -- when dt is (p + t) ==> when dt is x0; let v = t; [if x0 >= v]; when x0-v is p+  desugarMatch dt (APAdd ty _ p t) = arithBinMatch posRestrict (-.) dt ty p t+   where+    posRestrict plusty+      | plusty `elem` [TyN, TyF] = Just (>=.)+      | otherwise = Nothing -      -- when dt is x0-      (x0, g1) <- varFor dt+  -- when dt is (p * t) ==> when dt is x0; let v = t; [if v divides x0]; when x0 / v is p+  desugarMatch dt (APMul ty _ p t) = arithBinMatch intRestrict (/.) dt ty p t+   where+    intRestrict plusty+      | plusty `elem` [TyN, TyZ] = Just (flip (|.))+      | otherwise = Nothing -      -- if x0 < 0-      g2  <- desugarGuard $ AGBool (embed (atVar ty (coerce x0) <. ATNat ty 0))+  -- when dt is (p - t) ==> when dt is x0; let v = t; when x0 + v is p+  desugarMatch dt (APSub ty p t) = arithBinMatch (const Nothing) (+.) dt ty p t+  -- when dt is (p/q) ==> when $frac(dt) is (p, q)+  desugarMatch dt (APFrac _ p q) =+    desugarMatch+      (dtapp (DTPrim (TyQ :->: TyZ :*: TyN) PrimFrac) dt)+      (APTup (TyZ :*: TyN) [p, q])+  -- when dt is (-p) ==> when dt is x0; if x0 < 0; when -x0 is p+  desugarMatch dt (APNeg ty p) = do+    -- when dt is x0+    (x0, g1) <- varFor dt -      -- when -x0 is p-      neg <- desugarTerm $ mkUn ty Neg (atVar ty (coerce x0))-      g3  <- desugarMatch neg p+    -- if x0 < 0+    g2 <- desugarGuard $ AGBool (embed (atVar ty (coerce x0) <. ATNat ty 0)) -      return (g1 ++ g2 ++ g3)+    -- when -x0 is p+    neg <- desugarTerm $ mkUn ty Neg (atVar ty (coerce x0))+    g3 <- desugarMatch neg p -    mkMatch :: Member Fresh r => DTerm -> DPattern -> Sem r [DGuard]-    mkMatch dt dp = return [DGPat (embed dt) dp]+    return (g1 ++ g2 ++ g3) -    varMatch :: Member Fresh r => DTerm -> Name DTerm -> Sem r [DGuard]-    varMatch dt x = mkMatch dt (DPVar (getType dt) x)+  mkMatch :: Member Fresh r => DTerm -> DPattern -> Sem r [DGuard]+  mkMatch dt dp = return [DGPat (embed dt) dp] -    varFor :: Member Fresh r => DTerm -> Sem r (Name DTerm, [DGuard])-    varFor (DTVar _ (QName _ x)) = return (x, [])  -- XXX return a name + provenance??-    varFor dt          = do-      x <- fresh (string2Name "x")-      g <- varMatch dt x-      return (x, g)+  varMatch :: Member Fresh r => DTerm -> Name DTerm -> Sem r [DGuard]+  varMatch dt x = mkMatch dt (DPVar (getType dt) x) -    varForPat :: Member Fresh r => APattern -> Sem r (Name DTerm, [DGuard])-    varForPat (APVar _ x) = return (coerce x, [])-    varForPat p           = do-      x <- fresh (string2Name "px")     -- changing this from x fixed a bug and I don't know why =(-      (x,) <$> desugarMatch (dtVar (getType p) x) p+  varFor :: Member Fresh r => DTerm -> Sem r (Name DTerm, [DGuard])+  varFor (DTVar _ (QName _ x)) = return (x, []) -- XXX return a name + provenance??+  varFor dt = do+    x <- fresh (string2Name "x")+    g <- varMatch dt x+    return (x, g) -    arithBinMatch-      :: Member Fresh r-      => (Type -> Maybe (ATerm -> ATerm -> ATerm))-      -> (ATerm -> ATerm -> ATerm)-      -> DTerm -> Type -> APattern -> ATerm -> Sem r [DGuard]-    arithBinMatch restrict inverse dt ty p t = do-      (x0, g1) <- varFor dt+  varForPat :: Member Fresh r => APattern -> Sem r (Name DTerm, [DGuard])+  varForPat (APVar _ x) = return (coerce x, [])+  varForPat p = do+    x <- fresh (string2Name "px") -- changing this from x fixed a bug and I don't know why =(+    (x,) <$> desugarMatch (dtVar (getType p) x) p -      -- let v = t-      t' <- desugarTerm t-      (v, g2) <- varFor t'+  arithBinMatch ::+    Member Fresh r =>+    (Type -> Maybe (ATerm -> ATerm -> ATerm)) ->+    (ATerm -> ATerm -> ATerm) ->+    DTerm ->+    Type ->+    APattern ->+    ATerm ->+    Sem r [DGuard]+  arithBinMatch restrict inverse dt ty p t = do+    (x0, g1) <- varFor dt -      g3 <- case restrict ty of-        Nothing -> return []+    -- let v = t+    t' <- desugarTerm t+    (v, g2) <- varFor t' -        -- if x0 `cmp` v-        Just cmp ->-          desugarGuard $-            AGBool (embed (atVar ty (coerce x0) `cmp` atVar (getType t) (coerce v)))+    g3 <- case restrict ty of+      Nothing -> return []+      -- if x0 `cmp` v+      Just cmp ->+        desugarGuard $+          AGBool (embed (atVar ty (coerce x0) `cmp` atVar (getType t) (coerce v))) -      -- when x0 `inverse` v is p-      inv <- desugarTerm (atVar ty (coerce x0) `inverse` atVar (getType t) (coerce v))-      g4  <- desugarMatch inv p+    -- when x0 `inverse` v is p+    inv <- desugarTerm (atVar ty (coerce x0) `inverse` atVar (getType t) (coerce v))+    g4 <- desugarMatch inv p -      return (g1 ++ g2 ++ g3 ++ g4)+    return (g1 ++ g2 ++ g3 ++ g4)  -- | Desugar a container literal such as @[1,2,3]@ or @{1,2,3}@. desugarContainer :: Member Fresh r => Type -> Container -> [(ATerm, Maybe ATerm)] -> Maybe (Ellipsis ATerm) -> Sem r DTerm- -- Literal list containers desugar to nested applications of cons. desugarContainer ty ListContainer es Nothing =   foldr (dtbin ty (PrimBOp Cons)) (DTNil ty) <$> mapM (desugarTerm . fst) es- -- A list container with an ellipsis @[x, y, z .. e]@ desugars to an -- application of the primitive 'until' function. desugarContainer ty@(TyList _) ListContainer es (Just (Until t)) =   dtbin ty PrimUntil     <$> desugarTerm t     <*> desugarContainer ty ListContainer es Nothing- -- If desugaring a bag and there are any counts specified, desugar to -- an application of bagFromCounts to a bag of pairs (with a literal -- value of 1 filled in for missing counts as needed). desugarContainer (TyBag eltTy) BagContainer es mell   | any (isJust . snd) es =-    dtapp (DTPrim (TySet (eltTy :*: TyN) :->: TyBag eltTy) PrimC2B)-      <$> desugarContainer (TyBag (eltTy :*: TyN)) BagContainer counts mell--    where-      -- turn e.g.  x # 3, y   into   (x, 3), (y, 1)-      counts = [ (ATTup (eltTy :*: TyN) [t, fromMaybe (ATNat TyN 1) n], Nothing)-               | (t, n) <- es-               ]+      dtapp (DTPrim (TySet (eltTy :*: TyN) :->: TyBag eltTy) PrimC2B)+        <$> desugarContainer (TyBag (eltTy :*: TyN)) BagContainer counts mell+ where+  -- turn e.g.  x # 3, y   into   (x, 3), (y, 1)+  counts =+    [ (ATTup (eltTy :*: TyN) [t, fromMaybe (ATNat TyN 1) n], Nothing)+    | (t, n) <- es+    ]  -- Other containers desugar to an application of the appropriate -- container conversion function to the corresponding desugared list. desugarContainer ty _ es mell =   dtapp (DTPrim (TyList eltTy :->: ty) conv)     <$> desugarContainer (TyList eltTy) ListContainer es mell-  where-    (conv, eltTy) = case ty of-      TyBag e -> (PrimBag, e)-      TySet e -> (PrimSet, e)-      _       -> error $ "Impossible! Non-container type " ++ show ty ++ " in desugarContainer"+ where+  (conv, eltTy) = case ty of+    TyBag e -> (PrimBag, e)+    TySet e -> (PrimSet, e)+    _ -> error $ "Impossible! Non-container type " ++ show ty ++ " in desugarContainer"
src/Disco/Doc.hs view
@@ -1,141 +1,157 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Doc -- Copyright   :  disco team and contributors -- Maintainer  :  byorgey@gmail.com -- -- Built-in documentation.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Doc-       ( primDoc, primReference, otherDoc, otherReference-       ) where+module Disco.Doc (+  primDoc,+  primReference,+  otherDoc,+  otherReference,+) where -import           Data.Map               (Map)-import qualified Data.Map               as M+import Data.Map (Map)+import qualified Data.Map as M -import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Util             ((==>))+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Util ((==>))  -- | A map from some primitives to a short descriptive string, --   to be shown by the :doc command. primDoc :: Map Prim String-primDoc = M.fromList-  [ PrimUOp Neg  ==> "Arithmetic negation."-  , PrimBOp Add  ==> "The sum of two numbers, types, or graphs."-  , PrimBOp Sub  ==> "The difference of two numbers."-  , PrimBOp SSub ==> "The difference of two numbers, with a lower bound of 0."-  , PrimBOp Mul  ==> "The product of two numbers, types, or graphs."-  , PrimBOp Div  ==> "Divide two numbers."-  , PrimBOp IDiv ==> "The integer quotient of two numbers, rounded down."-  , PrimBOp Mod  ==> "a mod b is the remainder when a is divided by b."-  , PrimBOp Exp  ==> "Exponentiation.  a ^ b is a raised to the b power."-  , PrimUOp Fact ==> "n! computes the factorial of n, that is, 1 * 2 * ... * n."-  , PrimFloor    ==> "floor(x) is the largest integer which is <= x."-  , PrimCeil     ==> "ceiling(x) is the smallest integer which is >= x."-  , PrimAbs      ==> "abs(x) is the absolute value of x.  Also written |x|."-  , PrimUOp Not  ==> "Logical negation: not(true) = false and not(false) = true."-  , PrimBOp And  ==> "Logical conjunction (and): true /\\ true = true; otherwise x /\\ y = false."-  , PrimBOp Or   ==> "Logical disjunction (or): false \\/ false = false; otherwise x \\/ y = true."-  , PrimBOp Impl ==> "Logical implication (implies): true -> false = false; otherwise x -> y = true."-  , PrimBOp Iff  ==> "Biconditional (if and only if)."-  , PrimBOp Eq   ==> "Equality test.  x == y is true if x and y are equal."-  , PrimBOp Neq  ==> "Inequality test.  x /= y is true if x and y are unequal."-  , PrimBOp Lt   ==> "Less-than test. x < y is true if x is less than (but not equal to) y."-  , PrimBOp Gt   ==> "Greater-than test. x > y is true if x is greater than (but not equal to) y."-  , PrimBOp Leq  ==> "Less-than-or-equal test. x <= y is true if x is less than or equal to y."-  , PrimBOp Geq  ==> "Greater-than-or-equal test. x >= y is true if x is greater than or equal to y."--  , PrimBOp CartProd ==> "Cartesian product, i.e. the collection of all pairs.  Also works on bags and sets."-  , PrimPower    ==> "Power set, i.e. the set of all subsets.  Also works on bags."-  , PrimBOp Union ==> "Union of two sets (or bags)."-  , PrimBOp Inter ==> "Intersection of two sets (or bags)."-  , PrimBOp Diff  ==> "Difference of two sets (or bags)."-  ]+primDoc =+  M.fromList+    [ PrimUOp Neg ==> "Arithmetic negation."+    , PrimBOp Add ==> "The sum of two numbers, types, or graphs."+    , PrimBOp Sub ==> "The difference of two numbers."+    , PrimBOp SSub ==> "The difference of two numbers, with a lower bound of 0."+    , PrimBOp Mul ==> "The product of two numbers, types, or graphs."+    , PrimBOp Div ==> "Divide two numbers."+    , PrimBOp IDiv ==> "The integer quotient of two numbers, rounded down."+    , PrimBOp Mod ==> "a mod b is the remainder when a is divided by b."+    , PrimBOp Exp ==> "Exponentiation.  a ^ b is a raised to the b power."+    , PrimUOp Fact ==> "n! computes the factorial of n, that is, 1 * 2 * ... * n."+    , PrimFloor ==> "floor(x) is the largest integer which is <= x."+    , PrimCeil ==> "ceiling(x) is the smallest integer which is >= x."+    , PrimAbs ==> "abs(x) is the absolute value of x.  Also written |x|."+    , PrimUOp Not ==> "Logical negation: not(true) = false and not(false) = true."+    , PrimBOp And ==> "Logical conjunction (and): true /\\ true = true; otherwise x /\\ y = false."+    , PrimBOp Or ==> "Logical disjunction (or): false \\/ false = false; otherwise x \\/ y = true."+    , PrimBOp Impl ==> "Logical implication (implies): true -> false = false; otherwise x -> y = true."+    , PrimBOp Iff ==> "Biconditional (if and only if)."+    , PrimBOp Eq ==> "Equality test.  x == y is true if x and y are equal."+    , PrimBOp Neq ==> "Inequality test.  x /= y is true if x and y are unequal."+    , PrimBOp Lt ==> "Less-than test. x < y is true if x is less than (but not equal to) y."+    , PrimBOp Gt ==> "Greater-than test. x > y is true if x is greater than (but not equal to) y."+    , PrimBOp Leq ==> "Less-than-or-equal test. x <= y is true if x is less than or equal to y."+    , PrimBOp Geq ==> "Greater-than-or-equal test. x >= y is true if x is greater than or equal to y."+    , PrimBOp CartProd ==> "Cartesian product, i.e. the collection of all pairs.  Also works on bags and sets."+    , PrimPower ==> "Power set, i.e. the set of all subsets.  Also works on bags."+    , PrimBOp Union ==> "Union of two sets (or bags)."+    , PrimBOp Inter ==> "Intersection of two sets (or bags)."+    , PrimBOp Diff ==> "Difference of two sets (or bags)."+    ]  -- | A map from some primitives to their corresponding page in the --   Disco language reference --   (https://disco-lang.readthedocs.io/en/latest/reference/index.html). primReference :: Map Prim String-primReference = M.fromList-  [ PrimBOp Add  ==> "addition"-  , PrimBOp Sub  ==> "subtraction"-  , PrimBOp SSub ==> "subtraction"-  , PrimBOp Mul  ==> "multiplication"-  , PrimBOp Div  ==> "division"-  , PrimBOp IDiv ==> "integerdiv"-  , PrimBOp Mod  ==> "mod"-  , PrimBOp Exp  ==> "exponentiation"-  , PrimUOp Fact ==> "factorial"-  , PrimFloor    ==> "round"-  , PrimCeil     ==> "round"-  , PrimAbs      ==> "abs"-  , PrimUOp Not  ==> "logic-ops"-  , PrimBOp And  ==> "logic-ops"-  , PrimBOp Or   ==> "logic-ops"-  , PrimBOp Impl ==> "logic-ops"-  , PrimBOp Iff  ==> "logic-ops"-  , PrimBOp CartProd ==> "cp"-  , PrimPower    ==> "power"-  , PrimBOp Union ==> "set-ops"-  , PrimBOp Inter ==> "set-ops"-  , PrimBOp Diff  ==> "set-ops"-  ]+primReference =+  M.fromList+    [ PrimBOp Add ==> "addition"+    , PrimBOp Sub ==> "subtraction"+    , PrimBOp SSub ==> "subtraction"+    , PrimBOp Mul ==> "multiplication"+    , PrimBOp Div ==> "division"+    , PrimBOp IDiv ==> "integerdiv"+    , PrimBOp Mod ==> "mod"+    , PrimBOp Exp ==> "exponentiation"+    , PrimUOp Fact ==> "factorial"+    , PrimFloor ==> "round"+    , PrimCeil ==> "round"+    , PrimAbs ==> "abs"+    , PrimUOp Not ==> "logic-ops"+    , PrimBOp And ==> "logic-ops"+    , PrimBOp Or ==> "logic-ops"+    , PrimBOp Impl ==> "logic-ops"+    , PrimBOp Iff ==> "logic-ops"+    , PrimBOp CartProd ==> "cp"+    , PrimPower ==> "power"+    , PrimBOp Union ==> "set-ops"+    , PrimBOp Inter ==> "set-ops"+    , PrimBOp Diff ==> "set-ops"+    , PrimBOp Eq ==> "compare"+    , PrimBOp Neq ==> "compare"+    , PrimBOp Lt ==> "compare"+    , PrimBOp Gt ==> "compare"+    , PrimBOp Leq ==> "compare"+    , PrimBOp Geq ==> "compare"+    ]  otherDoc :: Map String String-otherDoc = M.fromList-  [ "N"          ==> docN-  , "ℕ"          ==> docN-  , "Nat"        ==> docN-  , "Natural"    ==> docN-  , "Z"          ==> docZ-  , "ℤ"          ==> docZ-  , "Int"        ==> docZ-  , "Integer"    ==> docZ-  , "F"          ==> docF-  , "𝔽"          ==> docF-  , "Frac"       ==> docF-  , "Fractional" ==> docF-  , "Q"          ==> docQ-  , "ℚ"          ==> docQ-  , "Rational"   ==> docQ-  , "Bool"       ==> docB-  , "Boolean"    ==> docB-  , "Prop"       ==> "The type of propositions."-  , "Set"        ==> "The type of finite sets."-  , "|~|"        ==> "Absolute value, or the size of a collection."-  ]-  where-    docN = "The type of natural numbers: 0, 1, 2, ..."-    docZ = "The type of integers: ..., -2, -1, 0, 1, 2, ..."-    docF = "The type of fractional numbers p/q >= 0."-    docQ = "The type of rational numbers p/q."-    docB = "The type of Booleans (true or false)."+otherDoc =+  M.fromList+    [ "N" ==> docN+    , "ℕ" ==> docN+    , "Nat" ==> docN+    , "Natural" ==> docN+    , "Z" ==> docZ+    , "ℤ" ==> docZ+    , "Int" ==> docZ+    , "Integer" ==> docZ+    , "F" ==> docF+    , "𝔽" ==> docF+    , "Frac" ==> docF+    , "Fractional" ==> docF+    , "Q" ==> docQ+    , "ℚ" ==> docQ+    , "Rational" ==> docQ+    , "Bool" ==> docB+    , "Boolean" ==> docB+    , "Unit" ==> "The unit type, i.e. a type with only a single value."+    , "Prop" ==> "The type of propositions."+    , "Set" ==> "The type of finite sets."+    , "|~|" ==> "Absolute value, or the size of a collection."+    , "{?" ==> "{? ... ?} is a case expression, for choosing a result based on conditions."+    ]+ where+  docN = "The type of natural numbers: 0, 1, 2, ..."+  docZ = "The type of integers: ..., -2, -1, 0, 1, 2, ..."+  docF = "The type of fractional numbers p/q >= 0."+  docQ = "The type of rational numbers p/q."+  docB = "The type of Booleans (true or false)."  otherReference :: Map String String-otherReference = M.fromList-  [ "N"          ==> "natural"-  , "ℕ"          ==> "natural"-  , "Nat"        ==> "natural"-  , "Natural"    ==> "natural"-  , "Z"          ==> "integer"-  , "ℤ"          ==> "integer"-  , "Int"        ==> "integer"-  , "Integer"    ==> "integer"-  , "F"          ==> "fraction"-  , "𝔽"          ==> "fraction"-  , "Frac"       ==> "fraction"-  , "Fractional" ==> "fraction"-  , "Q"          ==> "rational"-  , "ℚ"          ==> "rational"-  , "Rational"   ==> "rational"-  , "Bool"       ==> "bool"-  , "Boolean"    ==> "bool"-  , "Prop"       ==> "prop"-  , "Set"        ==> "set"-  , "|~|"        ==> "size"-  ]+otherReference =+  M.fromList+    [ "N" ==> "natural"+    , "ℕ" ==> "natural"+    , "Nat" ==> "natural"+    , "Natural" ==> "natural"+    , "Z" ==> "integer"+    , "ℤ" ==> "integer"+    , "Int" ==> "integer"+    , "Integer" ==> "integer"+    , "F" ==> "fraction"+    , "𝔽" ==> "fraction"+    , "Frac" ==> "fraction"+    , "Fractional" ==> "fraction"+    , "Q" ==> "rational"+    , "ℚ" ==> "rational"+    , "Rational" ==> "rational"+    , "Bool" ==> "bool"+    , "Boolean" ==> "bool"+    , "Unit" ==> "unit"+    , "Prop" ==> "prop"+    , "Set" ==> "set"+    , "|~|" ==> "size"+    , "{?" ==> "case"+    ]
src/Disco/Effects/Counter.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE BlockArguments  #-}+{-# LANGUAGE BlockArguments #-} {-# LANGUAGE TemplateHaskell #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.Counter -- Copyright   :  disco team and contributors@@ -10,30 +13,26 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Polysemy effect for integer counter.---------------------------------------------------------------------------------- module Disco.Effects.Counter where -import           Polysemy-import           Polysemy.State+import Polysemy+import Polysemy.State  data Counter m a where-   -- | Return the next integer in sequence.-  Next  :: Counter m Integer+  Next :: Counter m Integer  makeSem ''Counter  -- | Dispatch a counter effect, starting the counter from the given --   Integer. runCounter' :: Integer -> Sem (Counter ': r) a -> Sem r a-runCounter' i-  = evalState i-  . reinterpret \case+runCounter' i =+  evalState i+    . reinterpret \case       Next -> do         n <- get-        put (n+1)+        put (n + 1)         return n  -- | Dispatch a counter effect, starting the counter from zero.
src/Disco/Effects/Fresh.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE BlockArguments             #-}+{-# LANGUAGE BlockArguments #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TemplateHaskell #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.Fresh -- Copyright   :  disco team and contributors@@ -12,50 +15,47 @@ -- -- Polysemy effect for fresh name generation, compatible with the -- unbound-generics library.---------------------------------------------------------------------------------- module Disco.Effects.Fresh where -import           Disco.Effects.Counter-import           Disco.Names                           (QName, localName)-import           Polysemy-import           Polysemy.ConstraintAbsorber-import qualified Unbound.Generics.LocallyNameless      as U-import           Unbound.Generics.LocallyNameless.Name+import Disco.Effects.Counter+import Disco.Names (QName, localName)+import Polysemy+import Polysemy.ConstraintAbsorber+import qualified Unbound.Generics.LocallyNameless as U+import Unbound.Generics.LocallyNameless.Name  -- | Fresh name generation effect, supporting raw generation of fresh --   names, and opening binders with automatic freshening.  Simply --   increments a global counter every time 'fresh' is called and --   makes a variable with that numeric suffix. data Fresh m a where-  Fresh  :: Name x -> Fresh m (Name x)+  Fresh :: Name x -> Fresh m (Name x)  makeSem ''Fresh  -- | Dispatch the fresh name generation effect, starting at a given --   integer. runFresh' :: Integer -> Sem (Fresh ': r) a -> Sem r a-runFresh' i-  = runCounter' i-  . reinterpret \case+runFresh' i =+  runCounter' i+    . reinterpret \case       Fresh x -> case x of-        Fn s _  -> Fn s <$> next-        nm@Bn{} -> return nm+        Fn s _ -> Fn s <$> next+        nm@Bn {} -> return nm -    -- Above code copied from-    -- https://hackage.haskell.org/package/unbound-generics-0.4.1/docs/src/Unbound.Generics.LocallyNameless.Fresh.html ;-    -- see instance Monad m => Fresh (FreshMT m) .+-- Above code copied from+-- https://hackage.haskell.org/package/unbound-generics-0.4.1/docs/src/Unbound.Generics.LocallyNameless.Fresh.html ;+-- see instance Monad m => Fresh (FreshMT m) . -    -- It turns out to make things much simpler to reimplement the-    -- Fresh effect ourselves in terms of a state effect, since then-    -- we can immediately dispatch it.  The alternative would be to-    -- implement it in terms of (Embed U.FreshM), but then we are-    -- stuck with that constraint.  Given the constraint-absorbing-    -- machinery below, just impementing the 'fresh' effect itself-    -- means we can then reuse other things from unbound-generics that-    -- depend on a Fresh constraint, such as the 'unbind' function-    -- below.+-- It turns out to make things much simpler to reimplement the+-- Fresh effect ourselves in terms of a state effect, since then+-- we can immediately dispatch it.  The alternative would be to+-- implement it in terms of (Embed U.FreshM), but then we are+-- stuck with that constraint.  Given the constraint-absorbing+-- machinery below, just impementing the 'fresh' effect itself+-- means we can then reuse other things from unbound-generics that+-- depend on a Fresh constraint, such as the 'unbind' function+-- below.  -- | Run a computation requiring fresh name generation, beginning with --   0 for the initial freshly generated name.@@ -92,7 +92,7 @@ absorbFresh = absorbWithSem @U.Fresh @Action (FreshDict fresh) (Sub Dict) {-# INLINEABLE absorbFresh #-} -newtype FreshDict m = FreshDict { fresh_ :: forall x. Name x -> m (Name x) }+newtype FreshDict m = FreshDict {fresh_ :: forall x. Name x -> m (Name x)}  -- | Wrapper for a monadic action with phantom type parameter for reflection. --   Locally defined so that the instance we are going to build with reflection@@ -100,8 +100,11 @@ newtype Action m s' a = Action (m a)   deriving (Functor, Applicative, Monad) -instance ( Monad m-         , Reifies s' (FreshDict m)-         ) => U.Fresh (Action m s') where+instance+  ( Monad m+  , Reifies s' (FreshDict m)+  ) =>+  U.Fresh (Action m s')+  where   fresh x = Action $ fresh_ (reflect $ Proxy @s') x   {-# INLINEABLE fresh #-}
src/Disco/Effects/Input.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.Input -- Copyright   :  disco team and contributors@@ -7,20 +10,16 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Utility functions for input effect.-----------------------------------------------------------------------------------module Disco.Effects.Input-  ( module Polysemy.Input-  , inputToState-  )-  where+module Disco.Effects.Input (+  module Polysemy.Input,+  inputToState,+)+where -import           Polysemy-import           Polysemy.Input-import           Polysemy.State+import Polysemy+import Polysemy.Input+import Polysemy.State  -- | Run an input effect in terms of an ambient state effect. inputToState :: forall s r a. Member (State s) r => Sem (Input s ': r) a -> Sem r a-inputToState = interpret (\case { Input -> get @s })-+inputToState = interpret (\case Input -> get @s)
src/Disco/Effects/LFresh.hs view
@@ -1,8 +1,11 @@-{-# LANGUAGE BlockArguments             #-}+{-# LANGUAGE BlockArguments #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TemplateHaskell #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.LFresh -- Copyright   :  disco team and contributors@@ -12,24 +15,21 @@ -- -- Polysemy effect for local fresh name generation, compatible with -- the unbound-generics library.---------------------------------------------------------------------------------- module Disco.Effects.LFresh where -import           Data.Set                              (Set)-import qualified Data.Set                              as S-import           Data.Typeable                         (Typeable)-import           Polysemy-import           Polysemy.ConstraintAbsorber-import           Polysemy.Reader-import qualified Unbound.Generics.LocallyNameless      as U-import           Unbound.Generics.LocallyNameless.Name+import Data.Set (Set)+import qualified Data.Set as S+import Data.Typeable (Typeable)+import Polysemy+import Polysemy.ConstraintAbsorber+import Polysemy.Reader+import qualified Unbound.Generics.LocallyNameless as U+import Unbound.Generics.LocallyNameless.Name  -- | Local fresh name generation effect. data LFresh m a where-  Lfresh    :: Typeable a => Name a -> LFresh m (Name a)-  Avoid     :: [AnyName] -> m a -> LFresh m a+  Lfresh :: Typeable a => Name a -> LFresh m (Name a)+  Avoid :: [AnyName] -> m a -> LFresh m a   GetAvoids :: LFresh m (Set AnyName)  makeSem ''LFresh@@ -40,44 +40,48 @@ runLFresh = runReader S.empty . runLFresh'  runLFresh' :: Sem (LFresh ': r) a -> Sem (Reader (Set AnyName) ': r) a-runLFresh'-  = reinterpretH @_ @(Reader (Set AnyName)) \case-      Lfresh nm -> do-        let s = name2String nm-        used <- ask-        pureT $ head (filter (\x -> not (S.member (AnyName x) used))-                       (map (makeName s) [0..]))-      Avoid names m -> do-        m' <- runT m-        raise (subsume (runLFresh' (local (S.union (S.fromList names)) m')))-      GetAvoids  -> ask >>= pureT+runLFresh' =+  reinterpretH @_ @(Reader (Set AnyName)) \case+    Lfresh nm -> do+      let s = name2String nm+      used <- ask+      pureT $+        head+          ( filter+              (\x -> not (S.member (AnyName x) used))+              (map (makeName s) [0 ..])+          )+    Avoid names m -> do+      m' <- runT m+      raise (subsume (runLFresh' (local (S.union (S.fromList names)) m')))+    GetAvoids -> ask >>= pureT -  -- Much of the above code copied from-  -- https://hackage.haskell.org/package/unbound-generics-0.4.1/docs/src/Unbound.Generics.LocallyNameless.LFresh.html-  -- (see instance Monad m => LFresh (LFreshMT m))+-- Much of the above code copied from+-- https://hackage.haskell.org/package/unbound-generics-0.4.1/docs/src/Unbound.Generics.LocallyNameless.LFresh.html+-- (see instance Monad m => LFresh (LFreshMT m)) -  -- It turns out to make things much simpler to reimplement the-  -- LFresh effect ourselves in terms of a reader effect, since then-  -- we can immediately dispatch it as above.  The alternative would-  -- be to implement it in terms of (Final U.LFreshM) (see the-  -- commented code at the bottom of this file), but then we are stuck-  -- with that constraint.  Given the constraint-absorbing machinery-  -- below, just impementing the 'LFresh' effect itself means we can-  -- then reuse other things from unbound-generics that depend on a-  -- Fresh constraint, such as the 'lunbind' function below.+-- It turns out to make things much simpler to reimplement the+-- LFresh effect ourselves in terms of a reader effect, since then+-- we can immediately dispatch it as above.  The alternative would+-- be to implement it in terms of (Final U.LFreshM) (see the+-- commented code at the bottom of this file), but then we are stuck+-- with that constraint.  Given the constraint-absorbing machinery+-- below, just impementing the 'LFresh' effect itself means we can+-- then reuse other things from unbound-generics that depend on a+-- Fresh constraint, such as the 'lunbind' function below. -  -- NOTE: originally, there was a single function runLFresh which-  -- called reinterpretH and then immediately dispatched the Reader-  -- (Set AnyName) effect.  However, since runLFresh is recursive,-  -- this means that the recursive calls were running with a-  -- completely *separate* Reader effect that started over from the-  -- empty set! This meant that LFresh basically never changed any-  -- names, leading to all sorts of name clashes and crashes.-  ---  -- Instead, we need to organize things as above: runLFresh' is-  -- recursive, and keeps the Reader effect (using 'subsume' to squash-  -- the duplicated Reader effects together).  Then a top-level-  -- runLFresh function finally runs the Reader effect.+-- NOTE: originally, there was a single function runLFresh which+-- called reinterpretH and then immediately dispatched the Reader+-- (Set AnyName) effect.  However, since runLFresh is recursive,+-- this means that the recursive calls were running with a+-- completely *separate* Reader effect that started over from the+-- empty set! This meant that LFresh basically never changed any+-- names, leading to all sorts of name clashes and crashes.+--+-- Instead, we need to organize things as above: runLFresh' is+-- recursive, and keeps the Reader effect (using 'subsume' to squash+-- the duplicated Reader effects together).  Then a top-level+-- runLFresh function finally runs the Reader effect.  -------------------------------------------------- -- Other functions@@ -86,9 +90,11 @@ --   variables, and providing the opened pattern and term to the --   provided continuation.  The bound variables are also added to the --   set of in-scope variables within in the continuation.-lunbind-  :: (Member LFresh r, U.Alpha p, U.Alpha t)-  => U.Bind p t -> ((p,t) -> Sem r c) -> Sem r c+lunbind ::+  (Member LFresh r, U.Alpha p, U.Alpha t) =>+  U.Bind p t ->+  ((p, t) -> Sem r c) ->+  Sem r c lunbind b k = absorbLFresh (U.lunbind b k)  ------------------------------------------------------------@@ -101,8 +107,8 @@ {-# INLINEABLE absorbLFresh #-}  data LFreshDict m = LFreshDict-  { lfresh_    :: forall a. Typeable a => Name a -> m (Name a)-  , avoid_     :: forall a. [AnyName] -> m a -> m a+  { lfresh_ :: forall a. Typeable a => Name a -> m (Name a)+  , avoid_ :: forall a. [AnyName] -> m a -> m a   , getAvoids_ :: m (Set AnyName)   } @@ -112,9 +118,12 @@ newtype Action m s' a = Action (m a)   deriving (Functor, Applicative, Monad) -instance ( Monad m-         , Reifies s' (LFreshDict m)-         ) => U.LFresh (Action m s') where+instance+  ( Monad m+  , Reifies s' (LFreshDict m)+  ) =>+  U.LFresh (Action m s')+  where   lfresh x = Action $ lfresh_ (reflect $ Proxy @s') x   {-# INLINEABLE lfresh #-}   avoid xs (Action m) = Action $ avoid_ (reflect $ Proxy @s') xs m
src/Disco/Effects/Random.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.Random -- Copyright   :  disco team and contributors@@ -7,22 +10,19 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Utility functions for random effect.-----------------------------------------------------------------------------------module Disco.Effects.Random-  ( module Polysemy.Random-  , runGen-  )-  where+module Disco.Effects.Random (+  module Polysemy.Random,+  runGen,+)+where -import           Polysemy-import           Polysemy.Random+import Polysemy+import Polysemy.Random import qualified System.Random.SplitMix as SM-import qualified Test.QuickCheck.Gen    as QC+import qualified Test.QuickCheck.Gen as QC import qualified Test.QuickCheck.Random as QCR -import           Data.Word              (Word64)+import Data.Word (Word64)  -- | Run a QuickCheck generator using a 'Random' effect. runGen :: Member Random r => QC.Gen a -> Sem r a
src/Disco/Effects/State.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE BlockArguments #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.State -- Copyright   :  disco team and contributors@@ -9,25 +12,24 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Utility functions for state effect.-----------------------------------------------------------------------------------module Disco.Effects.State-  ( module Polysemy.State-  , zoom-  , use-  ,(%=),(.=))-  where+module Disco.Effects.State (+  module Polysemy.State,+  zoom,+  use,+  (%=),+  (.=),+)+where -import           Control.Lens   (Getter, Lens', view, (%~), (.~))+import Control.Lens (Getter, Lens', view, (%~), (.~)) -import           Polysemy-import           Polysemy.State+import Polysemy+import Polysemy.State  -- | Use a lens to zoom into a component of a state. zoom :: forall s a r c. Member (State s) r => Lens' s a -> Sem (State a ': r) c -> Sem r c zoom l = interpret \case-  Get   -> view l <$> get+  Get -> view l <$> get   Put a -> modify (l .~ a)  use :: Member (State s) r => Getter s a -> Sem r a
src/Disco/Effects/Store.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE BlockArguments  #-}+{-# LANGUAGE BlockArguments #-} {-# LANGUAGE TemplateHaskell #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Effects.Store -- Copyright   :  disco team and contributors@@ -10,44 +13,40 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Polysemy effect for a memory store with integer keys.---------------------------------------------------------------------------------- module Disco.Effects.Store where -import qualified Data.IntMap.Lazy      as IntMap-import           Data.IntSet           (IntSet)-import qualified Data.IntSet           as IntSet+import qualified Data.IntMap.Lazy as IntMap+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet -import           Disco.Effects.Counter-import           Polysemy-import           Polysemy.State+import Disco.Effects.Counter+import Polysemy+import Polysemy.State  data Store v m a where--  ClearStore  :: Store v m ()-  New         :: v -> Store v m Int+  ClearStore :: Store v m ()+  New :: v -> Store v m Int   LookupStore :: Int -> Store v m (Maybe v)   InsertStore :: Int -> v -> Store v m ()-  MapStore    :: (v -> v) -> Store v m ()+  MapStore :: (v -> v) -> Store v m ()   AssocsStore :: Store v m [(Int, v)]-  KeepKeys    :: IntSet -> Store v m ()+  KeepKeys :: IntSet -> Store v m ()  makeSem ''Store  -- | Dispatch a store effect. runStore :: forall v r a. Sem (Store v ': r) a -> Sem r a-runStore-  = runCounter-  . evalState @(IntMap.IntMap v) IntMap.empty-  . reinterpret2 \case-      ClearStore      -> put IntMap.empty-      New v           -> do+runStore =+  runCounter+    . evalState @(IntMap.IntMap v) IntMap.empty+    . reinterpret2 \case+      ClearStore -> put IntMap.empty+      New v -> do         loc <- fromIntegral <$> next         modify $ IntMap.insert loc v         return loc-      LookupStore k   -> gets (IntMap.lookup k)+      LookupStore k -> gets (IntMap.lookup k)       InsertStore k v -> modify (IntMap.insert k v)-      MapStore f      -> modify (IntMap.map f)-      AssocsStore     -> gets IntMap.assocs-      KeepKeys ks     -> modify (\m -> IntMap.withoutKeys m (IntMap.keysSet m `IntSet.difference` ks))+      MapStore f -> modify (IntMap.map f)+      AssocsStore -> gets IntMap.assocs+      KeepKeys ks -> modify (\m -> IntMap.withoutKeys m (IntMap.keysSet m `IntSet.difference` ks))
src/Disco/Enumerate.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE NondecreasingIndentation #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Enumerate -- Copyright   :  disco team and contributors@@ -9,41 +12,38 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Enumerate values inhabiting Disco types.---------------------------------------------------------------------------------+module Disco.Enumerate (+  ValueEnumeration, -module Disco.Enumerate-       (-         ValueEnumeration-         -- * Base types-         , enumVoid-         , enumUnit-         , enumBool-         , enumN-         , enumZ-         , enumF-         , enumQ-         , enumC+  -- * Base types+  enumVoid,+  enumUnit,+  enumBool,+  enumN,+  enumZ,+  enumF,+  enumQ,+  enumC, -         -- * Containers-         , enumSet-        --  , enumBag-         , enumList+  -- * Containers+  enumSet,+  --  , enumBag+  enumList, -         -- * Any type-         , enumType-         , enumTypes+  -- * Any type+  enumType,+  enumTypes, -         -- * Lifted functions that return lists-         , enumerateType-         , enumerateTypes-       )-       where+  -- * Lifted functions that return lists+  enumerateType,+  enumerateTypes,+)+where  import qualified Data.Enumeration.Invertible as E-import           Disco.AST.Generic           (Side (..))-import           Disco.Types-import           Disco.Value+import Disco.AST.Generic (Side (..))+import Disco.Types+import Disco.Value  type ValueEnumeration = E.IEnumeration Value @@ -58,16 +58,16 @@ -- | Enumerate the values of type @Bool@ as @[false, true]@. enumBool :: ValueEnumeration enumBool = E.mapE toV fromV $ E.finiteList [L, R]-  where-    toV i = VInj i VUnit-    fromV (VInj i VUnit) = i-    fromV _              = error "enumBool.fromV: value isn't a bool"+ where+  toV i = VInj i VUnit+  fromV (VInj i VUnit) = i+  fromV _ = error "enumBool.fromV: value isn't a bool"  -- | Unsafely extract the numeric value of a @Value@ --   (assumed to be a VNum). valToRat :: Value -> Rational valToRat (VNum _ r) = r-valToRat _          = error "valToRat: value isn't a number"+valToRat _ = error "valToRat: value isn't a number"  ratToVal :: Rational -> Value ratToVal = VNum mempty@@ -93,9 +93,9 @@ -- | Enumerate all Unicode characters. enumC :: ValueEnumeration enumC = E.mapE toV fromV (E.boundedEnum @Char)-  where-    toV   = ratToVal . fromIntegral . fromEnum-    fromV = toEnum . floor . valToRat+ where+  toV = ratToVal . fromIntegral . fromEnum+  fromV = toEnum . floor . valToRat  -- | Enumerate all *finite* sets over a certain element type, given an --   enumeration of the elements.  If we think of each finite set as a@@ -103,21 +103,21 @@ --   members, the sets are enumerated in order of the binary strings. enumSet :: ValueEnumeration -> ValueEnumeration enumSet e = E.mapE toV fromV (E.finiteSubsetOf e)-  where-    toV = VBag . map (,1)-    fromV (VBag vs) = map fst vs-    fromV _         = error "enumSet.fromV: value isn't a set"+ where+  toV = VBag . map (,1)+  fromV (VBag vs) = map fst vs+  fromV _ = error "enumSet.fromV: value isn't a set"  -- | Enumerate all *finite* lists over a certain element type, given --   an enumeration of the elements.  It is very difficult to describe --   the order in which the lists are generated. enumList :: ValueEnumeration -> ValueEnumeration enumList e = E.mapE toV fromV (E.listOf e)-  where-    toV = foldr VCons VNil-    fromV (VCons h t) = h : fromV t-    fromV VNil        = []-    fromV _           = error "enumList.fromV: value isn't a list"+ where+  toV = foldr VCons VNil+  fromV (VCons h t) = h : fromV t+  fromV VNil = []+  fromV _ = error "enumList.fromV: value isn't a list"  -- | Enumerate all functions from a finite domain, given enumerations --   for the domain and codomain.@@ -127,60 +127,60 @@     (E.Finite 0, _) -> E.singleton (VFun $ \_ -> error "enumFunction: void function called")     (_, E.Finite 0) -> E.void     (_, E.Finite 1) -> E.singleton (VFun $ \_ -> E.select ys 0)-    _               -> E.mapE toV fromV (E.functionOf xs ys)+    _ -> E.mapE toV fromV (E.functionOf xs ys)+ where+  -- XXX TODO: better error message on functions with an infinite domain -    -- XXX TODO: better error message on functions with an infinite domain-  where-    toV = VFun-    fromV (VFun f) = f-    fromV _        = error "enumFunction.fromV: value isn't a VFun"+  toV = VFun+  fromV (VFun f) = f+  fromV _ = error "enumFunction.fromV: value isn't a VFun"  -- | Enumerate all values of a product type, given enumerations of the --   two component types.  Uses a fair interleaving for infinite --   component types. enumProd :: ValueEnumeration -> ValueEnumeration -> ValueEnumeration enumProd xs ys = E.mapE toV fromV $ (E.><) xs ys-  where-    toV (x, y)        = VPair x y-    fromV (VPair x y) = (x, y)-    fromV _           = error "enumProd.fromV: value isn't a pair"+ where+  toV (x, y) = VPair x y+  fromV (VPair x y) = (x, y)+  fromV _ = error "enumProd.fromV: value isn't a pair"  -- | Enumerate all values of a sum type, given enumerations of the two --   component types. enumSum :: ValueEnumeration -> ValueEnumeration -> ValueEnumeration enumSum xs ys = E.mapE toV fromV $ (E.<+>) xs ys-  where-    toV (Left x)  = VInj L x-    toV (Right y) = VInj R y-    fromV (VInj L x) = Left x-    fromV (VInj R y) = Right y-    fromV _          = error "enumSum.fromV: value isn't a sum"+ where+  toV (Left x) = VInj L x+  toV (Right y) = VInj R y+  fromV (VInj L x) = Left x+  fromV (VInj R y) = Right y+  fromV _ = error "enumSum.fromV: value isn't a sum"  -- | Enumerate the values of a given type. enumType :: Type -> ValueEnumeration-enumType TyVoid     = enumVoid-enumType TyUnit     = enumUnit-enumType TyBool     = enumBool-enumType TyN        = enumN-enumType TyZ        = enumZ-enumType TyF        = enumF-enumType TyQ        = enumQ-enumType TyC        = enumC-enumType (TySet  t) = enumSet (enumType t)+enumType TyVoid = enumVoid+enumType TyUnit = enumUnit+enumType TyBool = enumBool+enumType TyN = enumN+enumType TyZ = enumZ+enumType TyF = enumF+enumType TyQ = enumQ+enumType TyC = enumC+enumType (TySet t) = enumSet (enumType t) enumType (TyList t) = enumList (enumType t)-enumType (a :*: b)  = enumProd (enumType a) (enumType b)-enumType (a :+: b)  = enumSum (enumType a) (enumType b)+enumType (a :*: b) = enumProd (enumType a) (enumType b)+enumType (a :+: b) = enumSum (enumType a) (enumType b) enumType (a :->: b) = enumFunction (enumType a) (enumType b)-enumType ty         = error $ "enumType: can't enumerate " ++ show ty+enumType ty = error $ "enumType: can't enumerate " ++ show ty  -- | Enumerate a finite product of types. enumTypes :: [Type] -> E.IEnumeration [Value]-enumTypes []     = E.singleton []-enumTypes (t:ts) = E.mapE toL fromL $ (E.><) (enumType t) (enumTypes ts)-  where-    toL (x, xs)  = x:xs-    fromL (x:xs) = (x, xs)-    fromL []     = error "enumTypes.fromL: empty list not in enumeration range"+enumTypes [] = E.singleton []+enumTypes (t : ts) = E.mapE toL fromL $ (E.><) (enumType t) (enumTypes ts)+ where+  toL (x, xs) = x : xs+  fromL (x : xs) = (x, xs)+  fromL [] = error "enumTypes.fromL: empty list not in enumeration range"  -- | Produce an actual list of the values of a type. enumerateType :: Type -> [Value]
src/Disco/Error.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StandaloneDeriving #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Error -- Copyright   :  disco team and contributors@@ -11,323 +14,315 @@ -- -- Type for collecting all potential Disco errors at the top level, -- and a type for runtime errors.-----------------------------------------------------------------------------------module Disco.Error (DiscoError(..), EvalError(..), panic, outputDiscoErrors) where+module Disco.Error (DiscoError (..), EvalError (..), panic, outputDiscoErrors) where -import           Prelude                          hiding ((<>))+import Prelude hiding ((<>)) -import           Text.Megaparsec                  (ParseErrorBundle,-                                                   errorBundlePretty)-import           Unbound.Generics.LocallyNameless (Name)+import Text.Megaparsec (+  ParseErrorBundle,+  errorBundlePretty,+ )+import Unbound.Generics.LocallyNameless (Name) -import           Disco.Effects.LFresh-import           Polysemy-import           Polysemy.Error-import           Polysemy.Output-import           Polysemy.Reader+import Disco.Effects.LFresh+import Polysemy+import Polysemy.Error+import Polysemy.Output+import Polysemy.Reader -import           Disco.Messages-import           Disco.Names                      (ModuleName, QName)-import           Disco.Parser                     (DiscoParseError)-import           Disco.Pretty-import           Disco.Typecheck.Solve-import           Disco.Typecheck.Util             (LocTCError (..),-                                                   TCError (..))-import           Disco.Types-import           Disco.Types.Qualifiers+import Disco.Messages+import Disco.Names (ModuleName, QName)+import Disco.Parser (DiscoParseError)+import Disco.Pretty+import Disco.Typecheck.Solve+import Disco.Typecheck.Util (+  LocTCError (..),+  TCError (..),+ )+import Disco.Types+import Disco.Types.Qualifiers  -- | Top-level error type for Disco. data DiscoError where-   -- | Module not found.   ModuleNotFound :: String -> DiscoError-   -- | Cyclic import encountered.   CyclicImport :: [ModuleName] -> DiscoError-   -- | Error encountered during typechecking.   TypeCheckErr :: LocTCError -> DiscoError-   -- | Error encountered during parsing.   ParseErr :: ParseErrorBundle String DiscoParseError -> DiscoError-   -- | Error encountered at runtime.   EvalErr :: EvalError -> DiscoError-   -- | Something that shouldn't happen; indicates the presence of a   --   bug.-  Panic         :: String    -> DiscoError--  deriving Show+  Panic :: String -> DiscoError+  deriving (Show)  -- | Errors that can be generated at runtime. data EvalError where-   -- | An unbound name was encountered.-  UnboundError  :: QName core  -> EvalError-+  UnboundError :: QName core -> EvalError   -- | An unbound name that really shouldn't happen, coming from some   --   kind of internal name generation scheme.-  UnboundPanic  :: Name core   -> EvalError-+  UnboundPanic :: Name core -> EvalError   -- | Division by zero.-  DivByZero     ::              EvalError-+  DivByZero :: EvalError   -- | Overflow, e.g. (2^66)!-  Overflow      ::              EvalError-+  Overflow :: EvalError   -- | Non-exhaustive case analysis.-  NonExhaustive ::              EvalError-+  NonExhaustive :: EvalError   -- | Infinite loop detected via black hole.-  InfiniteLoop  ::              EvalError-+  InfiniteLoop :: EvalError   -- | User-generated crash.-  Crash         :: String    -> EvalError+  Crash :: String -> EvalError  deriving instance Show EvalError  panic :: Member (Error DiscoError) r => String -> Sem r a panic = throw . Panic -outputDiscoErrors :: Member (Output Message) r => Sem (Error DiscoError ': r) () -> Sem r ()+outputDiscoErrors :: Member (Output (Message ann)) r => Sem (Error DiscoError ': r) () -> Sem r () outputDiscoErrors m = do   e <- runError m   either (err . pretty') return e  instance Pretty DiscoError where   pretty = \case-    ModuleNotFound m  -> "Error: couldn't find a module named '" <> text m <> "'."-    CyclicImport ms   -> cyclicImportError ms+    ModuleNotFound m -> "Error: couldn't find a module named '" <> text m <> "'."+    CyclicImport ms -> cyclicImportError ms     TypeCheckErr (LocTCError Nothing te) -> prettyTCError te     TypeCheckErr (LocTCError (Just n) te) ->-      vcat+      nest 2 $ vcat         [ "While checking " <> pretty' n <> ":"-        , nest 2 $ prettyTCError te+        , prettyTCError te         ]-    ParseErr pe       -> text (errorBundlePretty pe)-    EvalErr ee        -> prettyEvalError ee-    Panic s           ->+    ParseErr pe -> text (errorBundlePretty pe)+    EvalErr ee -> prettyEvalError ee+    Panic s ->       vcat         [ "Bug! " <> text s         , "Please report this as a bug at https://github.com/disco-lang/disco/issues/ ."         ] -rtd :: String -> Sem r Doc+rtd :: String -> Sem r (Doc ann) rtd page = "https://disco-lang.readthedocs.io/en/latest/reference/" <> text page <> ".html" -issue :: Int -> Sem r Doc+issue :: Int -> Sem r (Doc ann) issue n = "See https://github.com/disco-lang/disco/issues/" <> text (show n) -cyclicImportError-  :: Members '[Reader PA, LFresh] r-  => [ModuleName] -> Sem r Doc+cyclicImportError ::+  Members '[Reader PA, LFresh] r =>+  [ModuleName] ->+  Sem r (Doc ann) cyclicImportError ms =-  vcat+  nest 2 $ vcat     [ "Error: module imports form a cycle:"-    , nest 2 $ intercalate " ->" (map pretty ms)+    , intercalate " ->" (map pretty ms)     ] -prettyEvalError :: Members '[Reader PA, LFresh] r => EvalError -> Sem r Doc+prettyEvalError :: Members '[Reader PA, LFresh] r => EvalError -> Sem r (Doc ann) prettyEvalError = \case-   UnboundPanic x ->-     ("Bug! No variable found named" <+> pretty' x <> ".")-     $+$-     "Please report this as a bug at https://github.com/disco-lang/disco/issues/ ."-   UnboundError x -> "Error: encountered undefined name" <+> pretty' x <> ". Maybe you haven't defined it yet?"-   DivByZero      -> "Error: division by zero."-   Overflow       -> "Error: that number would not even fit in the universe!"-   NonExhaustive  -> "Error: value did not match any of the branches in a case expression."-   InfiniteLoop   -> "Error: infinite loop detected!"-   Crash s        -> "User crash:" <+> text s+  UnboundPanic x ->+    ("Bug! No variable found named" <+> pretty' x <> ".")+      $+$ "Please report this as a bug at https://github.com/disco-lang/disco/issues/ ."+  UnboundError x -> "Error: encountered undefined name" <+> pretty' x <> ". Maybe you haven't defined it yet?"+  DivByZero -> "Error: division by zero."+  Overflow -> "Error: that number would not even fit in the universe!"+  NonExhaustive -> "Error: value did not match any of the branches in a case expression."+  InfiniteLoop -> "Error: infinite loop detected!"+  Crash s -> "User crash:" <+> text s  -- [X] Step 1: nice error messages, make sure all are tested -- [ ] Step 2: link to wiki/website with more info on errors! -- [ ] Step 3: improve error messages according to notes below -- [ ] Step 4: get it to return multiple error messages -- [ ] Step 5: save parse locations, display with errors-prettyTCError :: Members '[Reader PA, LFresh] r => TCError -> Sem r Doc+prettyTCError :: Members '[Reader PA, LFresh] r => TCError -> Sem r (Doc ann) prettyTCError = \case-   -- XXX include some potential misspellings along with Unbound   --   see https://github.com/disco-lang/disco/issues/180-  Unbound x      -> vcat-    [ "Error: there is nothing named" <+> pretty' x <> "."-    , rtd "unbound"-    ]--  Ambiguous x ms -> vcat-    [ "Error: the name" <+> pretty' x <+> "is ambiguous. It could refer to:"-    , nest 2 (vcat . map (\m -> pretty' m <> "." <> pretty' x) $ ms)-    , rtd "ambiguous"-    ]--  NoType x -> vcat-    [ "Error: the definition of" <+> pretty' x <+> "must have an accompanying type signature."-    , "Try writing something like '" <> pretty' x <+> ": Int' (or whatever the type of"-      <+> pretty' x <+> "should be) first."-    , rtd "missingtype"-    ]--  NotCon c t ty -> vcat-    [ "Error: the expression"-    , nest 2 $ pretty' t-    , "must have both a" <+> conWord c <+> "type and also the incompatible type"-    , nest 2 $ pretty' ty <> "."-    , rtd "notcon"-    ]--  EmptyCase -> vcat-    [ "Error: empty case expressions {? ?} are not allowed."-    , rtd "empty-case"-    ]--  PatternType c pat ty -> vcat-    [ "Error: the pattern"-    , nest 2 $ pretty' pat-    , "is supposed to have type"-    , nest 2 $ pretty' ty <> ","-    , "but instead it has a" <+> conWord c <+> "type."-    , rtd "pattern-type"-    ]--  DuplicateDecls x -> vcat-    [ "Error: duplicate type signature for" <+> pretty' x <> "."-    , rtd "dup-sig"-    ]--  DuplicateDefns x -> vcat-    [ "Error: duplicate definition for" <+> pretty' x <> "."-    , rtd "dup-def"-    ]--  DuplicateTyDefns s -> vcat-    [ "Error: duplicate definition for type" <+> text s <> "."-    , rtd "dup-tydef"-    ]-+  Unbound x ->+    vcat+      [ "Error: there is nothing named" <+> pretty' x <> "."+      , rtd "unbound"+      ]+  Ambiguous x ms ->+    vcat+      [ "Error: the name" <+> pretty' x <+> "is ambiguous. It could refer to:"+      , indent 2 . vcat . map (\m -> pretty' m <> "." <> pretty' x) $ ms+      , rtd "ambiguous"+      ]+  NoType x ->+    vcat+      [ "Error: the definition of" <+> pretty' x <+> "must have an accompanying type signature."+      , "Try writing something like '"+          <> pretty' x+          <+> ": Int' (or whatever the type of"+          <+> pretty' x+          <+> "should be) first."+      , rtd "missingtype"+      ]+  NotCon c t ty ->+    vcat+      [ "Error: the expression"+      , indent 2 $ pretty' t+      , "must have both a" <+> conWord c <+> "type and also the incompatible type"+      , indent 2 $ pretty' ty <> "."+      , rtd "notcon"+      ]+  EmptyCase ->+    vcat+      [ "Error: empty case expressions {? ?} are not allowed."+      , rtd "empty-case"+      ]+  PatternType c pat ty ->+    vcat+      [ "Error: the pattern"+      , indent 2 $ pretty' pat+      , "is supposed to have type"+      , indent 2 $ pretty' ty <> ","+      , "but instead it has a" <+> conWord c <+> "type."+      , rtd "pattern-type"+      ]+  DuplicateDecls x ->+    vcat+      [ "Error: duplicate type signature for" <+> pretty' x <> "."+      , rtd "dup-sig"+      ]+  DuplicateDefns x ->+    vcat+      [ "Error: duplicate definition for" <+> pretty' x <> "."+      , rtd "dup-def"+      ]+  DuplicateTyDefns s ->+    vcat+      [ "Error: duplicate definition for type" <+> text s <> "."+      , rtd "dup-tydef"+      ]   -- XXX include all types involved in the cycle.-  CyclicTyDef s -> vcat-    [ "Error: cyclic type definition for" <+> text s <> "."-    , rtd "cyc-ty"-    ]-+  CyclicTyDef s ->+    vcat+      [ "Error: cyclic type definition for" <+> text s <> "."+      , rtd "cyc-ty"+      ]   -- XXX lots more info!  & Split into several different errors.-  NumPatterns -> vcat-    [ "Error: number of arguments does not match."-    , rtd "num-args"-    ]-+  NumPatterns ->+    vcat+      [ "Error: number of arguments does not match."+      , rtd "num-args"+      ]+  NonlinearPattern p x ->+    vcat+      [ "Error: pattern" <+> pretty' p <+> "contains duplicate variable" <+> pretty' x <> "."+      , rtd "nonlinear"+      ]   NoSearch ty ->     vcat-    [ "Error: the type"-    , nest 2 $ pretty' ty-    , "is not searchable (i.e. it cannot be used in a forall)."-    , rtd "no-search"-    ]-+      [ "Error: the type"+      , indent 2 $ pretty' ty+      , "is not searchable (i.e. it cannot be used in a forall)."+      , rtd "no-search"+      ]   Unsolvable solveErr -> prettySolveError solveErr-   -- XXX maybe include close edit-distance alternatives?-  NotTyDef s -> vcat-    [ "Error: there is no built-in or user-defined type named '" <> text s <> "'."-    , rtd "no-tydef"-    ]--  NoTWild -> vcat-    [ "Error: wildcards (_) are not allowed in expressions."-    , rtd "wildcard-expr"-    ]-+  NotTyDef s ->+    vcat+      [ "Error: there is no built-in or user-defined type named '" <> text s <> "'."+      , rtd "no-tydef"+      ]+  NoTWild ->+    vcat+      [ "Error: wildcards (_) are not allowed in expressions."+      , rtd "wildcard-expr"+      ]   -- XXX say how many are expected, how many there were, what the actual arguments were?   -- XXX distinguish between built-in and user-supplied type constructors in the error   --     message?-  NotEnoughArgs con -> vcat-    [ "Error: not enough arguments for the type '" <> pretty' con <> "'."-    , rtd "num-args-type"-    ]--  TooManyArgs con -> vcat-    [ "Error: too many arguments for the type '" <> pretty' con <> "'."-    , rtd "num-args-type"-    ]-+  NotEnoughArgs con ->+    vcat+      [ "Error: not enough arguments for the type '" <> pretty' con <> "'."+      , rtd "num-args-type"+      ]+  TooManyArgs con ->+    vcat+      [ "Error: too many arguments for the type '" <> pretty' con <> "'."+      , rtd "num-args-type"+      ]   -- XXX Mention the definition in which it was found, suggest adding the variable   --     as a parameter-  UnboundTyVar v -> vcat-    [ "Error: Unknown type variable '" <> pretty' v <> "'."-    , rtd "unbound-tyvar"-    ]--  NoPolyRec s ss tys -> vcat-    [ "Error: in the definition of " <> text s <> parens (intercalate "," (map text ss)) <> ": recursive occurrences of" <+> text s <+> "may only have type variables as arguments."-    , nest 2 (-        text s <> parens (intercalate "," (map pretty' tys)) <+> "does not follow this rule."-      )-    , rtd "no-poly-rec"-    ]-+  UnboundTyVar v ->+    vcat+      [ "Error: Unknown type variable '" <> pretty' v <> "'."+      , rtd "unbound-tyvar"+      ]+  NoPolyRec s ss tys ->+    vcat+      [ "Error: in the definition of " <> text s <> parens (intercalate "," (map text ss)) <> ": recursive occurrences of" <+> text s <+> "may only have type variables as arguments."+      , indent+          2+          ( text s <> parens (intercalate "," (map pretty' tys)) <+> "does not follow this rule."+          )+      , rtd "no-poly-rec"+      ]   NoError -> empty -conWord :: Con -> Sem r Doc+conWord :: Con -> Sem r (Doc ann) conWord = \case-  CArr         -> "function"-  CProd        -> "product"-  CSum         -> "sum"-  CSet         -> "set"-  CBag         -> "bag"-  CList        -> "list"+  CArr -> "function"+  CProd -> "pair"+  CSum -> "sum"+  CSet -> "set"+  CBag -> "bag"+  CList -> "list"   CContainer _ -> "container"-  CMap         -> "map"-  CGraph       -> "graph"-  CUser s      -> text s+  CMap -> "map"+  CGraph -> "graph"+  CUser s -> text s -prettySolveError :: Members '[Reader PA, LFresh] r => SolveError -> Sem r Doc+prettySolveError :: Members '[Reader PA, LFresh] r => SolveError -> Sem r (Doc ann) prettySolveError = \case-   -- XXX say which types!-  NoWeakUnifier -> vcat-    [ "Error: the shape of two types does not match."-    , rtd "shape-mismatch"-    ]-+  NoWeakUnifier ->+    vcat+      [ "Error: the shape of two types does not match."+      , rtd "shape-mismatch"+      ]   -- XXX say more!  XXX HIGHEST PRIORITY!-  NoUnify       -> vcat-    [ "Error: typechecking failed."-    , rtd "typecheck-fail"-    ]--  UnqualBase q b -> vcat-    [ "Error: values of type" <+> pretty' b <+> qualPhrase False q <> "."-    , rtd "not-qual"-    ]--  Unqual q ty -> vcat-    [ "Error: values of type" <+> pretty' ty <+> qualPhrase False q <> "."-    , rtd "not-qual"-    ]--  QualSkolem q a -> vcat-    [ "Error: type variable" <+> pretty' a <+> "represents any type, so we cannot assume values of that type"-    , nest 2 (qualPhrase True q) <> "."-    , rtd "qual-skolem"-    ]+  NoUnify ->+    vcat+      [ "Error: typechecking failed."+      , rtd "typecheck-fail"+      ]+  UnqualBase q b ->+    vcat+      [ "Error: values of type" <+> pretty' b <+> qualPhrase False q <> "."+      , rtd "not-qual"+      ]+  Unqual q ty ->+    vcat+      [ "Error: values of type" <+> pretty' ty <+> qualPhrase False q <> "."+      , rtd "not-qual"+      ]+  QualSkolem q a ->+    vcat+      [ "Error: type variable" <+> pretty' a <+> "represents any type, so we cannot assume values of that type"+      , indent 2 (qualPhrase True q) <> "."+      , rtd "qual-skolem"+      ] -qualPhrase :: Bool -> Qualifier -> Sem r Doc+qualPhrase :: Bool -> Qualifier -> Sem r (Doc ann) qualPhrase b q   | q `elem` [QBool, QBasic, QSimple] = "are" <+> (if b then empty else "not") <+> qualAction q   | otherwise = "can" <> (if b then empty else "not") <+> "be" <+> qualAction q -qualAction :: Qualifier -> Sem r Doc+qualAction :: Qualifier -> Sem r (Doc ann) qualAction = \case-  QNum    -> "added and multiplied"-  QSub    -> "subtracted"-  QDiv    -> "divided"-  QCmp    -> "compared"-  QEnum   -> "enumerated"-  QBool   -> "boolean"-  QBasic  -> "basic"+  QNum -> "added and multiplied"+  QSub -> "subtracted"+  QDiv -> "divided"+  QCmp -> "compared"+  QEnum -> "enumerated"+  QBool -> "boolean"+  QBasic -> "basic"   QSimple -> "simple"-
src/Disco/Eval.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE OverloadedStrings    #-}-{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeSynonymInstances #-} ------------------------------------------------------------------------------ -- | -- Module      :  Disco.Eval -- Copyright   :  disco team and contributors@@ -11,86 +10,91 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Top-level evaluation utilities.--------------------------------------------------------------------------------module Disco.Eval-       (-         -- * Effects--         EvalEffects-       , DiscoEffects--         -- * Top-level info record and associated lenses--       , DiscoConfig, initDiscoConfig, debugMode-       , TopInfo-       , replModInfo, topEnv, topModMap, lastFile, discoConfig--         -- * Running things--       , runDisco-       , runTCM-       , inputTopEnv-       , parseDiscoModule-       , typecheckTop+module Disco.Eval (+  -- * Effects+  EvalEffects,+  DiscoEffects, -         -- * Loading modules+  -- * Top-level info record and associated lenses+  DiscoConfig,+  initDiscoConfig,+  debugMode,+  TopInfo,+  replModInfo,+  topEnv,+  topModMap,+  lastFile,+  discoConfig, -       , loadDiscoModule-       , loadParsedDiscoModule-       , loadFile-       , addToREPLModule-       , setREPLModule-       , loadDefsFrom-       , loadDef+  -- * Running things+  runDisco,+  runTCM,+  inputTopEnv,+  parseDiscoModule,+  typecheckTop, -       )-       where+  -- * Loading modules+  loadDiscoModule,+  loadParsedDiscoModule,+  loadFile,+  addToREPLModule,+  setREPLModule,+  loadDefsFrom,+  loadDef,+)+where -import           Control.Arrow            ((&&&))-import           Control.Exception        (SomeException, handle)-import           Control.Lens             (makeLenses, toListOf, view, (%~),-                                           (.~), (<>~), (^.))-import           Control.Monad            (unless, void, when)-import           Control.Monad.IO.Class   (liftIO)-import           Data.Bifunctor-import           Data.Map                 (Map)-import qualified Data.Map                 as M-import qualified Data.Set                 as S-import           Prelude-import           System.FilePath          ((-<.>))+import Control.Arrow ((&&&))+import Control.Exception (SomeException, handle)+import Control.Lens (+  makeLenses,+  toListOf,+  view,+  (%~),+  (.~),+  (<>~),+  (^.),+ )+import Control.Monad (unless, void, when)+import Control.Monad.IO.Class (liftIO)+import Data.Bifunctor+import Data.Map (Map)+import qualified Data.Map as M+import qualified Data.Set as S+import System.FilePath ((-<.>))+import Prelude  import qualified System.Console.Haskeline as H -import           Disco.Effects.Fresh-import           Disco.Effects.Input-import           Disco.Effects.LFresh-import           Disco.Effects.State-import           Polysemy-import           Polysemy.Embed-import           Polysemy.Error-import           Polysemy.Fail-import           Polysemy.Output-import           Polysemy.Random-import           Polysemy.Reader+import Disco.Effects.Fresh+import Disco.Effects.Input+import Disco.Effects.LFresh+import Disco.Effects.State+import Polysemy+import Polysemy.Embed+import Polysemy.Error+import Polysemy.Fail+import Polysemy.Output+import Polysemy.Random+import Polysemy.Reader -import           Disco.AST.Core-import           Disco.AST.Surface-import           Disco.Compile            (compileDefns)-import           Disco.Context            as Ctx-import           Disco.Error-import           Disco.Extensions-import           Disco.Interpret.CESK-import           Disco.Messages-import           Disco.Module-import           Disco.Names-import           Disco.Parser-import           Disco.Pretty             hiding ((<>))-import qualified Disco.Pretty             as Pretty-import           Disco.Typecheck          (checkModule)-import           Disco.Typecheck.Util-import           Disco.Types-import           Disco.Value+import Disco.AST.Core+import Disco.AST.Surface+import Disco.Compile (compileDefns)+import Disco.Context as Ctx+import Disco.Error+import Disco.Extensions+import Disco.Interpret.CESK+import Disco.Messages+import Disco.Module+import Disco.Names+import Disco.Parser+import Disco.Pretty hiding ((<>))+import qualified Disco.Pretty as Pretty+import Disco.Typecheck (checkModule)+import Disco.Typecheck.Util+import Disco.Types+import Disco.Value  ------------------------------------------------------------ -- Configuation options@@ -103,9 +107,10 @@ makeLenses ''DiscoConfig  initDiscoConfig :: DiscoConfig-initDiscoConfig = DiscoConfig-  { _debugMode = False-  }+initDiscoConfig =+  DiscoConfig+    { _debugMode = False+    }  ------------------------------------------------------------ -- Top level info record@@ -114,32 +119,29 @@ -- | A record of information about the current top-level environment. data TopInfo = TopInfo   { _replModInfo :: ModuleInfo-    -- ^ Info about the top-level module collecting stuff entered at-    --   the REPL.--  , _topEnv      :: Env-    -- ^ Top-level environment mapping names to values.  Set by-    --   'loadDefs'.--  , _topModMap   :: Map ModuleName ModuleInfo-    -- ^ Mapping from loaded module names to their 'ModuleInfo'-    --   records.--  , _lastFile    :: Maybe FilePath-    -- ^ The most recent file which was :loaded by the user.-+  -- ^ Info about the top-level module collecting stuff entered at+  --   the REPL.+  , _topEnv :: Env+  -- ^ Top-level environment mapping names to values.  Set by+  --   'loadDefs'.+  , _topModMap :: Map ModuleName ModuleInfo+  -- ^ Mapping from loaded module names to their 'ModuleInfo'+  --   records.+  , _lastFile :: Maybe FilePath+  -- ^ The most recent file which was :loaded by the user.   , _discoConfig :: DiscoConfig   }  -- | The initial (empty) record of top-level info. initTopInfo :: DiscoConfig -> TopInfo-initTopInfo cfg = TopInfo-  { _replModInfo = emptyModuleInfo-  , _topEnv      = emptyCtx-  , _topModMap   = M.empty-  , _lastFile    = Nothing-  , _discoConfig = cfg-  }+initTopInfo cfg =+  TopInfo+    { _replModInfo = emptyModuleInfo+    , _topEnv = emptyCtx+    , _topModMap = M.empty+    , _lastFile = Nothing+    , _discoConfig = cfg+    }  makeLenses ''TopInfo @@ -158,13 +160,14 @@ -- However, just manually implementing it here seems easier.  -- | Effects needed at the top level.-type TopEffects = '[Error DiscoError, State TopInfo, Output Message, Embed IO, Final (H.InputT IO)]+type TopEffects = '[Error DiscoError, State TopInfo, Output (Message ()), Embed IO, Final (H.InputT IO)]  -- | Effects needed for evaluation.-type EvalEffects = [Error EvalError, Random, LFresh, Output Message, State Mem]-  -- XXX write about order.-  -- memory, counter etc. should not be reset by errors.+type EvalEffects = [Error EvalError, Random, LFresh, Output (Message ()), State Mem] +-- XXX write about order.+-- memory, counter etc. should not be reset by errors.+ -- | All effects needed for the top level + evaluation. type DiscoEffects = AppendEffects EvalEffects TopEffects @@ -188,21 +191,21 @@     . runFinal @(H.InputT IO)     . embedToFinal     . runEmbedded @_ @(H.InputT IO) liftIO-    . runOutputSem (handleMsg msgFilter)    -- Handle Output Message via printing to console-    . stateToIO (initTopInfo cfg)           -- Run State TopInfo via an IORef-    . inputToState                          -- Dispatch Input TopInfo effect via State effect-    . runState emptyMem                     -- Start with empty memory-    . outputDiscoErrors                     -- Output any top-level errors-    . runLFresh                             -- Generate locally fresh names-    . runRandomIO                           -- Generate randomness via IO-    . mapError EvalErr                      -- Embed runtime errors into top-level error type-    . failToError Panic                     -- Turn pattern-match failures into a Panic error-    . runReader emptyCtx                    -- Keep track of current Env+    . runOutputSem (handleMsg msgFilter) -- Handle Output Message via printing to console+    . stateToIO (initTopInfo cfg) -- Run State TopInfo via an IORef+    . inputToState -- Dispatch Input TopInfo effect via State effect+    . runState emptyMem -- Start with empty memory+    . outputDiscoErrors -- Output any top-level errors+    . runLFresh -- Generate locally fresh names+    . runRandomIO -- Generate randomness via IO+    . mapError EvalErr -- Embed runtime errors into top-level error type+    . failToError Panic -- Turn pattern-match failures into a Panic error+    . runReader emptyCtx -- Keep track of current Env     $ m-  where-    msgFilter-      | cfg ^. debugMode = const True-      | otherwise        = (/= Debug) . view messageType+ where+  msgFilter+    | cfg ^. debugMode = const True+    | otherwise = (/= Debug) . view messageType  ------------------------------------------------------------ -- Environment utilities@@ -252,7 +255,7 @@  -- | A variant of 'runTCM' that requires only a 'TCError' instead --   of a 'LocTCError'.-runTCM'  ::+runTCM' ::   Member (Error DiscoError) r =>   TyCtx ->   TyDefCtx ->@@ -266,12 +269,12 @@  -- | Run a typechecking computation in the context of the top-level --   REPL module, re-throwing a wrapped error if it fails.-typecheckTop-  :: Members '[Input TopInfo, Error DiscoError] r-  => Sem (Reader TyCtx ': Reader TyDefCtx ': Fresh ': Error TCError ': r) a-  -> Sem r a+typecheckTop ::+  Members '[Input TopInfo, Error DiscoError] r =>+  Sem (Reader TyCtx ': Reader TyDefCtx ': Fresh ': Error TCError ': r) a ->+  Sem r a typecheckTop tcm = do-  tyctx  <- inputs (view (replModInfo . miTys))+  tyctx <- inputs (view (replModInfo . miTys))   imptyctx <- inputs (toListOf (replModInfo . miImports . traverse . miTys))   tydefs <- inputs (view (replModInfo . miTydefs))   imptydefs <- inputs (toListOf (replModInfo . miImports . traverse . miTydefs))@@ -288,9 +291,12 @@ -- --   The 'Resolver' argument specifies where to look for imported --   modules.-loadDiscoModule-  :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r-  => Bool -> Resolver -> FilePath -> Sem r ModuleInfo+loadDiscoModule ::+  Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r =>+  Bool ->+  Resolver ->+  FilePath ->+  Sem r ModuleInfo loadDiscoModule quiet resolver =   loadDiscoModule' quiet resolver [] @@ -299,9 +305,13 @@ --   a context that includes the current top-level context (unlike a --   module loaded from disk).  Used for e.g. blocks/modules entered --   at the REPL prompt.-loadParsedDiscoModule-  :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r-  => Bool -> Resolver -> ModuleName -> Module -> Sem r ModuleInfo+loadParsedDiscoModule ::+  Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r =>+  Bool ->+  Resolver ->+  ModuleName ->+  Module ->+  Sem r ModuleInfo loadParsedDiscoModule quiet resolver =   loadParsedDiscoModule' quiet REPL resolver [] @@ -309,15 +319,19 @@ --   Map from module names to 'ModuleInfo' records, to avoid loading --   any imported module more than once. Resolve the module, load and --   parse it, then call 'loadParsedDiscoModule''.-loadDiscoModule'-  :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r-  => Bool -> Resolver -> [ModuleName] -> FilePath-  -> Sem r ModuleInfo-loadDiscoModule' quiet resolver inProcess modPath  = do-  (resolvedPath, prov) <- resolveModule resolver modPath-                  >>= maybe (throw $ ModuleNotFound modPath) return+loadDiscoModule' ::+  Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r =>+  Bool ->+  Resolver ->+  [ModuleName] ->+  FilePath ->+  Sem r ModuleInfo+loadDiscoModule' quiet resolver inProcess modPath = do+  (resolvedPath, prov) <-+    resolveModule resolver modPath+      >>= maybe (throw $ ModuleNotFound modPath) return   let name = Named prov modPath-  when (name `elem` inProcess) (throw $ CyclicImport (name:inProcess))+  when (name `elem` inProcess) (throw $ CyclicImport (name : inProcess))   modMap <- use @TopInfo topModMap   case M.lookup name modMap of     Just mi -> return mi@@ -337,34 +351,39 @@ --   it in the context of the top-level type context iff the --   'LoadingMode' parameter is 'REPL'.  Recursively load all its --   imports, then typecheck it.-loadParsedDiscoModule'-  :: Members '[State TopInfo, Output Message, Random, State Mem, Error DiscoError, Embed IO] r-  => Bool -> LoadingMode -> Resolver -> [ModuleName] -> ModuleName -> Module -> Sem r ModuleInfo+loadParsedDiscoModule' ::+  Members '[State TopInfo, Output (Message ann), Random, State Mem, Error DiscoError, Embed IO] r =>+  Bool ->+  LoadingMode ->+  Resolver ->+  [ModuleName] ->+  ModuleName ->+  Module ->+  Sem r ModuleInfo loadParsedDiscoModule' quiet mode resolver inProcess name cm@(Module _ mns _ _ _) = do-   -- Recursively load any modules imported by this one, plus standard   -- library modules (unless NoStdLib is enabled), and build a map with the results.   mis <- mapM (loadDiscoModule' quiet (withStdlib resolver) inProcess) mns   stdmis <- case NoStdLib `S.member` modExts cm of-    True  -> return []+    True -> return []     False -> mapM (loadDiscoModule' True FromStdlib inProcess) stdLib   let modImps = M.fromList (map (view miName &&& id) (mis ++ stdmis))    -- Get context and type definitions from the REPL, in case we are in REPL mode.   topImports <- use (replModInfo . miImports)-  topTyCtx   <- use (replModInfo . miTys)+  topTyCtx <- use (replModInfo . miTys)   topTyDefns <- use (replModInfo . miTydefs)    -- Choose the contexts to use based on mode: if we are loading a   -- standalone module, we should start it in an empty context.  If we   -- are loading something entered at the REPL, we need to include any   -- existing top-level REPL context.-  let importMap = case mode of { Standalone -> modImps; REPL -> topImports <> modImps }-      tyctx   = case mode of { Standalone -> emptyCtx ; REPL -> topTyCtx }-      tydefns = case mode of { Standalone -> M.empty ; REPL -> topTyDefns }+  let importMap = case mode of Standalone -> modImps; REPL -> topImports <> modImps+      tyctx = case mode of Standalone -> emptyCtx; REPL -> topTyCtx+      tydefns = case mode of Standalone -> M.empty; REPL -> topTyDefns    -- Typecheck (and resolve names in) the module.-  m  <- runTCM tyctx tydefns $ checkModule name importMap cm+  m <- runTCM tyctx tydefns $ checkModule name importMap cm    -- Evaluate all the module definitions and add them to the topEnv.   mapError EvalErr $ loadDefsFrom m@@ -375,27 +394,29 @@  -- | Try loading the contents of a file from the filesystem, emitting --   an error if it's not found.-loadFile :: Members '[Output Message, Embed IO] r => FilePath -> Sem r (Maybe String)+loadFile :: Members '[Output (Message ann), Embed IO] r => FilePath -> Sem r (Maybe String) loadFile file = do   res <- liftIO $ handle @SomeException (return . Left) (Right <$> readFile file)   case res of-    Left _  -> info ("File not found:" <+> text file) >> return Nothing+    Left _ -> info ("File not found:" <+> text file) >> return Nothing     Right s -> return (Just s)  -- | Add things from the given module to the set of currently loaded --   things.-addToREPLModule-  :: Members '[Error DiscoError, State TopInfo, Random, State Mem, Output Message] r-  => ModuleInfo -> Sem r ()+addToREPLModule ::+  Members '[Error DiscoError, State TopInfo, Random, State Mem, Output (Message ann)] r =>+  ModuleInfo ->+  Sem r () addToREPLModule mi = modify @TopInfo (replModInfo <>~ mi)  -- | Set the given 'ModuleInfo' record as the currently loaded --   module. This also includes updating the top-level state with new --   term definitions, documentation, types, and type definitions. --   Replaces any previously loaded module.-setREPLModule-  :: Members '[State TopInfo, Random, Error EvalError, State Mem, Output Message] r-  => ModuleInfo -> Sem r ()+setREPLModule ::+  Members '[State TopInfo, Random, Error EvalError, State Mem, Output (Message ann)] r =>+  ModuleInfo ->+  Sem r () setREPLModule mi = do   modify @TopInfo $ replModInfo .~ mi @@ -408,7 +429,6 @@   ModuleInfo ->   Sem r () loadDefsFrom mi = do-   -- Note that the compiled definitions we get back from compileDefns   -- are topologically sorted by mutually recursive group. Each   -- definition needs to be evaluated in an environment containing the@@ -418,7 +438,9 @@  loadDef ::   Members '[State TopInfo, Random, Error EvalError, State Mem] r =>-  QName Core -> Core -> Sem r ()+  QName Core ->+  Core ->+  Sem r () loadDef x body = do   v <- inputToState @TopInfo . inputTopEnv $ eval body   modify @TopInfo $ topEnv %~ Ctx.insert x v
src/Disco/Extensions.hs view
@@ -6,17 +6,17 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Optional extensions to the disco language.-module Disco.Extensions-  ( Ext (..),-    ExtSet,-    defaultExts,-    allExts,-    allExtsList,-    addExtension,-  )+module Disco.Extensions (+  Ext (..),+  ExtSet,+  defaultExts,+  allExts,+  allExtsList,+  addExtension,+) where -import           Data.Set (Set)+import Data.Set (Set) import qualified Data.Set as S  type ExtSet = Set Ext
src/Disco/Interactive/CmdLine.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Interactive.CmdLine -- Copyright   :  disco team and contributors@@ -7,50 +10,52 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Definition of the command-line REPL interface for Disco.-----------------------------------------------------------------------------------module Disco.Interactive.CmdLine-  ( -- * Command-line options record--    DiscoOpts(..)--    -- * optparse-applicative command line parsers-  , discoOpts, discoInfo--    -- * main+module Disco.Interactive.CmdLine (+  -- * Command-line options record+  DiscoOpts (..), -  , discoMain+  -- * optparse-applicative command line parsers+  discoOpts,+  discoInfo, -  ) where+  -- * main+  discoMain,+) where -import           Data.Version                           (showVersion)-import           Paths_disco                            (version)+import Data.Version (showVersion)+import Paths_disco (version) -import           Control.Lens                           hiding (use)-import           Control.Monad                          (unless, when)-import qualified Control.Monad.Catch                    as CMC-import           Control.Monad.IO.Class                 (MonadIO (..))-import           Data.Foldable                          (forM_)-import           Data.List                              (isPrefixOf)-import           Data.Maybe                             (isJust)-import           System.Exit                            (exitFailure,-                                                         exitSuccess)+import Control.Lens hiding (use)+import Control.Monad (unless, when)+import qualified Control.Monad.Catch as CMC+import Control.Monad.IO.Class (MonadIO (..))+import Data.Foldable (forM_)+import Data.List (isPrefixOf)+import Data.Maybe (isJust)+import System.Exit (+  exitFailure,+  exitSuccess,+ ) -import qualified Options.Applicative                    as O-import           System.Console.Haskeline               as H+import qualified Options.Applicative as O+import System.Console.Haskeline as H -import           Disco.Error-import           Disco.Eval-import           Disco.Interactive.Commands-import           Disco.Messages-import           Disco.Module                           (miExts)-import           Disco.Pretty+import Disco.AST.Surface (emptyModule)+import Disco.Error+import Disco.Eval+import Disco.Interactive.Commands+import Disco.Messages+import Disco.Module (+  Resolver (FromStdlib),+  miExts,+ )+import Disco.Names (ModuleName (REPLModule))+import Disco.Pretty -import           Disco.Effects.State-import           Polysemy-import           Polysemy.ConstraintAbsorber.MonadCatch-import           Polysemy.Error+import Disco.Effects.State+import Polysemy+import Polysemy.ConstraintAbsorber.MonadCatch+import Polysemy.Error  ------------------------------------------------------------ -- Command-line options parser@@ -58,63 +63,75 @@  -- | Command-line options for disco. data DiscoOpts = DiscoOpts-  { onlyVersion :: Bool          -- ^ Should we just print the version?-  , evaluate    :: Maybe String  -- ^ A single expression to evaluate-  , cmdFile     :: Maybe String  -- ^ Execute the commands in a given file-  , checkFile   :: Maybe String  -- ^ Check a file and then exit-  , debugFlag   :: Bool+  { onlyVersion :: Bool+  -- ^ Should we just print the version?+  , evaluate :: Maybe String+  -- ^ A single expression to evaluate+  , cmdFile :: Maybe String+  -- ^ Execute the commands in a given file+  , checkFile :: Maybe String+  -- ^ Check a file and then exit+  , debugFlag :: Bool   }  discoOpts :: O.Parser DiscoOpts-discoOpts = DiscoOpts-  <$> O.switch (-        mconcat-        [ O.long "version"-        , O.short 'v'-        , O.help "show current version"-        ]-        )--   <*> O.optional (-        O.strOption (mconcat-          [ O.long "evaluate"-          , O.short 'e'-          , O.help "evaluate an expression"-          , O.metavar "TERM"-          ])+discoOpts =+  DiscoOpts+    <$> O.switch+      ( mconcat+          [ O.long "version"+          , O.short 'v'+          , O.help "show current version"+          ]       )-  <*> O.optional (-        O.strOption (mconcat-          [ O.long "file"-          , O.short 'f'-          , O.help "execute the commands in a file"-          , O.metavar "FILE"-          ])+    <*> O.optional+      ( O.strOption+          ( mconcat+              [ O.long "evaluate"+              , O.short 'e'+              , O.help "evaluate an expression"+              , O.metavar "TERM"+              ]+          )       )-  <*> O.optional (-        O.strOption (mconcat-          [ O.long "check"-          , O.help "check a file without starting the interactive REPL"-          , O.metavar "FILE"-          ])+    <*> O.optional+      ( O.strOption+          ( mconcat+              [ O.long "file"+              , O.short 'f'+              , O.help "execute the commands in a file"+              , O.metavar "FILE"+              ]+          )       )-  <*> O.switch (-        mconcat-        [ O.long "debug"-        , O.help "print debugging information"-        , O.short 'd'-        ]-        )+    <*> O.optional+      ( O.strOption+          ( mconcat+              [ O.long "check"+              , O.help "check a file without starting the interactive REPL"+              , O.metavar "FILE"+              ]+          )+      )+    <*> O.switch+      ( mconcat+          [ O.long "debug"+          , O.help "print debugging information"+          , O.short 'd'+          ]+      )  discoVersion :: String discoVersion = showVersion version  discoInfo :: O.ParserInfo DiscoOpts-discoInfo = O.info (O.helper <*> discoOpts) $ mconcat-  [ O.fullDesc-  , O.progDesc "Command-line interface for Disco, a programming language for discrete mathematics."-  , O.header $ "disco " ++ discoVersion-  ]+discoInfo =+  O.info (O.helper <*> discoOpts) $+    mconcat+      [ O.fullDesc+      , O.progDesc "Command-line interface for Disco, a programming language for discrete mathematics."+      , O.header $ "disco " ++ discoVersion+      ]  optsToCfg :: DiscoOpts -> DiscoConfig optsToCfg opts = initDiscoConfig & debugMode .~ debugFlag opts@@ -137,68 +154,70 @@   let batch = any isJust [evaluate opts, cmdFile opts, checkFile opts]   unless batch $ putStr banner   runDisco (optsToCfg opts) $ do+    -- Load an empty module just to force standard libraries to be loaded first+    _ <- loadParsedDiscoModule True FromStdlib REPLModule emptyModule+     case checkFile opts of       Just file -> do         res <- handleLoad file         liftIO $ if res then exitSuccess else exitFailure-      Nothing   -> return ()+      Nothing -> return ()     case cmdFile opts of       Just file -> do         mcmds <- loadFile file         case mcmds of-          Nothing   -> return ()+          Nothing -> return ()           Just cmds -> mapM_ handleCMD (lines cmds)-      Nothing   -> return ()+      Nothing -> return ()     forM_ (evaluate opts) handleCMD-    unless batch loop--  where--    -- These types used to involve InputT Disco, but we now use Final-    -- (InputT IO) in the list of effects.  see-    -- https://github.com/polysemy-research/polysemy/issues/395 for-    -- inspiration.+    unless batch $ do+      loop+ where+  -- These types used to involve InputT Disco, but we now use Final+  -- (InputT IO) in the list of effects.  see+  -- https://github.com/polysemy-research/polysemy/issues/395 for+  -- inspiration. -    ctrlC :: MonadIO m => m a -> SomeException -> m a-    ctrlC act e = do-      liftIO $ print e-      act+  ctrlC :: MonadIO m => m a -> SomeException -> m a+  ctrlC act e = do+    liftIO $ print e+    act -    withCtrlC :: (MonadIO m, CMC.MonadCatch m) => m a -> m a -> m a-    withCtrlC resume act = CMC.catch act (ctrlC resume)+  withCtrlC :: (MonadIO m, CMC.MonadCatch m) => m a -> m a -> m a+  withCtrlC resume act = CMC.catch act (ctrlC resume) -    loop :: Members DiscoEffects r => Sem r ()-    loop = do-      minput <- embedFinal $ withCtrlC (return $ Just "") (getInputLine "Disco> ")-      case minput of-        Nothing -> return ()-        Just input-          | ":q" `isPrefixOf` input && input `isPrefixOf` ":quit" -> do-              liftIO $ putStrLn "Goodbye!"-              return ()-          | ":{" `isPrefixOf` input -> do-              multiLineLoop []-              loop-          | otherwise -> do-              mapError @_ @DiscoError (Panic . show) $-                absorbMonadCatch $+  loop :: Members DiscoEffects r => Sem r ()+  loop = do+    minput <- embedFinal $ withCtrlC (return $ Just "") (getInputLine "Disco> ")+    case minput of+      Nothing -> return ()+      Just input+        | ":q" `isPrefixOf` input && input `isPrefixOf` ":quit" -> do+            liftIO $ putStrLn "Goodbye!"+            return ()+        | ":{" `isPrefixOf` input -> do+            multiLineLoop []+            loop+        | otherwise -> do+            mapError @_ @DiscoError (Panic . show) $+              absorbMonadCatch $                 withCtrlC (return ()) $-                handleCMD input-              loop+                  handleCMD input+            loop -    multiLineLoop :: Members DiscoEffects r => [String] -> Sem r ()-    multiLineLoop ls = do-      minput <- embedFinal $ withCtrlC (return Nothing) (getInputLine "Disco| ")-      case minput of-        Nothing -> return ()-        Just input-          | ":}" `isPrefixOf` input -> do-              mapError @_ @DiscoError (Panic . show) $-                absorbMonadCatch $+  multiLineLoop :: Members DiscoEffects r => [String] -> Sem r ()+  multiLineLoop ls = do+    minput <- embedFinal $ withCtrlC (return Nothing) (getInputLine "Disco| ")+    case minput of+      Nothing -> return ()+      Just input+        | ":}" `isPrefixOf` input -> do+            mapError @_ @DiscoError (Panic . show) $+              absorbMonadCatch $                 withCtrlC (return ()) $-                handleCMD (unlines (reverse ls))-          | otherwise -> do-              multiLineLoop (input:ls)+                  handleCMD (unlines (reverse ls))+        | otherwise -> do+            multiLineLoop (input : ls)  -- | Parse and run the command corresponding to some REPL input. handleCMD :: Members DiscoEffects r => String -> Sem r ()@@ -206,7 +225,8 @@ handleCMD s = do   exts <- use @TopInfo (replModInfo . miExts)   case parseLine discoCommands exts s of-    Left m  -> info (text m)+    Left m -> info (text m)     Right l -> catch @DiscoError (dispatch discoCommands l) (info . pretty')-                -- The above has to be catch, not outputErrors, because-                -- the latter won't resume afterwards.++-- The above has to be catch, not outputErrors, because+-- the latter won't resume afterwards.
src/Disco/Interactive/Commands.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE OverloadedStrings  #-}-{-# LANGUAGE PatternSynonyms    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE StandaloneDeriving #-} ------------------------------------------------------------------------------ -- | -- Module      :  Disco.Interactive.Commands -- Copyright   :  disco team and contributors@@ -12,68 +11,86 @@ -- -- Defining and dispatching all commands/functionality available at -- the REPL prompt.--------------------------------------------------------------------------------module Disco.Interactive.Commands-  ( dispatch,-    discoCommands,-    handleLoad,-    loadFile,-    parseLine-  ) where+module Disco.Interactive.Commands (+  dispatch,+  discoCommands,+  handleLoad,+  loadFile,+  parseLine,+) where -import           Control.Arrow                    ((&&&))-import           Control.Lens                     (to, view, (%~), (.~), (?~),-                                                   (^.))-import           Control.Monad.Except-import           Data.Char                        (isSpace)-import           Data.Coerce-import           Data.List                        (find, isPrefixOf, sortBy)-import           Data.Map                         ((!))-import qualified Data.Map                         as M-import           Data.Typeable-import           Prelude                          as P-import           System.FilePath                  (splitFileName)+import Control.Arrow ((&&&))+import Control.Lens (+  to,+  view,+  (%~),+  (.~),+  (?~),+  (^.),+ )+import Control.Monad.Except+import Data.Char (isSpace)+import Data.Coerce+import Data.List (find, isPrefixOf, sortBy)+import Data.Map ((!))+import qualified Data.Map as M+import Data.Typeable+import System.FilePath (splitFileName)+import Prelude as P -import           Text.Megaparsec                  hiding (State, runParser)-import qualified Text.Megaparsec.Char             as C-import           Unbound.Generics.LocallyNameless (Name, name2String,-                                                   string2Name)+import Text.Megaparsec hiding (State, runParser)+import qualified Text.Megaparsec.Char as C+import Unbound.Generics.LocallyNameless (+  Name,+  name2String,+  string2Name,+ ) -import           Disco.Effects.Input-import           Disco.Effects.LFresh-import           Disco.Effects.State-import           Polysemy-import           Polysemy.Error                   hiding (try)-import           Polysemy.Output-import           Polysemy.Reader+import Disco.Effects.Input+import Disco.Effects.LFresh+import Disco.Effects.State+import Polysemy+import Polysemy.Error hiding (try)+import Polysemy.Output+import Polysemy.Reader -import           Data.Maybe                       (mapMaybe, maybeToList)-import           Disco.AST.Surface-import           Disco.AST.Typed-import           Disco.Compile-import           Disco.Context                    as Ctx-import           Disco.Desugar-import           Disco.Doc-import           Disco.Error-import           Disco.Eval-import           Disco.Extensions-import           Disco.Interpret.CESK-import           Disco.Messages-import           Disco.Module-import           Disco.Names-import           Disco.Parser                     (Parser, ident, reservedOp,-                                                   runParser, sc, symbol, term,-                                                   wholeModule, withExts)-import           Disco.Pretty                     hiding (empty, (<>))-import qualified Disco.Pretty                     as Pretty-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims               (Prim (PrimBOp, PrimUOp),-                                                   toPrim)-import           Disco.Typecheck-import           Disco.Typecheck.Erase-import           Disco.Types                      (pattern TyString, toPolyType)-import           Disco.Value+import Data.Maybe (mapMaybe, maybeToList)+import Disco.AST.Surface+import Disco.AST.Typed+import Disco.Compile+import Disco.Context as Ctx+import Disco.Desugar+import Disco.Doc+import Disco.Error+import Disco.Eval+import Disco.Extensions+import Disco.Interpret.CESK+import Disco.Messages+import Disco.Module+import Disco.Names+import Disco.Parser (+  Parser,+  ident,+  reservedOp,+  runParser,+  sc,+  symbol,+  term,+  wholeModule,+  withExts,+ )+import Disco.Pretty hiding (empty, (<>))+import qualified Disco.Pretty as PP+import Disco.Property (prettyTestResult)+import Disco.Syntax.Operators+import Disco.Syntax.Prims (+  Prim (PrimBOp, PrimUOp),+  toPrim,+ )+import Disco.Typecheck+import Disco.Typecheck.Erase+import Disco.Types (toPolyType, pattern TyString)+import Disco.Value  ------------------------------------------------------------ -- REPL expression type@@ -82,24 +99,24 @@ -- | Data type to represent things typed at the Disco REPL.  Each --   constructor has a singleton type to facilitate dispatch. data REPLExpr :: CmdTag -> * where-  TypeCheck :: Term      -> REPLExpr 'CTypeCheck -- Typecheck a term-  Eval      :: Module    -> REPLExpr 'CEval      -- Evaluate a block-  TestProp  :: Term      -> REPLExpr 'CTestProp  -- Run a property test-  ShowDefn  :: Name Term -> REPLExpr 'CShowDefn  -- Show a variable's definition-  Parse     :: Term      -> REPLExpr 'CParse     -- Show the parsed AST-  Pretty    :: Term      -> REPLExpr 'CPretty    -- Pretty-print a term-  Print     :: Term      -> REPLExpr 'CPrint     -- Print a string-  Ann       :: Term      -> REPLExpr 'CAnn       -- Show type-annotated term-  Desugar   :: Term      -> REPLExpr 'CDesugar   -- Show a desugared term-  Compile   :: Term      -> REPLExpr 'CCompile   -- Show a compiled term-  Load      :: FilePath  -> REPLExpr 'CLoad      -- Load a file.-  Reload    ::              REPLExpr 'CReload    -- Reloads the most recently-                                                 -- loaded file.-  Doc       :: DocInput  -> REPLExpr 'CDoc       -- Show documentation.-  Nop       ::              REPLExpr 'CNop       -- No-op, e.g. if the user-                                                 -- just enters a comment-  Help      ::              REPLExpr 'CHelp      -- Show help-  Names     ::              REPLExpr 'CNames     -- Show bound names+  TypeCheck :: Term -> REPLExpr 'CTypeCheck -- Typecheck a term+  Eval :: Module -> REPLExpr 'CEval -- Evaluate a block+  TestProp :: Term -> REPLExpr 'CTestProp -- Run a property test+  ShowDefn :: Name Term -> REPLExpr 'CShowDefn -- Show a variable's definition+  Parse :: Term -> REPLExpr 'CParse -- Show the parsed AST+  Pretty :: Term -> REPLExpr 'CPretty -- Pretty-print a term+  Print :: Term -> REPLExpr 'CPrint -- Print a string+  Ann :: Term -> REPLExpr 'CAnn -- Show type-annotated term+  Desugar :: Term -> REPLExpr 'CDesugar -- Show a desugared term+  Compile :: Term -> REPLExpr 'CCompile -- Show a compiled term+  Load :: FilePath -> REPLExpr 'CLoad -- Load a file.+  Reload :: REPLExpr 'CReload -- Reloads the most recently+  -- loaded file.+  Doc :: DocInput -> REPLExpr 'CDoc -- Show documentation.+  Nop :: REPLExpr 'CNop -- No-op, e.g. if the user+  -- just enters a comment+  Help :: REPLExpr 'CHelp -- Show help+  Names :: REPLExpr 'CNames -- Show bound names  deriving instance Show (REPLExpr c) @@ -152,23 +169,23 @@ -- | Data type to represent all the information about a single REPL --   command. data REPLCommand (c :: CmdTag) = REPLCommand-  { -- | Name of the command-    name      :: String,-    -- | Help text showing how to use the command, e.g. ":ann <term>"-    helpcmd   :: String,-    -- | Short free-form text explaining the command.-    --   We could also consider adding long help text as well.-    shortHelp :: String,-    -- | Is the command for users or devs?-    category  :: REPLCommandCategory,-    -- | Is it a built-in command or colon command?-    cmdtype   :: REPLCommandType,-    -- | The action to execute,-    -- given the input to the-    -- command.-    action    :: REPLExpr c -> (forall r. Members DiscoEffects r => Sem r ()),-    -- | Parser for the command argument(s).-    parser    :: Parser (REPLExpr c)+  { name :: String+  -- ^ Name of the command+  , helpcmd :: String+  -- ^ Help text showing how to use the command, e.g. ":ann <term>"+  , shortHelp :: String+  -- ^ Short free-form text explaining the command.+  --   We could also consider adding long help text as well.+  , category :: REPLCommandCategory+  -- ^ Is the command for users or devs?+  , cmdtype :: REPLCommandType+  -- ^ Is it a built-in command or colon command?+  , action :: REPLExpr c -> (forall r. Members DiscoEffects r => Sem r ())+  -- ^ The action to execute,+  -- given the input to the+  -- command.+  , parser :: Parser (REPLExpr c)+  -- ^ Parser for the command argument(s).   }  -- | An existential wrapper around any REPL command info record.@@ -200,22 +217,22 @@ --   to the first matching command. discoCommands :: REPLCommands discoCommands =-  [ SomeCmd annCmd,-    SomeCmd compileCmd,-    SomeCmd desugarCmd,-    SomeCmd docCmd,-    SomeCmd evalCmd,-    SomeCmd helpCmd,-    SomeCmd loadCmd,-    SomeCmd namesCmd,-    SomeCmd nopCmd,-    SomeCmd parseCmd,-    SomeCmd prettyCmd,-    SomeCmd printCmd,-    SomeCmd reloadCmd,-    SomeCmd showDefnCmd,-    SomeCmd typeCheckCmd,-    SomeCmd testPropCmd+  [ SomeCmd annCmd+  , SomeCmd compileCmd+  , SomeCmd desugarCmd+  , SomeCmd docCmd+  , SomeCmd evalCmd+  , SomeCmd helpCmd+  , SomeCmd loadCmd+  , SomeCmd namesCmd+  , SomeCmd nopCmd+  , SomeCmd parseCmd+  , SomeCmd prettyCmd+  , SomeCmd printCmd+  , SomeCmd reloadCmd+  , SomeCmd showDefnCmd+  , SomeCmd typeCheckCmd+  , SomeCmd testPropCmd   ]  ------------------------------------------------------------@@ -236,12 +253,12 @@ --   colon, return a parser for its arguments. parseCommandArgs :: REPLCommands -> String -> Parser SomeREPLExpr parseCommandArgs allCommands cmd = maybe badCmd snd $ find ((cmd `isPrefixOf`) . fst) parsers-  where-    badCmd = fail $ "Command \":" ++ cmd ++ "\" is unrecognized."+ where+  badCmd = fail $ "Command \":" ++ cmd ++ "\" is unrecognized." -    parsers =-      map (\(SomeCmd rc) -> (name rc, SomeREPL <$> parser rc)) $-        byCmdType ColonCmd allCommands+  parsers =+    map (\(SomeCmd rc) -> (name rc, SomeREPL <$> parser rc)) $+      byCmdType ColonCmd allCommands  -- | Parse a file name. fileParser :: Parser FilePath@@ -259,7 +276,7 @@ parseLine :: REPLCommands -> ExtSet -> String -> Either String SomeREPLExpr parseLine allCommands exts s =   case runParser (withExts exts (lineParser allCommands)) "" s of-    Left e  -> Left $ errorBundlePretty e+    Left e -> Left $ errorBundlePretty e     Right l -> Right l  --------------------------------------------------------------------------------@@ -272,17 +289,17 @@ annCmd :: REPLCommand 'CAnn annCmd =   REPLCommand-    { name = "ann",-      helpcmd = ":ann",-      shortHelp = "Show type-annotated typechecked term",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleAnn $ x,-      parser = Ann <$> term+    { name = "ann"+    , helpcmd = ":ann"+    , shortHelp = "Show type-annotated typechecked term"+    , category = Dev+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleAnn+    , parser = Ann <$> term     }  handleAnn ::-  Members '[Error DiscoError, Input TopInfo, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, Output (Message ())] r =>   REPLExpr 'CAnn ->   Sem r () handleAnn (Ann t) = do@@ -295,17 +312,17 @@ compileCmd :: REPLCommand 'CCompile compileCmd =   REPLCommand-    { name = "compile",-      helpcmd = ":compile",-      shortHelp = "Show a compiled term",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleCompile $ x,-      parser = Compile <$> term+    { name = "compile"+    , helpcmd = ":compile"+    , shortHelp = "Show a compiled term"+    , category = Dev+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleCompile+    , parser = Compile <$> term     }  handleCompile ::-  Members '[Error DiscoError, Input TopInfo, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, Output (Message ())] r =>   REPLExpr 'CCompile ->   Sem r () handleCompile (Compile t) = do@@ -318,17 +335,17 @@ desugarCmd :: REPLCommand 'CDesugar desugarCmd =   REPLCommand-    { name = "desugar",-      helpcmd = ":desugar",-      shortHelp = "Show a desugared term",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleDesugar $ x,-      parser = Desugar <$> term+    { name = "desugar"+    , helpcmd = ":desugar"+    , shortHelp = "Show a desugared term"+    , category = Dev+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleDesugar+    , parser = Desugar <$> term     }  handleDesugar ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   REPLExpr 'CDesugar ->   Sem r () handleDesugar (Desugar t) = do@@ -341,50 +358,64 @@ docCmd :: REPLCommand 'CDoc docCmd =   REPLCommand-    { name = "doc",-      helpcmd = ":doc <term>",-      shortHelp = "Show documentation",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleDoc $ x,-      parser = Doc <$> parseDoc+    { name = "doc"+    , helpcmd = ":doc <term>"+    , shortHelp = "Show documentation"+    , category = User+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleDoc+    , parser = Doc <$> parseDoc     } --- XXX+-- An input to the :doc command can be either a term, a primitive+-- operator, or something else. data DocInput = DocTerm Term | DocPrim Prim | DocOther String   deriving (Show)  parseDoc :: Parser DocInput parseDoc =-      (DocTerm <$> try term)-  <|> (DocPrim <$> try (parseNakedOpPrim <?> "operator"))-  <|> (DocOther <$> (sc *> many (anySingleBut ' ')))+  (DocTerm <$> try term)+    <|> (DocPrim <$> try (parseNakedOpPrim <?> "operator"))+    <|> (DocOther <$> (sc *> many (anySingleBut ' ')))  handleDoc ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   REPLExpr 'CDoc ->   Sem r () handleDoc (Doc (DocTerm (TBool _))) = handleDocBool+handleDoc (Doc (DocTerm TUnit)) = handleDocUnit+handleDoc (Doc (DocTerm TWild)) = handleDocWild handleDoc (Doc (DocTerm (TPrim p))) = handleDocPrim p-handleDoc (Doc (DocTerm (TVar x)))  = handleDocVar x-handleDoc (Doc (DocTerm _))         =+handleDoc (Doc (DocTerm (TVar x))) = handleDocVar x+handleDoc (Doc (DocTerm _)) =   err "Can't display documentation for an expression.  Try asking about a function, operator, or type name."-handleDoc (Doc (DocPrim p))         = handleDocPrim p-handleDoc (Doc (DocOther s))        = handleDocOther s+handleDoc (Doc (DocPrim p)) = handleDocPrim p+handleDoc (Doc (DocOther s)) = handleDocOther s -handleDocBool :: Members '[Output Message] r => Sem r ()+handleDocBool :: Members '[Output (Message ())] r => Sem r () handleDocBool =   info $     "true and false (also written True and False) are the two possible values of type Boolean."-    $+$-    mkReference "bool"+      $+$ mkReference "bool" +handleDocUnit :: Members '[Output (Message ())] r => Sem r ()+handleDocUnit =+  info $+    "The unit value, i.e. the single value of type Unit."+      $+$ mkReference "unit"++handleDocWild :: Members '[Output (Message ())] r => Sem r ()+handleDocWild =+  info $+    "A wildcard pattern."+      $+$ mkReference "wild-pattern"+ handleDocVar ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   Name Term ->   Sem r () handleDocVar x = do-  replCtx  <- inputs @TopInfo (view (replModInfo . miTys))+  replCtx <- inputs @TopInfo (view (replModInfo . miTys))   replTydefs <- inputs @TopInfo (view (replModInfo . miTydefs))   replDocs <- inputs @TopInfo (view (replModInfo . miDocs)) @@ -402,103 +433,119 @@     ([], Nothing) ->       -- Maybe the variable name entered by the user is actually a prim.       case toPrim (name2String x) of-        (prim:_) -> handleDocPrim prim-        _        -> err $ "No documentation found for '" <> pretty' x <> "'."+        (prim : _) -> handleDocPrim prim+        _ -> err $ "No documentation found for '" <> pretty' x <> "'."     (binds, def) ->       mapM_ (showDoc docs) (map Left binds ++ map Right (maybeToList def))--  where-    showDoc docMap (Left (qn, ty)) = info $+ where+  showDoc docMap (Left (qn, ty)) =+    info $       hsep [pretty' x, ":", pretty' ty]-      $+$-      case Ctx.lookup' qn docMap of-        Just (DocString ss : _) -> vcat (text "" : map text ss ++ [text ""])-        _                       -> Pretty.empty-    showDoc docMap (Right tdBody) = info $+        $+$ case Ctx.lookup' qn docMap of+          Just (DocString ss : _) -> vcat (text "" : map text ss ++ [text ""])+          _ -> PP.empty+  showDoc docMap (Right tdBody) =+    info $       pretty' (name2String x, tdBody)-      $+$-      case Ctx.lookupAll' x docMap of-        ((_, DocString ss : _) : _) -> vcat (text "" : map text ss ++ [text ""])-        _                           -> Pretty.empty+        $+$ case Ctx.lookupAll' x docMap of+          ((_, DocString ss : _) : _) -> vcat (text "" : map text ss ++ [text ""])+          _ -> PP.empty  handleDocPrim ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   Prim ->   Sem r () handleDocPrim prim = do   handleTypeCheck (TypeCheck (TPrim prim))-  info $ vcat-    [ case prim of-        PrimUOp u -> describeAlts (f == Post) (f == Pre) syns-          where+  info+    . vcat+    $ ( case prim of+          PrimUOp u -> describeAlts (f == Post) (f == Pre) syns+           where             OpInfo (UOpF f _) syns _ = uopMap ! u-        PrimBOp b -> describeAlts True True (opSyns $ bopMap ! b)-        _         -> Pretty.empty-    , case prim of-        PrimUOp u -> describePrec (uPrec u)-        PrimBOp b -> describePrec (bPrec b) <> describeFixity (assoc b)-        _         -> Pretty.empty-    ]+          PrimBOp b -> describeAlts True True (opSyns $ bopMap ! b)+          _ -> []+      )+      ++ ( case prim of+            PrimUOp u -> [describePrec (uPrec u)]+            PrimBOp b -> [describePrec (bPrec b) <> describeFixity (assoc b)]+            _ -> []+         )   case (M.lookup prim primDoc, M.lookup prim primReference) of     (Nothing, Nothing) -> return ()-    (Nothing, Just p)  -> info $ mkReference p-    (Just d, mp)  ->-      info $ "" $+$ text d $+$ "" $+$ maybe Pretty.empty (\p -> mkReference p $+$ "") mp-  where-    describePrec p = "precedence level" <+> text (show p)-    describeFixity In  = Pretty.empty-    describeFixity InL = ", left associative"-    describeFixity InR = ", right associative"-    describeAlts _ _ []            = Pretty.empty-    describeAlts _ _ [_]           = Pretty.empty-    describeAlts pre post (_:alts) = "Alternative syntax:" <+> intercalate "," (map showOp alts)-      where-        showOp op = hcat-          [ if pre then "~" else Pretty.empty-          , text op-          , if post then "~" else Pretty.empty]-+    (Nothing, Just p) -> info $ mkReference p+    (Just d, mp) ->+      info $+        vcat+          [ PP.empty+          , text d+          , PP.empty+          , maybe PP.empty (\p -> vcat [mkReference p, PP.empty]) mp+          ]+ where+  describePrec p = "precedence level" <+> text (show p)+  describeFixity In = PP.empty+  describeFixity InL = ", left associative"+  describeFixity InR = ", right associative"+  describeAlts _ _ [] = []+  describeAlts _ _ [_] = []+  describeAlts pre post (_ : alts) = ["Alternative syntax:" <+> intercalate "," (map showOp alts)]+   where+    showOp op =+      hcat+        [ if pre then "~" else PP.empty+        , text op+        , if post then "~" else PP.empty+        ] -mkReference :: String -> Sem r Doc+mkReference :: String -> Sem r (Doc ann) mkReference p =   "https://disco-lang.readthedocs.io/en/latest/reference/" <> text p <> ".html"  handleDocOther ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   String ->   Sem r () handleDocOther s =   case (M.lookup s otherDoc, M.lookup s otherReference) of     (Nothing, Nothing) -> info $ "No documentation found for '" <> text s <> "'."-    (Nothing, Just p)  -> info $ mkReference p-    (Just d, mp)  ->-      info $ text d $+$ "" $+$ maybe Pretty.empty (\p -> mkReference p $+$ "") mp+    (Nothing, Just p) -> info $ mkReference p+    (Just d, mp) ->+      info $+        vcat+          [ text d+          , PP.empty+          , maybe PP.empty (\p -> vcat [mkReference p, PP.empty]) mp+          ]  ------------------------------------------------------------ -- eval  evalCmd :: REPLCommand 'CEval-evalCmd = REPLCommand-  { name      = "eval"-  , helpcmd   = "<code>"-  , shortHelp = "Evaluate a block of code"-  , category  = User-  , cmdtype   = BuiltIn-  , action    = \x -> handleEval x-  , parser    = Eval <$> wholeModule REPL-  }+evalCmd =+  REPLCommand+    { name = "eval"+    , helpcmd = "<code>"+    , shortHelp = "Evaluate a block of code"+    , category = User+    , cmdtype = BuiltIn+    , action = handleEval+    , parser = Eval <$> wholeModule REPL+    } -handleEval-  :: Members (Error DiscoError ': State TopInfo ': Output Message ': Embed IO ': EvalEffects) r-  => REPLExpr 'CEval -> Sem r ()+handleEval ::+  Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': Embed IO ': EvalEffects) r =>+  REPLExpr 'CEval ->+  Sem r () handleEval (Eval m) = do   mi <- inputToState @TopInfo $ loadParsedDiscoModule False FromCwdOrStdlib REPLModule m   addToREPLModule mi   forM_ (mi ^. miTerms) (mapError EvalErr . evalTerm True . fst)-  -- garbageCollect? +-- garbageCollect?+ -- First argument = should the value be printed?-evalTerm :: Members (Error EvalError ': State TopInfo ': Output Message ': EvalEffects) r => Bool -> ATerm -> Sem r Value+evalTerm :: Members (Error EvalError ': State TopInfo ': Output (Message ()) ': EvalEffects) r => Bool -> ATerm -> Sem r Value evalTerm pr at = do   env <- use @TopInfo topEnv   v <- runInputConst env $ eval (compileTerm at)@@ -507,11 +554,11 @@   when pr $ info $ runInputConst tydefs $ prettyValue' ty v    modify @TopInfo $-    (replModInfo . miTys %~ Ctx.insert (QName (QualifiedName REPLModule) (string2Name "it")) (toPolyType ty)) .-    (topEnv %~ Ctx.insert (QName (QualifiedName REPLModule) (string2Name "it")) v)+    (replModInfo . miTys %~ Ctx.insert (QName (QualifiedName REPLModule) (string2Name "it")) (toPolyType ty))+      . (topEnv %~ Ctx.insert (QName (QualifiedName REPLModule) (string2Name "it")) v)   return v-  where-    ty = getType at+ where+  ty = getType at  ------------------------------------------------------------ -- :help@@ -519,33 +566,33 @@ helpCmd :: REPLCommand 'CHelp helpCmd =   REPLCommand-    { name = "help",-      helpcmd = ":help",-      shortHelp = "Show help",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> handleHelp x,-      parser = return Help+    { name = "help"+    , helpcmd = ":help"+    , shortHelp = "Show help"+    , category = User+    , cmdtype = ColonCmd+    , action = handleHelp+    , parser = return Help     } -handleHelp :: Member (Output Message) r => REPLExpr 'CHelp -> Sem r ()+handleHelp :: Member (Output (Message ())) r => REPLExpr 'CHelp -> Sem r () handleHelp Help =   info $     vcat-    [ "Commands available from the prompt:"-    , text ""-    , vcat (map (\(SomeCmd c) -> showCmd c) $ sortedList discoCommands)-    , text ""-    ]-  where-    maxlen = longestCmd discoCommands-    sortedList cmds =-      sortBy (\(SomeCmd x) (SomeCmd y) -> compare (name x) (name y)) $ filteredCommands cmds-    --  don't show dev-only commands by default-    filteredCommands cmds = P.filter (\(SomeCmd c) -> category c == User) cmds-    showCmd c = text (padRight (helpcmd c) maxlen ++ "  " ++ shortHelp c)-    longestCmd cmds = maximum $ map (\(SomeCmd c) -> length $ helpcmd c) cmds-    padRight s maxsize = take maxsize (s ++ repeat ' ')+      [ "Commands available from the prompt:"+      , text ""+      , vcat (map (\(SomeCmd c) -> showCmd c) $ sortedList discoCommands)+      , text ""+      ]+ where+  maxlen = longestCmd discoCommands+  sortedList cmds =+    sortBy (\(SomeCmd x) (SomeCmd y) -> compare (name x) (name y)) $ filteredCommands cmds+  showCmd c = text (padRight (helpcmd c) maxlen ++ "  " ++ shortHelp c)+  longestCmd cmds = maximum $ map (\(SomeCmd c) -> length $ helpcmd c) cmds+  padRight s maxsize = take maxsize (s ++ repeat ' ')+  --  don't show dev-only commands by default+  filteredCommands = P.filter (\(SomeCmd c) -> category c == User)  ------------------------------------------------------------ -- :load@@ -553,13 +600,13 @@ loadCmd :: REPLCommand 'CLoad loadCmd =   REPLCommand-    { name = "load",-      helpcmd = ":load <filename>",-      shortHelp = "Load a file",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> handleLoadWrapper x,-      parser = Load <$> fileParser+    { name = "load"+    , helpcmd = ":load <filename>"+    , shortHelp = "Load a file"+    , category = User+    , cmdtype = ColonCmd+    , action = handleLoadWrapper+    , parser = Load <$> fileParser     }  -- | Parses, typechecks, and loads a module by first recursively loading any imported@@ -567,13 +614,13 @@ --   in the parent module are executed. --   Disco.Interactive.CmdLine uses a version of this function that returns a Bool. handleLoadWrapper ::-  Members (Error DiscoError ': State TopInfo ': Output Message ': Embed IO ': EvalEffects) r =>+  Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': Embed IO ': EvalEffects) r =>   REPLExpr 'CLoad ->   Sem r () handleLoadWrapper (Load fp) = void (handleLoad fp)  handleLoad ::-  Members (Error DiscoError ': State TopInfo ': Output Message ': Embed IO ': EvalEffects) r =>+  Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': Embed IO ': EvalEffects) r =>   FilePath ->   Sem r Bool handleLoad fp = do@@ -599,36 +646,36 @@   info "Loaded."   return t --- XXX Return a structured summary of the results, not a Bool;--- separate out results generation and pretty-printing, & move this--- somewhere else.-runAllTests :: Members (Output Message ': Input TopInfo ': EvalEffects) r => [QName Term] -> Ctx ATerm [AProperty] -> Sem r Bool -- (Ctx ATerm [TestResult])+-- XXX Return a structured summary of the results, not a Bool; & move+-- this somewhere else?+runAllTests :: Members (Output (Message ()) ': Input TopInfo ': EvalEffects) r => [QName Term] -> Ctx ATerm [AProperty] -> Sem r Bool -- (Ctx ATerm [TestResult]) runAllTests declNames aprops   | Ctx.null aprops = return True-  | otherwise     = do+  | otherwise = do       info "Running tests..."       -- Use the order the names were defined in the module       and <$> mapM (uncurry runTests) (mapMaybe (\n -> (n,) <$> Ctx.lookup' (coerce n) aprops) declNames)--  where-    numSamples :: Int-    numSamples = 50   -- XXX make this configurable somehow--    runTests :: Members (Output Message ': Input TopInfo ': EvalEffects) r => QName Term -> [AProperty] -> Sem r Bool-    runTests (QName _ n) props = do-      results <- inputTopEnv $ traverse (sequenceA . (id &&& runTest numSamples)) props-      let failures = P.filter (not . testIsOk . snd) results-          hdr = pretty' n <> ":"+ where+  numSamples :: Int+  numSamples = 50 -- XXX make this configurable somehow+  runTests :: Members (Output (Message ()) ': Input TopInfo ': EvalEffects) r => QName Term -> [AProperty] -> Sem r Bool+  runTests (QName _ n) props = do+    results <- inputTopEnv $ traverse (sequenceA . (id &&& runTest numSamples)) props+    let failures = P.filter (not . testIsOk . snd) results+        hdr = pretty' n <> ":" -      case P.null failures of-        True  -> info $ nest 2 $ hdr <+> "OK"-        False -> do-          tydefs <- inputs @TopInfo (view (replModInfo . to allTydefs))-          let prettyFailures =-                runInputConst tydefs . runReader initPA . runLFresh $-                  bulletList "-" $ map (uncurry prettyTestFailure) failures-          info $ nest 2 $ hdr $+$ prettyFailures-      return (P.null failures)+    case P.null failures of+      True -> info $ indent 2 $ hdr <+> "OK"+      False -> do+        tydefs <- inputs @TopInfo (view (replModInfo . to allTydefs))+        let prettyFailures =+              runInputConst tydefs+                . runReader initPA+                . runLFresh+                $ bulletList "-"+                $ map (uncurry prettyTestResult) failures+        info $ indent 2 $ hdr $+$ prettyFailures+    return (P.null failures)  ------------------------------------------------------------ -- :names@@ -636,18 +683,18 @@ namesCmd :: REPLCommand 'CNames namesCmd =   REPLCommand-    { name = "names",-      helpcmd = ":names",-      shortHelp = "Show all names in current scope",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> inputToState . handleNames $ x,-      parser = return Names+    { name = "names"+    , helpcmd = ":names"+    , shortHelp = "Show all names in current scope"+    , category = User+    , cmdtype = ColonCmd+    , action = inputToState . handleNames+    , parser = return Names     }  -- | Show names and types for each item in the top-level context. handleNames ::-  Members '[Input TopInfo, LFresh, Output Message] r =>+  Members '[Input TopInfo, LFresh, Output (Message ())] r =>   REPLExpr 'CNames ->   Sem r () handleNames Names = do@@ -655,10 +702,9 @@   ctx <- inputs @TopInfo (view (replModInfo . miTys))   info $     vcat (map pretty' (M.assocs tyDef))-    $+$-    vcat (map showFn (Ctx.assocs ctx))-  where-    showFn (QName _ x, ty) = hsep [pretty' x, text ":", pretty' ty]+      $+$ vcat (map showFn (Ctx.assocs ctx))+ where+  showFn (QName _ x, ty) = hsep [pretty' x, text ":", pretty' ty]  ------------------------------------------------------------ -- nop@@ -666,13 +712,13 @@ nopCmd :: REPLCommand 'CNop nopCmd =   REPLCommand-    { name = "nop",-      helpcmd = "",-      shortHelp = "No-op, e.g. if the user just enters a comment",-      category = Dev,-      cmdtype = BuiltIn,-      action = \x -> handleNop x,-      parser = Nop <$ (sc <* eof)+    { name = "nop"+    , helpcmd = ""+    , shortHelp = "No-op, e.g. if the user just enters a comment"+    , category = Dev+    , cmdtype = BuiltIn+    , action = handleNop+    , parser = Nop <$ (sc <* eof)     }  handleNop :: REPLExpr 'CNop -> Sem r ()@@ -684,16 +730,16 @@ parseCmd :: REPLCommand 'CParse parseCmd =   REPLCommand-    { name = "parse",-      helpcmd = ":parse <expr>",-      shortHelp = "Show the parsed AST",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> handleParse x,-      parser = Parse <$> term+    { name = "parse"+    , helpcmd = ":parse <expr>"+    , shortHelp = "Show the parsed AST"+    , category = Dev+    , cmdtype = ColonCmd+    , action = handleParse+    , parser = Parse <$> term     } -handleParse :: Member (Output Message) r => REPLExpr 'CParse -> Sem r ()+handleParse :: Member (Output (Message ())) r => REPLExpr 'CParse -> Sem r () handleParse (Parse t) = info (text (show t))  ------------------------------------------------------------@@ -702,16 +748,16 @@ prettyCmd :: REPLCommand 'CPretty prettyCmd =   REPLCommand-    { name = "pretty",-      helpcmd = ":pretty <expr>",-      shortHelp = "Pretty-print a term",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> handlePretty x,-      parser = Pretty <$> term+    { name = "pretty"+    , helpcmd = ":pretty <expr>"+    , shortHelp = "Pretty-print a term"+    , category = Dev+    , cmdtype = ColonCmd+    , action = handlePretty+    , parser = Pretty <$> term     } -handlePretty :: Members '[LFresh, Output Message] r => REPLExpr 'CPretty -> Sem r ()+handlePretty :: Members '[LFresh, Output (Message ())] r => REPLExpr 'CPretty -> Sem r () handlePretty (Pretty t) = info $ pretty' t  ------------------------------------------------------------@@ -720,16 +766,16 @@ printCmd :: REPLCommand 'CPrint printCmd =   REPLCommand-    { name = "print",-      helpcmd = ":print <expr>",-      shortHelp = "Print a string without the double quotes, interpreting special characters",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> handlePrint x,-      parser = Print <$> term+    { name = "print"+    , helpcmd = ":print <expr>"+    , shortHelp = "Print a string without the double quotes, interpreting special characters"+    , category = User+    , cmdtype = ColonCmd+    , action = handlePrint+    , parser = Print <$> term     } -handlePrint :: Members (Error DiscoError ': State TopInfo ': Output Message ': EvalEffects) r => REPLExpr 'CPrint -> Sem r ()+handlePrint :: Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': EvalEffects) r => REPLExpr 'CPrint -> Sem r () handlePrint (Print t) = do   at <- inputToState . typecheckTop $ checkTop t (toPolyType TyString)   v <- mapError EvalErr . evalTerm False $ at@@ -741,24 +787,24 @@ reloadCmd :: REPLCommand 'CReload reloadCmd =   REPLCommand-    { name = "reload",-      helpcmd = ":reload",-      shortHelp = "Reloads the most recently loaded file",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> handleReload x,-      parser = return Reload+    { name = "reload"+    , helpcmd = ":reload"+    , shortHelp = "Reloads the most recently loaded file"+    , category = User+    , cmdtype = ColonCmd+    , action = handleReload+    , parser = return Reload     }  handleReload ::-  Members (Error DiscoError ': State TopInfo ': Output Message ': Embed IO ': EvalEffects) r =>+  Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': Embed IO ': EvalEffects) r =>   REPLExpr 'CReload ->   Sem r () handleReload Reload = do   file <- use lastFile   case file of     Nothing -> info "No file to reload."-    Just f  -> void (handleLoad f)+    Just f -> void (handleLoad f)  ------------------------------------------------------------ -- :defn@@ -766,22 +812,22 @@ showDefnCmd :: REPLCommand 'CShowDefn showDefnCmd =   REPLCommand-    { name = "defn",-      helpcmd = ":defn <var>",-      shortHelp = "Show a variable's definition",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleShowDefn $ x,-      parser = ShowDefn <$> (sc *> ident)+    { name = "defn"+    , helpcmd = ":defn <var>"+    , shortHelp = "Show a variable's definition"+    , category = User+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleShowDefn+    , parser = ShowDefn <$> (sc *> ident)     }  handleShowDefn ::-  Members '[Input TopInfo, LFresh, Output Message] r =>+  Members '[Input TopInfo, LFresh, Output (Message ())] r =>   REPLExpr 'CShowDefn ->   Sem r () handleShowDefn (ShowDefn x) = do   let name2s = name2String x-  defns   <- inputs @TopInfo (view (replModInfo . miTermdefs))+  defns <- inputs @TopInfo (view (replModInfo . miTermdefs))   tyDefns <- inputs @TopInfo (view (replModInfo . miTydefs))    let xdefs = Ctx.lookupAll' (coerce x) defns@@ -791,7 +837,7 @@     let ds = map (pretty' . snd) xdefs ++ maybe [] (pure . pretty' . (name2s,)) mtydef     case ds of       [] -> text "No definition for" <+> pretty' x-      _  -> vcat ds+      _ -> vcat ds  ------------------------------------------------------------ -- :test@@ -799,17 +845,17 @@ testPropCmd :: REPLCommand 'CTestProp testPropCmd =   REPLCommand-    { name = "test",-      helpcmd = ":test <property>",-      shortHelp = "Test a property using random examples",-      category = User,-      cmdtype = ColonCmd,-      action = \x -> handleTest x,-      parser = TestProp <$> term+    { name = "test"+    , helpcmd = ":test <property>"+    , shortHelp = "Test a property using random examples"+    , category = User+    , cmdtype = ColonCmd+    , action = handleTest+    , parser = TestProp <$> term     }  handleTest ::-  Members (Error DiscoError ': State TopInfo ': Output Message ': EvalEffects) r =>+  Members (Error DiscoError ': State TopInfo ': Output (Message ()) ': EvalEffects) r =>   REPLExpr 'CTestProp ->   Sem r () handleTest (TestProp t) = do@@ -817,7 +863,7 @@   tydefs <- use @TopInfo (replModInfo . to allTydefs)   inputToState . inputTopEnv $ do     r <- runTest 100 at -- XXX make configurable-    info $ runInputConst tydefs . runReader initPA $ nest 2 $ "-" <+> prettyTestResult at r+    info $ runInputConst tydefs . runReader initPA $ indent 2 . nest 2 $ "-" <+> prettyTestResult at r  ------------------------------------------------------------ -- :type@@ -825,17 +871,17 @@ typeCheckCmd :: REPLCommand 'CTypeCheck typeCheckCmd =   REPLCommand-    { name = "type",-      helpcmd = ":type <term>",-      shortHelp = "Typecheck a term",-      category = Dev,-      cmdtype = ColonCmd,-      action = \x -> inputToState @TopInfo . handleTypeCheck $ x,-      parser = parseTypeCheck+    { name = "type"+    , helpcmd = ":type <term>"+    , shortHelp = "Typecheck a term"+    , category = Dev+    , cmdtype = ColonCmd+    , action = inputToState @TopInfo . handleTypeCheck+    , parser = parseTypeCheck     }  handleTypeCheck ::-  Members '[Error DiscoError, Input TopInfo, LFresh, Output Message] r =>+  Members '[Error DiscoError, Input TopInfo, LFresh, Output (Message ())] r =>   REPLExpr 'CTypeCheck ->   Sem r () handleTypeCheck (TypeCheck t) = do@@ -858,7 +904,7 @@  parseNakedOpPrim :: Parser Prim parseNakedOpPrim = sc *> choice (map mkOpParser (concat opTable))-  where-    mkOpParser :: OpInfo -> Parser Prim-    mkOpParser (OpInfo (UOpF _ op) syns _) = choice (map ((PrimUOp op <$) . reservedOp) syns)-    mkOpParser (OpInfo (BOpF _ op) syns _) = choice (map ((PrimBOp op <$) . reservedOp) syns)+ where+  mkOpParser :: OpInfo -> Parser Prim+  mkOpParser (OpInfo (UOpF _ op) syns _) = choice (map ((PrimUOp op <$) . reservedOp) syns)+  mkOpParser (OpInfo (BOpF _ op) syns _) = choice (map ((PrimBOp op <$) . reservedOp) syns)
src/Disco/Interpret/CESK.hs view
@@ -2,6 +2,9 @@ {-# OPTIONS_GHC -fmax-pmcheck-models=200 #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Interpret.CESK -- Copyright   :  disco team and contributors@@ -10,55 +13,59 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- CESK machine interpreter for Disco.--------------------------------------------------------------------------------module Disco.Interpret.CESK-  ( CESK,-    runCESK,-    step,-    eval,-    runTest,-  )+module Disco.Interpret.CESK (+  CESK,+  runCESK,+  step,+  eval,+  runTest,+) where -import           Unbound.Generics.LocallyNameless   (Bind, Name)+import Unbound.Generics.LocallyNameless (Bind, Name) -import           Algebra.Graph-import qualified Algebra.Graph.AdjacencyMap         as AdjMap-import           Control.Arrow                      ((***), (>>>))-import           Control.Monad                      ((>=>))-import           Data.Bifunctor                     (first, second)-import           Data.List                          (find)-import qualified Data.Map                           as M-import           Data.Maybe                         (isJust)-import           Data.Ratio-import           Disco.AST.Core-import           Disco.AST.Generic                  (Ellipsis (..), Side (..),-                                                     selectSide)-import           Disco.AST.Typed                    (AProperty)-import           Disco.Compile-import           Disco.Context                      as Ctx-import           Disco.Enumerate-import           Disco.Error-import           Disco.Names-import           Disco.Property-import           Disco.Types                        hiding (V)-import           Disco.Value-import           Math.Combinatorics.Exact.Binomial  (choose)-import           Math.Combinatorics.Exact.Factorial (factorial)-import           Math.NumberTheory.Primes           (factorise, unPrime)-import           Math.NumberTheory.Primes.Testing   (isPrime)-import           Math.OEIS                          (catalogNums,-                                                     extendSequence,-                                                     lookupSequence)+import Algebra.Graph+import qualified Algebra.Graph.AdjacencyMap as AdjMap+import Control.Arrow ((***), (>>>))+import Control.Monad ((>=>))+import Data.Bifunctor (first, second)+import Data.List (find)+import qualified Data.Map as M+import Data.Maybe (isJust)+import Data.Ratio+import Disco.AST.Core+import Disco.AST.Generic (+  Ellipsis (..),+  Side (..),+  selectSide,+ )+import Disco.AST.Typed (AProperty)+import Disco.Compile+import Disco.Context as Ctx+import Disco.Enumerate+import Disco.Error+import Disco.Names+import Disco.Property+import Disco.Types hiding (V)+import Disco.Value+import Math.Combinatorics.Exact.Binomial (choose)+import Math.Combinatorics.Exact.Factorial (factorial)+import Math.NumberTheory.Primes (factorise, unPrime)+import Math.NumberTheory.Primes.Testing (isPrime) -import           Disco.Effects.Fresh-import           Disco.Effects.Input-import           Disco.Effects.Random-import           Polysemy-import           Polysemy.Error-import           Polysemy.State+-- import Math.OEIS (+--   catalogNums,+--   extendSequence,+--   lookupSequence,+--  ) +import Disco.Effects.Fresh+import Disco.Effects.Input+import Disco.Effects.Random+import Polysemy+import Polysemy.Error+import Polysemy.State+ ------------------------------------------------------------ -- Utilities ------------------------------------------------------------@@ -141,22 +148,22 @@  -- | Is the CESK machine in a final state? isFinal :: CESK -> Maybe (Either EvalError Value)-isFinal (Up e [])  = Just (Left e)+isFinal (Up e []) = Just (Left e) isFinal (Out v []) = Just (Right v)-isFinal _          = Nothing+isFinal _ = Nothing  -- | Run a CESK machine to completion. runCESK :: Members '[Fresh, Random, State Mem] r => CESK -> Sem r (Either EvalError Value) runCESK cesk = case isFinal cesk of   Just res -> return res-  Nothing  -> step cesk >>= runCESK+  Nothing -> step cesk >>= runCESK  -- | Advance the CESK machine by one step. step :: Members '[Fresh, Random, State Mem] r => CESK -> Sem r CESK step cesk = case cesk of   (In (CVar x) e k) -> case Ctx.lookup' x e of     Nothing -> return $ Up (UnboundError x) k-    Just v  -> return $ Out v k+    Just v -> return $ Out v k   (In (CNum d r) _ k) -> return $ Out (VNum d r) k   (In (CConst OMatchErr) _ k) -> return $ Up NonExhaustive k   (In (CConst OEmptyGraph) _ k) -> return $ Out (VGraph empty) k@@ -177,7 +184,6 @@     return $ Out (foldr (VPair . VRef) VUnit locs) k   (In (CForce c) e k) -> return $ In c e (FForce : k)   (In (CTest vars c) e k) -> return $ In c e (FTest (TestVars vars) e : k)-   (Out v (FInj s : k)) -> return $ Out (VInj s v) k   (Out (VInj L v) (FCase e b1 _ : k)) -> do     (x, c1) <- unbind b1@@ -200,7 +206,6 @@   (Out (VClo e (x : xs) b) (FArgV v : k)) -> return $ Out (VClo (Ctx.insert (localName x) v e) xs b) k   (Out (VConst op) (FArgV v : k)) -> appConst k op v   (Out (VFun f) (FArgV v : k)) -> return $ Out (f v) k-   (Out (VRef n) (FForce : k)) -> do     cell <- lkup n     case cell of@@ -213,18 +218,15 @@   (Out v (FUpdate n : k)) -> do     set n (V v)     return $ Out v k--  (Up err (f@FTest{} : k)) ->+  (Up err (f@FTest {} : k)) ->     return $ Out (VProp (VPDone (TestResult False (TestRuntimeError err) emptyTestEnv))) (f : k)   (Up err (_ : ks)) -> return $ Up err ks-   (Out v (FTest vs e : k)) -> do     let result = ensureProp v-        res    = getTestEnv vs e+        res = getTestEnv vs e     case res of       Left err -> return $ Up err k       Right e' -> return $ Out (VProp $ extendPropEnv e' result) k-   _ -> error "Impossible! Bad CESK machine state"  ------------------------------------------------------------@@ -233,15 +235,18 @@  arity2 :: (Value -> Value -> a) -> Value -> a arity2 f (VPair x y) = f x y-arity2 _f _v         = error "arity2 on a non-pair!"+arity2 _f _v = error "arity2 on a non-pair!"  arity3 :: (Value -> Value -> Value -> a) -> Value -> a arity3 f (VPair x (VPair y z)) = f x y z-arity3 _f _v                   = error "arity3 on a non-triple!"+arity3 _f _v = error "arity3 on a non-triple!" -appConst-  :: Members '[Random, State Mem] r-  => Cont -> Op -> Value -> Sem r CESK+appConst ::+  Members '[Random, State Mem] r =>+  Cont ->+  Op ->+  Value ->+  Sem r CESK appConst k = \case   --------------------------------------------------   -- Basics@@ -259,80 +264,80 @@   OAbs -> numOp1 abs >=> out   OMul -> numOp2 (*) >=> out   ODiv -> numOp2' divOp >>> outWithErr-    where-      divOp :: Member (Error EvalError) r => Rational -> Rational -> Sem r Value-      divOp _ 0 = throw DivByZero-      divOp m n = return $ ratv (m / n)+   where+    divOp :: Member (Error EvalError) r => Rational -> Rational -> Sem r Value+    divOp _ 0 = throw DivByZero+    divOp m n = return $ ratv (m / n)   OExp -> numOp2 (\m n -> m ^^ numerator n) >=> out   OMod -> numOp2' modOp >>> outWithErr-    where-      modOp :: Member (Error EvalError) r => Rational -> Rational -> Sem r Value-      modOp m n-        | n == 0 = throw DivByZero-        | otherwise = return $ intv (numerator m `mod` numerator n)+   where+    modOp :: Member (Error EvalError) r => Rational -> Rational -> Sem r Value+    modOp m n+      | n == 0 = throw DivByZero+      | otherwise = return $ intv (numerator m `mod` numerator n)   ODivides -> numOp2' (\m n -> return (enumv $ divides m n)) >=> out-    where-      divides 0 0 = True-      divides 0 _ = False-      divides x y = denominator (y / x) == 1+   where+    divides 0 0 = True+    divides 0 _ = False+    divides x y = denominator (y / x) == 1    --------------------------------------------------   -- Number theory    OIsPrime -> intOp1 (enumv . isPrime) >=> out   OFactor -> intOp1' primFactor >>> outWithErr-    where-      -- Semantics of the @$factor@ prim: turn a natural number into its-      -- bag of prime factors.  Crash if called on 0, which does not have-      -- a prime factorization.-      primFactor :: Member (Error EvalError) r => Integer -> Sem r Value-      primFactor 0 = throw (Crash "0 has no prime factorization!")-      primFactor n = return . VBag $ map ((intv . unPrime) *** fromIntegral) (factorise n)+   where+    -- Semantics of the @$factor@ prim: turn a natural number into its+    -- bag of prime factors.  Crash if called on 0, which does not have+    -- a prime factorization.+    primFactor :: Member (Error EvalError) r => Integer -> Sem r Value+    primFactor 0 = throw (Crash "0 has no prime factorization!")+    primFactor n = return . VBag $ map ((intv . unPrime) *** fromIntegral) (factorise n)   OFrac -> numOp1' (return . primFrac) >=> out-    where-      -- Semantics of the @$frac@ prim: turn a rational number into a pair-      -- of its numerator and denominator.-      primFrac :: Rational -> Value-      primFrac r = VPair (intv (numerator r)) (intv (denominator r))+   where+    -- Semantics of the @$frac@ prim: turn a rational number into a pair+    -- of its numerator and denominator.+    primFrac :: Rational -> Value+    primFrac r = VPair (intv (numerator r)) (intv (denominator r))    --------------------------------------------------   -- Combinatorics    OMultinom -> arity2 multinomOp >=> out-    where-      multinomOp :: Value -> Value -> Sem r Value-      multinomOp (vint -> n0) (vlist vint -> ks0) = return . intv $ multinomial n0 ks0-        where-          multinomial :: Integer -> [Integer] -> Integer-          multinomial _ [] = 1-          multinomial n (k' : ks)-            | k' > n = 0-            | otherwise = choose n k' * multinomial (n - k') ks+   where+    multinomOp :: Value -> Value -> Sem r Value+    multinomOp (vint -> n0) (vlist vint -> ks0) = return . intv $ multinomial n0 ks0+     where+      multinomial :: Integer -> [Integer] -> Integer+      multinomial _ [] = 1+      multinomial n (k' : ks)+        | k' > n = 0+        | otherwise = choose n k' * multinomial (n - k') ks   OFact -> numOp1' factOp >>> outWithErr-    where-      factOp :: Member (Error EvalError) r => Rational -> Sem r Value-      factOp (numerator -> n)-        | n > fromIntegral (maxBound :: Int) = throw Overflow-        | otherwise = return . intv $ factorial (fromIntegral n)+   where+    factOp :: Member (Error EvalError) r => Rational -> Sem r Value+    factOp (numerator -> n)+      | n > fromIntegral (maxBound :: Int) = throw Overflow+      | otherwise = return . intv $ factorial (fromIntegral n)   OEnum -> out . enumOp-    where-      enumOp :: Value -> Value-      enumOp (VType ty) = listv id (enumerateType ty)-      enumOp v          = error $ "Impossible! enumOp on non-type " ++ show v+   where+    enumOp :: Value -> Value+    enumOp (VType ty) = listv id (enumerateType ty)+    enumOp v = error $ "Impossible! enumOp on non-type " ++ show v   OCount -> out . countOp-    where-      countOp :: Value -> Value-      countOp (VType ty) = case countType ty of-        Just num -> VInj R (intv num)-        Nothing  -> VNil-      countOp v = error $ "Impossible! countOp on non-type " ++ show v+   where+    countOp :: Value -> Value+    countOp (VType ty) = case countType ty of+      Just num -> VInj R (intv num)+      Nothing -> VNil+    countOp v = error $ "Impossible! countOp on non-type " ++ show v    --------------------------------------------------   -- Sequences    OUntil -> arity2 $ \v1 -> out . ellipsis (Until v1)-  OLookupSeq -> out . oeisLookup-  OExtendSeq -> out . oeisExtend+  -- OLookupSeq -> out . oeisLookup+  -- OExtendSeq -> out . oeisExtend   --------------------------------------------------   -- Comparison @@ -342,40 +347,37 @@   -- Container operations    OPower -> withBag OPower $ out . VBag . sortNCount . map (first VBag) . choices-    where-      choices :: [(Value, Integer)] -> [([(Value, Integer)], Integer)]-      choices [] = [([], 1)]-      choices ((x, n) : xs) = xs' ++ concatMap (\k' -> map (cons n (x, k')) xs') [1 .. n]-        where-          xs' = choices xs-      cons n (x, k') (zs, m) = ((x, k') : zs, choose n k' * m)-  OBagElem -> arity2 $ \x -> withBag OBagElem $-    out . enumv . isJust . find (valEq x) . map fst+   where+    choices :: [(Value, Integer)] -> [([(Value, Integer)], Integer)]+    choices [] = [([], 1)]+    choices ((x, n) : xs) = xs' ++ concatMap (\k' -> map (cons n (x, k')) xs') [1 .. n]+     where+      xs' = choices xs+    cons n (x, k') (zs, m) = ((x, k') : zs, choose n k' * m)+  OBagElem -> arity2 $ \x ->+    withBag OBagElem $+      out . enumv . isJust . find (valEq x) . map fst   OListElem -> arity2 $ \x -> out . enumv . isJust . find (valEq x) . vlist id--  OEachSet -> arity2 $ \f -> withBag OEachSet $-    outWithErr . fmap (VBag . countValues) . mapM (evalApp f . (:[]) . fst)--  OEachBag -> arity2 $ \f -> withBag OEachBag $-    outWithErr . fmap (VBag . sortNCount) . mapM (\(x,n) -> (,n) <$> evalApp f [x])-+  OEachSet -> arity2 $ \f ->+    withBag OEachSet $+      outWithErr . fmap (VBag . countValues) . mapM (evalApp f . (: []) . fst)+  OEachBag -> arity2 $ \f ->+    withBag OEachBag $+      outWithErr . fmap (VBag . sortNCount) . mapM (\(x, n) -> (,n) <$> evalApp f [x])   OFilterBag -> arity2 $ \f -> withBag OFilterBag $ \xs ->     outWithErr $ do-      bs <- mapM (evalApp f . (:[]) . fst) xs+      bs <- mapM (evalApp f . (: []) . fst) xs       return . VBag . map snd . Prelude.filter (isTrue . fst) $ zip bs xs-    where-      isTrue (VInj R VUnit) = True-      isTrue _              = False-+   where+    isTrue (VInj R VUnit) = True+    isTrue _ = False   OMerge -> arity3 $ \f bxs bys ->     case (bxs, bys) of       (VBag xs, VBag ys) -> outWithErr (VBag <$> mergeM f xs ys)       (VBag _, _) -> error $ "Impossible! OMerge on non-VBag " ++ show bys-      _           -> error $ "Impossible! OMerge on non-VBag " ++ show bxs-+      _ -> error $ "Impossible! OMerge on non-VBag " ++ show bxs   OBagUnions -> withBag OBagUnions $ \cts ->-    out . VBag $ sortNCount [(x, m*n) | (VBag xs, n) <- cts, (x,m) <- xs]-+    out . VBag $ sortNCount [(x, m * n) | (VBag xs, n) <- cts, (x, m) <- xs]   --------------------------------------------------   -- Container conversions @@ -392,83 +394,91 @@   --   Disco> :desugar let x = 3 in ⟅ 'a' # (2 + x), 'b', 'b' ⟆   --   (λx. bagFromCounts(bag(('a', 2 + x) :: ('b', 1) :: ('b', 1) :: [])))(3) -  OCountsToBag -> withBag OCountsToBag $-    out . VBag . sortNCount . map (second (uncurry (*)) . assoc . first (vpair id vint))-    where-      assoc ((a, b), c) = (a, (b, c))--  OUnsafeCountsToBag -> withBag OUnsafeCountsToBag $-    out . VBag . map (second (uncurry (*)) . assoc . first (vpair id vint))-    where-      assoc ((a, b), c) = (a, (b, c))+  OCountsToBag ->+    withBag OCountsToBag $+      out . VBag . sortNCount . map (second (uncurry (*)) . assoc . first (vpair id vint))+   where+    assoc ((a, b), c) = (a, (b, c))+  OUnsafeCountsToBag ->+    withBag OUnsafeCountsToBag $+      out . VBag . map (second (uncurry (*)) . assoc . first (vpair id vint))+   where+    assoc ((a, b), c) = (a, (b, c))    --------------------------------------------------   -- Maps -  OMapToSet -> withMap OMapToSet $-    out . VBag . map (\(k',v) -> (VPair (fromSimpleValue k') v, 1)) . M.assocs--  OSetToMap -> withBag OSetToMap $-    out . VMap . M.fromList . map (convertAssoc . fst)-    where-      convertAssoc (VPair k' v) = (toSimpleValue k', v)-      convertAssoc v = error $ "Impossible! convertAssoc on non-VPair " ++ show v--  OInsert -> arity3 $ \k' v -> withMap OInsert $-    out . VMap . M.insert (toSimpleValue k') v--  OLookup -> arity2 $ \k' -> withMap OLookup $-    out . toMaybe . M.lookup (toSimpleValue k')-    where-      toMaybe = maybe (VInj L VUnit) (VInj R)+  OMapToSet ->+    withMap OMapToSet $+      out . VBag . map (\(k', v) -> (VPair (fromSimpleValue k') v, 1)) . M.assocs+  OSetToMap ->+    withBag OSetToMap $+      out . VMap . M.fromList . map (convertAssoc . fst)+   where+    convertAssoc (VPair k' v) = (toSimpleValue k', v)+    convertAssoc v = error $ "Impossible! convertAssoc on non-VPair " ++ show v+  OInsert -> arity3 $ \k' v ->+    withMap OInsert $+      out . VMap . M.insert (toSimpleValue k') v+  OLookup -> arity2 $ \k' ->+    withMap OLookup $+      out . toMaybe . M.lookup (toSimpleValue k')+   where+    toMaybe = maybe (VInj L VUnit) (VInj R)    --------------------------------------------------   -- Graph operations -  OVertex  -> out . VGraph . Vertex . toSimpleValue+  OVertex -> out . VGraph . Vertex . toSimpleValue   OOverlay -> arity2 $ withGraph2 OOverlay $ \g1 g2 ->     out $ VGraph (Overlay g1 g2)   OConnect -> arity2 $ withGraph2 OConnect $ \g1 g2 ->     out $ VGraph (Connect g1 g2)   OSummary -> withGraph OSummary $ out . graphSummary-   --------------------------------------------------   -- Propositions -  OForall tys -> out . (\v -> VProp (VPSearch SMForall tys v emptyTestEnv ))-  OExists tys -> out . (\v -> VProp (VPSearch SMExists tys v emptyTestEnv ))+  OForall tys -> out . (\v -> VProp (VPSearch SMForall tys v emptyTestEnv))+  OExists tys -> out . (\v -> VProp (VPSearch SMExists tys v emptyTestEnv))   OHolds -> testProperty Exhaustive >=> resultToBool >>> outWithErr   ONotProp -> out . VProp . notProp . ensureProp   OShouldEq ty -> arity2 $ \v1 v2 ->     out $ VProp (VPDone (TestResult (valEq v1 v2) (TestEqual ty v1 v2) emptyTestEnv))-+  OShouldLt ty -> arity2 $ \v1 v2 ->+    out $ VProp (VPDone (TestResult (valLt v1 v2) (TestLt ty v1 v2) emptyTestEnv))+  OAnd -> arity2 $ \p1 p2 ->+    out $ VProp (VPBin LAnd (ensureProp p1) (ensureProp p2))+  OOr -> arity2 $ \p1 p2 ->+    out $ VProp (VPBin LOr (ensureProp p1) (ensureProp p2))+  OImpl -> arity2 $ \p1 p2 ->+    out $ VProp (VPBin LImpl (ensureProp p1) (ensureProp p2))   c -> error $ "Unimplemented: appConst " ++ show c-  where-    outWithErr :: Sem (Error EvalError ': r) Value -> Sem r CESK-    outWithErr m = either (`Up` k) (`Out` k) <$> runError m-    out v = return $ Out v k-    up e  = return $ Up e k+ where+  outWithErr :: Sem (Error EvalError ': r) Value -> Sem r CESK+  outWithErr m = either (`Up` k) (`Out` k) <$> runError m+  out v = return $ Out v k+  up e = return $ Up e k -    withBag :: Op -> ([(Value,Integer)] -> Sem r a) -> Value -> Sem r a-    withBag op f = \case-      VBag xs -> f xs-      v       -> error $ "Impossible! " ++ show op ++ " on non-VBag " ++ show v+  withBag :: Op -> ([(Value, Integer)] -> Sem r a) -> Value -> Sem r a+  withBag op f = \case+    VBag xs -> f xs+    v -> error $ "Impossible! " ++ show op ++ " on non-VBag " ++ show v -    withMap :: Op -> (M.Map SimpleValue Value -> Sem r a) -> Value -> Sem r a-    withMap op f = \case-      VMap m -> f m-      v      -> error $ "Impossible! " ++ show op ++ " on non-VMap " ++ show v+  withMap :: Op -> (M.Map SimpleValue Value -> Sem r a) -> Value -> Sem r a+  withMap op f = \case+    VMap m -> f m+    v -> error $ "Impossible! " ++ show op ++ " on non-VMap " ++ show v -    withGraph :: Op -> (Graph SimpleValue -> Sem r a) -> Value -> Sem r a-    withGraph op f = \case-      VGraph g -> f g-      v        -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v+  withGraph :: Op -> (Graph SimpleValue -> Sem r a) -> Value -> Sem r a+  withGraph op f = \case+    VGraph g -> f g+    v -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v -    withGraph2 :: Op -> (Graph SimpleValue -> Graph SimpleValue -> Sem r a) -> Value -> Value -> Sem r a-    withGraph2 op f v1 v2 = case (v1, v2) of-      (VGraph g1, VGraph g2) -> f g1 g2-      (_, VGraph _) -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v1-      _             -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v2+  withGraph2 :: Op -> (Graph SimpleValue -> Graph SimpleValue -> Sem r a) -> Value -> Value -> Sem r a+  withGraph2 op f v1 v2 = case (v1, v2) of+    (VGraph g1, VGraph g2) -> f g1 g2+    (_, VGraph _) -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v1+    _ -> error $ "Impossible! " ++ show op ++ " on non-VGraph " ++ show v2  -------------------------------------------------- -- Arithmetic@@ -484,7 +494,7 @@  numOp1' :: (Rational -> Sem r Value) -> Value -> Sem r Value numOp1' f (VNum _ m) = f m-numOp1' _ v          = error $ "Impossible! numOp1' on non-VNum " ++ show v+numOp1' _ v = error $ "Impossible! numOp1' on non-VNum " ++ show v  numOp2 :: (Rational -> Rational -> Rational) -> Value -> Sem r Value numOp2 (#) = numOp2' $ \m n -> return (ratv (m # n))@@ -496,9 +506,9 @@       res <- n1 # n2       case res of         VNum _ r -> return $ VNum (d1 <> d2) r-        _        -> return res-    (VNum{}, _) -> error $ "Impossible! numOp2' on non-VNum " ++ show v2-    _           -> error $ "Impossible! numOp2' on non-VNum " ++ show v1+        _ -> return res+    (VNum {}, _) -> error $ "Impossible! numOp2' on non-VNum " ++ show v2+    _ -> error $ "Impossible! numOp2' on non-VNum " ++ show v1  -- | Perform a square root operation. If the program typechecks, --   then the argument and output will really be Natural.@@ -551,15 +561,15 @@ compareBags [] [] = EQ compareBags [] _ = LT compareBags _ [] = GT-compareBags ((x, xn) : xs) ((y, yn) : ys)-  = valCmp x y <> compare xn yn <> compareBags xs ys+compareBags ((x, xn) : xs) ((y, yn) : ys) =+  valCmp x y <> compare xn yn <> compareBags xs ys  compareMaps :: [(SimpleValue, Value)] -> [(SimpleValue, Value)] -> Ordering compareMaps [] [] = EQ compareMaps [] _ = LT compareMaps _ [] = GT-compareMaps ((k1, v1) : as1) ((k2, v2) : as2)-  = valCmp (fromSimpleValue k1) (fromSimpleValue k2) <> valCmp v1 v2 <> compareMaps as1 as2+compareMaps ((k1, v1) : as1) ((k2, v2) : as2) =+  valCmp (fromSimpleValue k1) (fromSimpleValue k2) <> valCmp v1 v2 <> compareMaps as1 as2  ------------------------------------------------------------ -- Polynomial sequences [a,b,c,d .. e]@@ -577,16 +587,16 @@   | d > 0 = takeWhile (<= y) nums   | d < 0 = takeWhile (>= y) nums   | otherwise = nums-  where-    d = constdiff xs-    nums = babbage xs+ where+  d = constdiff xs+  nums = babbage xs  -- | Extend a sequence infinitely by interpolating it as a polynomial --   sequence, via forward differences.  Essentially the same --   algorithm used by Babbage's famous Difference Engine. babbage :: Num a => [a] -> [a]-babbage []       = []-babbage [x]      = repeat x+babbage [] = []+babbage [x] = repeat x babbage (x : xs) = scanl (+) x (babbage (diff (x : xs)))  -- | Compute the forward difference of the given sequence, that is,@@ -607,23 +617,23 @@ -- OEIS ------------------------------------------------------------ --- | Looks up a sequence of integers in OEIS.---   Returns 'left()' if the sequence is unknown in OEIS,---   otherwise 'right "https://oeis.org/<oeis_sequence_id>"'-oeisLookup :: Value -> Value-oeisLookup (vlist vint -> ns) = maybe VNil parseResult (lookupSequence ns)-  where-    parseResult r = VInj R (listv charv ("https://oeis.org/" ++ seqNum r))-    seqNum = getCatalogNum . catalogNums+-- -- | Looks up a sequence of integers in OEIS.+-- --   Returns 'left()' if the sequence is unknown in OEIS,+-- --   otherwise 'right "https://oeis.org/<oeis_sequence_id>"'+-- oeisLookup :: Value -> Value+-- oeisLookup (vlist vint -> ns) = maybe VNil parseResult (lookupSequence ns)+--  where+--   parseResult r = VInj R (listv charv ("https://oeis.org/" ++ seqNum r))+--   seqNum = getCatalogNum . catalogNums -    getCatalogNum []      = error "No catalog info"-    getCatalogNum (n : _) = n+--   getCatalogNum [] = error "No catalog info"+--   getCatalogNum (n : _) = n --- | Extends a Disco integer list with data from a known OEIS---   sequence.  Returns a list of integers upon success, otherwise the---   original list (unmodified).-oeisExtend :: Value -> Value-oeisExtend = listv intv . extendSequence . vlist vint+-- -- | Extends a Disco integer list with data from a known OEIS+-- --   sequence.  Returns a list of integers upon success, otherwise the+-- --   original list (unmodified).+-- oeisExtend :: Value -> Value+-- oeisExtend = listv intv . extendSequence . vlist vint  ------------------------------------------------------------ -- Normalizing bags/sets@@ -645,8 +655,8 @@ sortNCount [] = [] sortNCount [x] = [x] sortNCount xs = merge (+) (sortNCount firstHalf) (sortNCount secondHalf)-  where-    (firstHalf, secondHalf) = splitAt (length xs `div` 2) xs+ where+  (firstHalf, secondHalf) = splitAt (length xs `div` 2) xs  -- | Generic function for merging two sorted, count-annotated lists of --   type @[(a,Integer)]@ a la merge sort, using the given comparison@@ -660,18 +670,18 @@   [(Value, Integer)] ->   [(Value, Integer)] merge g = go-  where-    go [] [] = []-    go [] ((y, n) : ys) = mergeCons y 0 n (go [] ys)-    go ((x, n) : xs) [] = mergeCons x n 0 (go xs [])-    go ((x, n1) : xs) ((y, n2) : ys) = case valCmp x y of-      LT -> mergeCons x n1 0 (go xs ((y, n2) : ys))-      EQ -> mergeCons x n1 n2 (go xs ys)-      GT -> mergeCons y 0 n2 (go ((x, n1) : xs) ys)+ where+  go [] [] = []+  go [] ((y, n) : ys) = mergeCons y 0 n (go [] ys)+  go ((x, n) : xs) [] = mergeCons x n 0 (go xs [])+  go ((x, n1) : xs) ((y, n2) : ys) = case valCmp x y of+    LT -> mergeCons x n1 0 (go xs ((y, n2) : ys))+    EQ -> mergeCons x n1 n2 (go xs ys)+    GT -> mergeCons y 0 n2 (go ((x, n1) : xs) ys) -    mergeCons a m1 m2 zs = case g m1 m2 of-      0 -> zs-      n -> (a, n) : zs+  mergeCons a m1 m2 zs = case g m1 m2 of+    0 -> zs+    n -> (a, n) : zs  mergeM ::   Members '[Random, Error EvalError, State Mem] r =>@@ -680,21 +690,21 @@   [(Value, Integer)] ->   Sem r [(Value, Integer)] mergeM g = go-  where-    go [] [] = return []-    go [] ((y, n) : ys) = mergeCons y 0 n =<< go [] ys-    go ((x, n) : xs) [] = mergeCons x n 0 =<< go xs []-    go ((x, n1) : xs) ((y, n2) : ys) = case valCmp x y of-      LT -> mergeCons x n1 0 =<< go xs ((y, n2) : ys)-      EQ -> mergeCons x n1 n2 =<< go xs ys-      GT -> mergeCons y 0 n2 =<< go ((x, n1) : xs) ys+ where+  go [] [] = return []+  go [] ((y, n) : ys) = mergeCons y 0 n =<< go [] ys+  go ((x, n) : xs) [] = mergeCons x n 0 =<< go xs []+  go ((x, n1) : xs) ((y, n2) : ys) = case valCmp x y of+    LT -> mergeCons x n1 0 =<< go xs ((y, n2) : ys)+    EQ -> mergeCons x n1 n2 =<< go xs ys+    GT -> mergeCons y 0 n2 =<< go ((x, n1) : xs) ys -    mergeCons a m1 m2 zs = do-      nm <- evalApp g [VPair (intv m1) (intv m2)]-      return $ case nm of-        VNum _ 0 -> zs-        VNum _ n -> (a, numerator n) : zs-        v -> error $ "Impossible! merge function in mergeM returned non-VNum " ++ show v+  mergeCons a m1 m2 zs = do+    nm <- evalApp g [VPair (intv m1) (intv m2)]+    return $ case nm of+      VNum _ 0 -> zs+      VNum _ n -> (a, numerator n) : zs+      v -> error $ "Impossible! merge function in mergeM returned non-VNum " ++ show v  ------------------------------------------------------------ -- Graphs@@ -702,14 +712,14 @@  graphSummary :: Graph SimpleValue -> Value graphSummary = toDiscoAdjMap . reifyGraph-  where-    reifyGraph :: Graph SimpleValue -> [(SimpleValue, [SimpleValue])]-    reifyGraph =-      AdjMap.adjacencyList . foldg AdjMap.empty AdjMap.vertex AdjMap.overlay AdjMap.connect+ where+  reifyGraph :: Graph SimpleValue -> [(SimpleValue, [SimpleValue])]+  reifyGraph =+    AdjMap.adjacencyList . foldg AdjMap.empty AdjMap.vertex AdjMap.overlay AdjMap.connect -    toDiscoAdjMap :: [(SimpleValue, [SimpleValue])] -> Value-    toDiscoAdjMap =-      VMap . M.fromList . map (second (VBag . countValues . map fromSimpleValue))+  toDiscoAdjMap :: [(SimpleValue, [SimpleValue])] -> Value+  toDiscoAdjMap =+    VMap . M.fromList . map (second (VBag . countValues . map fromSimpleValue))  ------------------------------------------------------------ -- Propositions / tests@@ -717,66 +727,87 @@  resultToBool :: Member (Error EvalError) r => TestResult -> Sem r Value resultToBool (TestResult _ (TestRuntimeError e) _) = throw e-resultToBool (TestResult b _ _)                    = return $ enumv b+resultToBool (TestResult b _ _) = return $ enumv b  notProp :: ValProp -> ValProp-notProp (VPDone r)            = VPDone (invertPropResult r)+notProp (VPDone r) = VPDone (invertPropResult r) notProp (VPSearch sm tys p e) = VPSearch (invertMotive sm) tys p e+notProp (VPBin LAnd vp1 vp2) = VPBin LOr (notProp vp1) (notProp vp2)+notProp (VPBin LOr vp1 vp2) = VPBin LAnd (notProp vp1) (notProp vp2)+notProp (VPBin LImpl vp1 vp2) = VPBin LAnd vp1 (notProp vp2)  -- | Convert a @Value@ to a @ValProp@, embedding booleans if necessary. ensureProp :: Value -> ValProp-ensureProp (VProp p)  = p+ensureProp (VProp p) = p ensureProp (VInj L _) = VPDone (TestResult False TestBool emptyTestEnv) ensureProp (VInj R _) = VPDone (TestResult True TestBool emptyTestEnv)-ensureProp _          = error "ensureProp: non-prop value"+ensureProp _ = error "ensureProp: non-prop value" -testProperty-  :: Members '[Random, State Mem] r-  => SearchType -> Value -> Sem r TestResult+combineTestResultBool :: LOp -> TestResult -> TestResult -> Bool+combineTestResultBool op (TestResult b1 _ _) (TestResult b2 _ _) = interpLOp op b1 b2++testProperty ::+  Members '[Random, State Mem] r =>+  SearchType ->+  Value ->+  Sem r TestResult testProperty initialSt = checkProp . ensureProp-  where-    checkProp-      :: Members '[Random, State Mem] r-      => ValProp -> Sem r TestResult-    checkProp (VPDone r) = return r-    checkProp (VPSearch sm tys f e) =-      extendResultEnv e <$> (generateSamples initialSt vals >>= go)-      where-        vals = enumTypes tys-        (SearchMotive (whenFound, wantsSuccess)) = sm+ where+  checkProp ::+    Members '[Random, State Mem] r =>+    ValProp ->+    Sem r TestResult+  checkProp (VPDone r) = return r+  checkProp (VPBin op vp1 vp2) = do+    tr1 <- checkProp vp1+    tr2 <- checkProp vp2+    return $ TestResult (combineTestResultBool op tr1 tr2) (TestBin op tr1 tr2) emptyTestEnv+  checkProp (VPSearch sm tys f e) =+    extendResultEnv e <$> (generateSamples initialSt vals >>= go)+   where+    vals = enumTypes tys+    (SearchMotive (whenFound, wantsSuccess)) = sm -        go-          :: Members '[Random, State Mem] r-          => ([[Value]], SearchType) -> Sem r TestResult-        go ([], st)   = return $ TestResult (not whenFound) (TestNotFound st) emptyTestEnv-        go (x:xs, st) = do-          mprop <- runError (ensureProp <$> evalApp f x)-          case mprop of-            Left err    -> return $ TestResult False (TestRuntimeError err) emptyTestEnv-            Right (VPDone r) -> continue st xs r-            Right prop       -> checkProp prop >>= continue st xs+    go ::+      Members '[Random, State Mem] r =>+      ([[Value]], SearchType) ->+      Sem r TestResult+    go ([], st) = return $ TestResult (not whenFound) (TestNotFound st) emptyTestEnv+    go (x : xs, st) = do+      mprop <- runError (ensureProp <$> evalApp f x)+      case mprop of+        Left err -> return $ TestResult False (TestRuntimeError err) emptyTestEnv+        Right (VPDone r) -> continue st xs r+        Right prop -> checkProp prop >>= continue st xs -        continue-          :: Members '[Random, State Mem] r-          => SearchType -> [[Value]] -> TestResult -> Sem r TestResult-        continue st xs r@(TestResult _ _ e')-          | testIsError r              = return r-          | testIsOk r == wantsSuccess =-            return $ TestResult whenFound (TestFound r) e'-          | otherwise                  = go (xs, st)+    continue ::+      Members '[Random, State Mem] r =>+      SearchType ->+      [[Value]] ->+      TestResult ->+      Sem r TestResult+    continue st xs r@(TestResult _ _ e')+      | testIsError r = return r+      | testIsOk r == wantsSuccess =+          return $ TestResult whenFound (TestFound r) e'+      | otherwise = go (xs, st) -evalApp-  :: Members '[Random, Error EvalError, State Mem] r-  => Value -> [Value] -> Sem r Value+evalApp ::+  Members '[Random, Error EvalError, State Mem] r =>+  Value ->+  [Value] ->+  Sem r Value evalApp f xs =   runFresh (runCESK (Out f (map FArgV xs))) >>= either throw return -runTest-  :: Members '[Random, Error EvalError, Input Env, State Mem] r-  => Int -> AProperty -> Sem r TestResult+runTest ::+  Members '[Random, Error EvalError, Input Env, State Mem] r =>+  Int ->+  AProperty ->+  Sem r TestResult runTest n p = testProperty (Randomized n' n') =<< eval (compileProperty p)-  where-    n' = fromIntegral (n `div` 2)+ where+  n' = fromIntegral (n `div` 2)  ------------------------------------------------------------ -- Top-level evaluation
src/Disco/Messages.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE DeriveFunctor   #-}+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE TemplateHaskell #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Messages -- Copyright   :  disco team and contributors@@ -11,53 +14,50 @@ -- -- Message logging framework (e.g. for errors, warnings, etc.) for -- disco.---------------------------------------------------------------------------------- module Disco.Messages where -import           Control.Lens-import           Control.Monad   (when)-import           Polysemy-import           Polysemy.Output+import Control.Lens+import Control.Monad (when)+import Polysemy+import Polysemy.Output -import           Disco.Pretty    (Doc, Pretty, pretty', renderDoc')+import Disco.Pretty (Doc, Pretty, pretty', renderDoc')  data MessageType-    = Info-    | Warning-    | ErrMsg-    | Debug-    deriving (Show, Read, Eq, Ord, Enum, Bounded)+  = Info+  | Warning+  | ErrMsg+  | Debug+  deriving (Show, Read, Eq, Ord, Enum, Bounded) -data Message = Message {_messageType :: MessageType, _message :: Doc}-    deriving (Show)+data Message ann = Message {_messageType :: MessageType, _message :: (Doc ann)}+  deriving (Show)  makeLenses ''Message -handleMsg :: Member (Embed IO) r => (Message -> Bool) -> Message -> Sem r ()+handleMsg :: Member (Embed IO) r => (Message ann -> Bool) -> Message ann -> Sem r () handleMsg p m = when (p m) $ printMsg m -printMsg :: Member (Embed IO) r => Message -> Sem r ()+printMsg :: Member (Embed IO) r => Message ann -> Sem r () printMsg (Message _ m) = embed $ putStrLn (renderDoc' m) -msg :: Member (Output Message) r => MessageType -> Sem r Doc -> Sem r ()+msg :: Member (Output (Message ann)) r => MessageType -> Sem r (Doc ann) -> Sem r () msg typ m = m >>= output . Message typ -info :: Member (Output Message) r => Sem r Doc -> Sem r ()+info :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r () info = msg Info -infoPretty :: (Member (Output Message) r, Pretty t) => t -> Sem r ()+infoPretty :: (Member (Output (Message ann)) r, Pretty t) => t -> Sem r () infoPretty = info . pretty' -warn :: Member (Output Message) r => Sem r Doc -> Sem r ()+warn :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r () warn = msg Warning -debug :: Member (Output Message) r => Sem r Doc -> Sem r ()+debug :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r () debug = msg Debug -debugPretty :: (Member (Output Message) r, Pretty t) => t -> Sem r ()+debugPretty :: (Member (Output (Message ann)) r, Pretty t) => t -> Sem r () debugPretty = debug . pretty' -err :: Member (Output Message) r => Sem r Doc -> Sem r ()+err :: Member (Output (Message ann)) r => Sem r (Doc ann) -> Sem r () err = msg ErrMsg
src/Disco/Module.hs view
@@ -1,10 +1,13 @@-{-# LANGUAGE DeriveAnyClass       #-}-{-# LANGUAGE DeriveDataTypeable   #-}-{-# LANGUAGE StandaloneDeriving   #-}-{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Module -- Copyright   :  (c) 2019 disco team (see LICENSE)@@ -14,43 +17,52 @@ -- -- The 'ModuleInfo' record representing a disco module, and functions -- to resolve the location of a module on disk.------------------------------------------------------------------------------- module Disco.Module where -import           Data.Data                               (Data)-import           GHC.Generics                            (Generic)+import Data.Data (Data)+import GHC.Generics (Generic) -import           Control.Lens                            (Getting, foldOf,-                                                          makeLenses, view)-import           Control.Monad                           (filterM)-import           Control.Monad.IO.Class                  (MonadIO (..))-import           Data.Bifunctor                          (first)-import           Data.Map                                (Map)-import qualified Data.Map                                as M-import           Data.Maybe                              (listToMaybe)-import qualified Data.Set                                as S-import           System.Directory                        (doesFileExist)-import           System.FilePath                         (replaceExtension,-                                                          (</>))+import Control.Lens (+  Getting,+  foldOf,+  makeLenses,+  view,+ )+import Control.Monad (filterM)+import Control.Monad.IO.Class (MonadIO (..))+import Data.Bifunctor (first)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe (listToMaybe)+import qualified Data.Set as S+import System.Directory (doesFileExist)+import System.FilePath (+  replaceExtension,+  (</>),+ ) -import           Unbound.Generics.LocallyNameless        (Alpha, Bind, Name,-                                                          Subst, bind)-import           Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)+import Unbound.Generics.LocallyNameless (+  Alpha,+  Bind,+  Name,+  Subst,+  bind,+ )+import Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind) -import           Polysemy+import Polysemy -import           Disco.AST.Surface-import           Disco.AST.Typed-import           Disco.Context-import           Disco.Extensions-import           Disco.Names-import           Disco.Pretty                            hiding ((<>))-import           Disco.Typecheck.Erase                   (erase, erasePattern)-import           Disco.Typecheck.Util                    (TyCtx)-import           Disco.Types+import Disco.AST.Surface+import Disco.AST.Typed+import Disco.Context+import Disco.Extensions+import Disco.Names+import Disco.Pretty hiding ((<>))+import Disco.Typecheck.Erase (erase, erasePattern)+import Disco.Typecheck.Util (TyCtx)+import Disco.Types -import           Paths_disco+import Paths_disco  ------------------------------------------------------------ -- ModuleInfo and related types@@ -76,10 +88,10 @@   deriving (Show, Generic, Alpha, Data, Subst Type)  instance Pretty Defn where-  pretty (Defn x patTys ty clauses) = vcat $-    prettyTyDecl x (foldr (:->:) ty patTys)-    :-    map (pretty . (x,) . eraseClause) clauses+  pretty (Defn x patTys ty clauses) =+    vcat $+      prettyTyDecl x (foldr (:->:) ty patTys)+        : map (pretty . (x,) . eraseClause) clauses  -- | A clause in a definition consists of a list of patterns (the LHS --   of the =) and a term (the RHS).  For example, given the concrete@@ -89,31 +101,31 @@  eraseClause :: Clause -> Bind [Pattern] Term eraseClause b = bind (map erasePattern ps) (erase t)-  where (ps, t) = unsafeUnbind b+ where+  (ps, t) = unsafeUnbind b  -- | Type checking a module yields a value of type ModuleInfo which contains --   mapping from terms to their relavent documenation, a mapping from terms to --   properties, and a mapping from terms to their types. data ModuleInfo = ModuleInfo-  { _miName     :: ModuleName-  , _miImports  :: Map ModuleName ModuleInfo--  -- List of names declared by the module, in the order they occur-  , _miNames    :: [QName Term]-  , _miDocs     :: Ctx Term Docs-  , _miProps    :: Ctx ATerm [AProperty]-  , _miTys      :: TyCtx-  , _miTydefs   :: TyDefCtx+  { _miName :: ModuleName+  , _miImports :: Map ModuleName ModuleInfo+  , -- List of names declared by the module, in the order they occur+    _miNames :: [QName Term]+  , _miDocs :: Ctx Term Docs+  , _miProps :: Ctx ATerm [AProperty]+  , _miTys :: TyCtx+  , _miTydefs :: TyDefCtx   , _miTermdefs :: Ctx ATerm Defn-  , _miTerms    :: [(ATerm, PolyType)]-  , _miExts     :: ExtSet+  , _miTerms :: [(ATerm, PolyType)]+  , _miExts :: ExtSet   }   deriving (Show)  makeLenses ''ModuleInfo  instance Semigroup ModuleInfo where-  -- | Two ModuleInfos+  -- \| Two ModuleInfos   --   are merged by joining their doc, type, type definition, and term   --   contexts. The property context of the new module is the one   --   obtained from the second module. The name of the new module is@@ -121,8 +133,8 @@   --   earlier ones.  Note that this function should really only be used   --   for the special top-level REPL module.   ModuleInfo n1 is1 ns1 d1 _ ty1 tyd1 tm1 tms1 es1-    <> ModuleInfo _  is2 ns2 d2 p2 ty2 tyd2 tm2 tms2 es2-    = ModuleInfo+    <> ModuleInfo _ is2 ns2 d2 p2 ty2 tyd2 tm2 tms2 es2 =+      ModuleInfo         n1         (is1 <> is2)         (ns1 <> ns2)@@ -178,7 +190,7 @@ --   `:load`ed module). withStdlib :: Resolver -> Resolver withStdlib (FromDir fp) = FromDirOrStdlib fp-withStdlib r            = r+withStdlib r = r  -- | Given a module resolution mode and a raw module name, relavent --   directories are searched for the file containing the provided@@ -189,9 +201,9 @@   datadir <- liftIO getDataDir   let searchPath =         case resolver of-          FromStdlib          -> [(datadir, Stdlib)]-          FromDir dir         -> [(dir, Dir dir)]-          FromCwdOrStdlib     -> [(datadir, Stdlib), (".", Dir ".")]+          FromStdlib -> [(datadir, Stdlib)]+          FromDir dir -> [(dir, Dir dir)]+          FromCwdOrStdlib -> [(datadir, Stdlib), (".", Dir ".")]           FromDirOrStdlib dir -> [(datadir, Stdlib), (dir, Dir dir)]   let fps = map (first (</> replaceExtension modname "disco")) searchPath   fexists <- liftIO $ filterM (doesFileExist . fst) fps
src/Disco/Names.hs view
@@ -1,39 +1,48 @@-{-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE OverloadedStrings #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Names -- Copyright   :  disco team and contributors -- Maintainer  :  byorgey@gmail.com -- -- Names for modules and identifiers.---------------------------------------------------------------------------------+module Disco.Names (+  -- * Modules and their provenance+  ModuleProvenance (..),+  ModuleName (..), --- SPDX-License-Identifier: BSD-3-Clause+  -- * Names and their provenance+  NameProvenance (..),+  QName (..),+  isFree,+  localName,+  (.-), -module Disco.Names-  ( -- * Modules and their provenance-    ModuleProvenance(..), ModuleName(..)-    -- * Names and their provenance-  , NameProvenance(..), QName(..), isFree, localName, (.-)-    -- * Name-related utilities-  , fvQ, substQ, substsQ-  ) where+  -- * Name-related utilities+  fvQ,+  substQ,+  substsQ,+) where -import           Control.Lens                     (Traversal', filtered)-import           Data.Data                        (Data)-import           Data.Data.Lens                   (template)-import           Data.Typeable                    (Typeable)-import           GHC.Generics                     (Generic)-import           Prelude                          hiding ((<>))-import           System.FilePath                  (dropExtension)-import           Unbound.Generics.LocallyNameless+import Control.Lens (Traversal', filtered)+import Data.Data (Data)+import Data.Data.Lens (template)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import System.FilePath (dropExtension)+import Unbound.Generics.LocallyNameless+import Prelude hiding ((<>)) -import           Disco.Pretty-import           Disco.Types+import Disco.Pretty+import Disco.Types  ------------------------------------------------------------ -- Modules@@ -41,16 +50,19 @@  -- | Where did a module come from? data ModuleProvenance-  = Dir FilePath -- ^ From a particular directory (relative to cwd)-  | Stdlib       -- ^ From the standard library+  = -- | From a particular directory (relative to cwd)+    Dir FilePath+  | -- | From the standard library+    Stdlib   deriving (Eq, Ord, Show, Generic, Data, Alpha, Subst Type)  -- | The name of a module. data ModuleName-  = REPLModule   -- ^ The special top-level "module" consisting of-                 -- what has been entered at the REPL.-  | Named ModuleProvenance String-                 -- ^ A named module, with its name and provenance.+  = -- | The special top-level "module" consisting of+    -- what has been entered at the REPL.+    REPLModule+  | -- | A named module, with its name and provenance.+    Named ModuleProvenance String   deriving (Eq, Ord, Show, Generic, Data, Alpha, Subst Type)  ------------------------------------------------------------@@ -59,19 +71,21 @@  -- | Where did a name come from? data NameProvenance-  = LocalName                    -- ^ The name is locally bound-  | QualifiedName ModuleName     -- ^ The name is exported by the given module+  = -- | The name is locally bound+    LocalName+  | -- | The name is exported by the given module+    QualifiedName ModuleName   deriving (Eq, Ord, Show, Generic, Data, Alpha, Subst Type)  -- | A @QName@, or qualified name, is a 'Name' paired with its --   'NameProvenance'.-data QName a = QName { qnameProvenance :: NameProvenance, qname :: Name a }+data QName a = QName {qnameProvenance :: NameProvenance, qname :: Name a}   deriving (Eq, Ord, Show, Generic, Data, Alpha, Subst Type)  -- | Does this name correspond to a free variable? isFree :: QName a -> Bool isFree (QName (QualifiedName _) _) = True-isFree (QName LocalName n)         = isFreeName n+isFree (QName LocalName n) = isFreeName n  -- | Create a locally bound qualified name. localName :: Name a -> QName a@@ -88,7 +102,7 @@ -- | The @unbound-generics@ library gives us free variables for free. --   But when dealing with typed and desugared ASTs, we want all the --   free 'QName's instead of just 'Name's.-fvQ :: (Data t, Typeable e)  => Traversal' t (QName e)+fvQ :: (Data t, Typeable e) => Traversal' t (QName e) fvQ = template . filtered isFree  substQ :: Subst b a => QName b -> b -> a -> a@@ -102,10 +116,10 @@ ------------------------------------------------------------  instance Pretty ModuleName where-  pretty REPLModule        = "REPL"+  pretty REPLModule = "REPL"   pretty (Named (Dir _) s) = text (dropExtension s)-  pretty (Named Stdlib s)  = text (dropExtension s)+  pretty (Named Stdlib s) = text (dropExtension s)  instance Pretty (QName a) where-  pretty (QName LocalName x)          = pretty x+  pretty (QName LocalName x) = pretty x   pretty (QName (QualifiedName mn) x) = pretty mn <> "." <> pretty x
src/Disco/Parser.hs view
@@ -1,1093 +1,1263 @@ {-# LANGUAGE TemplateHaskell #-} --------------------------------------------------------------------------------- |--- Module      :  Disco.Parser--- Copyright   :  disco team and contributors--- Maintainer  :  byorgey@gmail.com------ SPDX-License-Identifier: BSD-3-Clause------ Parser to convert concrete Disco syntax into an (untyped, surface--- language) AST.-----------------------------------------------------------------------------------module Disco.Parser-       ( -- * Parser type and utilities-         DiscoParseError(..), Parser, runParser, withExts, indented, thenIndented--         -- * Lexer--         -- ** Basic lexemes-       , sc, lexeme, symbol, reservedOp-       , natural, reserved, reservedWords, ident--         -- ** Punctuation-       , parens, braces, angles, brackets-       , semi, comma, colon, dot, pipe-       , lambda--         -- * Disco parser--         -- ** Modules-       , wholeModule, parseModule, parseExtName, parseTopLevel, parseDecl-       , parseImport, parseModuleName--         -- ** Terms-       , term, parseTerm, parseTerm', parseExpr, parseAtom-       , parseContainer, parseEllipsis, parseContainerComp, parseQual-       , parseLet, parseTypeOp--         -- ** Case and patterns-       , parseCase, parseBranch, parseGuards, parseGuard-       , parsePattern, parseAtomicPattern--         -- ** Types-       , parseType, parseAtomicType-       , parsePolyTy-       )-       where--import           Unbound.Generics.LocallyNameless        (Name, bind, embed,-                                                          fvAny, string2Name)-import           Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)--import           Control.Monad.Combinators.Expr-import           Text.Megaparsec                         hiding (runParser)-import qualified Text.Megaparsec                         as MP-import           Text.Megaparsec.Char-import qualified Text.Megaparsec.Char.Lexer              as L--import           Control.Lens                            (makeLenses, toListOf,-                                                          use, (%=), (%~), (&),-                                                          (.=))-import           Control.Monad.State-import           Data.Char                               (isAlpha, isDigit)-import           Data.Foldable                           (asum)-import           Data.List                               (find, intercalate)-import qualified Data.Map                                as M-import           Data.Maybe                              (fromMaybe)-import           Data.Ratio-import           Data.Set                                (Set)-import qualified Data.Set                                as S--import           Disco.AST.Surface-import           Disco.Extensions-import           Disco.Module-import           Disco.Pretty                            (prettyStr)-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Types-import           Polysemy                                (run)----------------------------------------------------------------- Lexer---- Some of the basic setup code for the parser taken from--- https://markkarpov.com/megaparsec/parsing-simple-imperative-language.html---- | Currently required indent level.-data IndentMode where-  NoIndent   :: IndentMode   -- ^ Don't require indent.-  ThenIndent :: IndentMode   -- ^ Parse one token without-                             --   indent, then switch to @Indent@.-  Indent     :: IndentMode   -- ^ Require everything to be indented at-                             --   least one space.---- | Extra custom state for the parser.-data ParserState = ParserState-  { _indentMode  :: IndentMode  -- ^ Currently required level of indentation.-  , _enabledExts :: Set Ext     -- ^ Set of enabled language extensions-                                --   (some of which may affect parsing).-  }--makeLenses ''ParserState--initParserState :: ParserState-initParserState = ParserState NoIndent S.empty---- OpaqueTerm is a wrapper around Term just to make ShowErrorComponent--- happy, which requires Eq and Ord instances; but we can't make Term--- an instance of either.-newtype OpaqueTerm = OT Term-instance Show OpaqueTerm where-  show (OT t) = show t-instance Eq OpaqueTerm where-  _ == _ = True-instance Ord OpaqueTerm where-  compare _ _ = EQ--data DiscoParseError-  = ReservedVarName String-  | InvalidPattern OpaqueTerm-  deriving (Show, Eq, Ord)--instance ShowErrorComponent DiscoParseError where-  showErrorComponent (ReservedVarName x)     = "keyword \"" ++ x ++ "\" cannot be used as a variable name"-  showErrorComponent (InvalidPattern (OT t)) = "Invalid pattern: " ++ run (prettyStr t)-  errorComponentLen (ReservedVarName x) = length x-  errorComponentLen (InvalidPattern _)  = 1---- | A parser is a megaparsec parser of strings, with an extra layer---   of state to keep track of the current indentation level and---   language extensions, and some custom error messages.-type Parser = StateT ParserState (MP.Parsec DiscoParseError String)---- | Run a parser from the initial state.-runParser :: Parser a -> FilePath -> String -> Either (ParseErrorBundle String DiscoParseError) a-runParser = MP.runParser . flip evalStateT initParserState---- | Run a parser under a specified 'IndentMode'.-withIndentMode :: IndentMode -> Parser a -> Parser a-withIndentMode m p = do-  indentMode .= m-  res <- p-  indentMode .= NoIndent-  return res---- | @indented p@ is just like @p@, except that every token must not---   start in the first column.-indented :: Parser a -> Parser a-indented = withIndentMode Indent---- | @indented p@ is just like @p@, except that every token after the---   first must not start in the first column.-thenIndented :: Parser a -> Parser a-thenIndented = withIndentMode ThenIndent---- | @requireIndent p@ possibly requires @p@ to be indented, depending---   on the current '_indentMode'.  Used in the definition of---   'lexeme' and 'symbol'.-requireIndent :: Parser a -> Parser a-requireIndent p = do-  l <- use indentMode-  case l of-    ThenIndent -> do-      a <- p-      indentMode .= Indent-      return a-    Indent     -> L.indentGuard sc GT pos1 >> p-    NoIndent   -> p---- | Locally set the enabled extensions within a subparser.-withExts :: Set Ext -> Parser a -> Parser a-withExts exts p = do-  oldExts <- use enabledExts-  enabledExts .= exts-  a <- p-  enabledExts .= oldExts-  return a---- | Locally enable some additional extensions within a subparser.-withAdditionalExts :: Set Ext -> Parser a -> Parser a-withAdditionalExts exts p = do-  oldExts <- use enabledExts-  enabledExts %= S.union exts-  a <- p-  enabledExts .= oldExts-  return a---- | Ensure that a specific extension is enabled, fail if not.-ensureEnabled :: Ext -> Parser ()-ensureEnabled e = do-  exts <- use enabledExts-  guard $ e `S.member` exts---- | Generically consume whitespace, including comments.-sc :: Parser ()-sc = L.space space1 lineComment empty {- no block comments in disco -}-  where-    lineComment  = L.skipLineComment "--"---- | Parse a lexeme, that is, a parser followed by consuming---   whitespace.-lexeme :: Parser a -> Parser a-lexeme p = requireIndent $ L.lexeme sc p---- | Parse a given string as a lexeme.-symbol :: String -> Parser String-symbol s = requireIndent $ L.symbol sc s---- | Parse a reserved operator.-reservedOp :: String -> Parser ()-reservedOp s = (lexeme . try) (string s *> notFollowedBy (oneOf opChar))---- | Characters that can occur in an operator symbol.-opChar :: [Char]-opChar = "~!@#$%^&*-+=|<>?/\\."--parens, braces, angles, brackets, bagdelims, fbrack, cbrack :: Parser a -> Parser a-parens    = between (symbol "(") (symbol ")")-braces    = between (symbol "{") (symbol "}")-angles    = between (symbol "<") (symbol ">")-brackets  = between (symbol "[") (symbol "]")-bagdelims = between (symbol "⟅") (symbol "⟆")-fbrack    = between (symbol "⌊") (symbol "⌋")-cbrack    = between (symbol "⌈") (symbol "⌉")--semi, comma, colon, dot, pipe, hash :: Parser String-semi      = symbol ";"-comma     = symbol ","-colon     = symbol ":"-dot       = symbol "."-pipe      = symbol "|"-hash      = symbol "#"---- | A literal ellipsis of two or more dots, @..@-ellipsis :: Parser String-ellipsis  = label "ellipsis (..)" $ concat <$> ((:) <$> dot <*> some dot)---- | The symbol that starts an anonymous function (either a backslash---   or a Greek λ).-lambda :: Parser String-lambda = symbol "\\" <|> symbol "λ"--forall :: Parser ()-forall = () <$ symbol "∀" <|> reserved "forall"--exists :: Parser ()-exists = () <$ symbol "∃" <|> reserved "exists"---- | Parse a natural number.-natural :: Parser Integer-natural = lexeme L.decimal <?> "natural number"---- | Parse a nonnegative decimal of the form @xxx.yyyy[zzz]@, where---   the @y@s and bracketed @z@s are both optional as long as the---   other is present.  (In other words, there must be something after---   the period.) For example, this parser accepts all of the---   following:------   > 2.0---   > 2.333---   > 2.33[45]---   > 2.[45]------   The idea is that brackets surround an infinitely repeating---   sequence of digits.------   We used to accept @2.@ with no trailing digits, but no longer do.---   See https://github.com/disco-lang/disco/issues/245 and Note---   [Trailing period].-decimal :: Parser Rational-decimal = lexeme (readDecimal <$> some digit <* char '.'-                              <*> fractionalPart-                 )-  where-    digit = satisfy isDigit-    fractionalPart =-          -- either some digits optionally followed by bracketed digits...-          (,) <$> some digit <*> optional (brackets (some digit))-          -- ...or just bracketed digits.-      <|> ([],) <$> (Just <$> brackets (some digit))--    readDecimal a (b, mrep)-      = read a % 1   -- integer part--        -- next part is just b/10^n-        + (if null b then 0 else read b) % (10^length b)--        -- repeating part-        + readRep (length b) mrep--    readRep _      Nothing    = 0-    readRep offset (Just rep) = read rep % (10^offset * (10^length rep - 1))-      -- If s = 0.[rep] then 10^(length rep) * s = rep.[rep], so-      -- 10^(length rep) * s - s = rep, so-      ---      --   s = rep/(10^(length rep) - 1).-      ---      -- We also have to divide by 10^(length b) to shift it over-      -- past any non-repeating prefix.---- ~~~~ Note [Trailing period]------ We used to accept numbers with nothing after the trailing period,--- such as @2.@. However, this caused some problems with parsing:------   - First, https://github.com/disco-lang/disco/issues/99 which we---     solved by making sure there was not another period after the---     trailing period.---   - Next, https://github.com/disco-lang/disco/issues/245.------ I first tried solving #245 by disallowing *any* operator character--- after the trailing period, but then some tests in the test suite--- started failing, where we had written things like @1./(10^5)@.  The--- problem is that when a period is followed by another operator--- symbol, sometimes we might want them to be parsed as an operator--- (as in @2.-4@, #245), and sometimes we might not (as in--- @1./(10^5)@).  So in the end it seems simpler and cleaner to--- require at least a 0 digit after the period --- just like pretty--- much every other programming language and just like standard--- mathematical practice.---- | Parse a reserved word.-reserved :: String -> Parser ()-reserved w = (lexeme . try) $ string w *> notFollowedBy alphaNumChar---- | The list of all reserved words.-reservedWords :: [String]-reservedWords =-  [ "unit", "true", "false", "True", "False", "let", "in", "is"-  , "if", "when"-  , "otherwise", "and", "or", "mod", "choose", "implies", "iff"-  , "min", "max"-  , "union", "∪", "intersect", "∩", "subset", "⊆", "elem", "∈"-  , "enumerate", "count", "divides"-  , "Void", "Unit", "Bool", "Boolean", "Proposition", "Prop", "Char"-  , "Nat", "Natural", "Int", "Integer", "Frac", "Fractional", "Rational", "Fin"-  , "List", "Bag", "Set", "Graph", "Map"-  , "N", "Z", "F", "Q", "ℕ", "ℤ", "𝔽", "ℚ"-  , "∀", "forall", "∃", "exists", "type"-  , "import", "using"-  ]---- | Parse an identifier, i.e. any non-reserved string beginning with---   a given type of character and continuing with alphanumerics,---   underscores, and apostrophes.-identifier :: Parser Char -> Parser String-identifier begin = (lexeme . try) (p >>= check) <?> "variable name"-  where-    p       = (:) <$> begin <*> many identChar-    identChar = alphaNumChar <|> oneOf "_'"-    check x-      | x `elem` reservedWords = do-          -- back up to beginning of bad token to report correct position-          updateParserState (\s -> s { stateOffset = stateOffset s - length x })-          customFailure $ ReservedVarName x-      | otherwise = return x---- | Parse an 'identifier' and turn it into a 'Name'.-ident :: Parser (Name Term)-ident = string2Name <$> identifier letterChar----------------------------------------------------------------- Parser---- | Results from parsing a block of top-level things.-data TLResults = TLResults-  { _tlDecls :: [Decl]-  , _tlDocs  :: [(Name Term, [DocThing])]-  , _tlTerms :: [Term]-  }--emptyTLResults :: TLResults-emptyTLResults = TLResults [] [] []--makeLenses ''TLResults---- | Parse the entire input as a module (with leading whitespace and---   no leftovers).-wholeModule :: LoadingMode -> Parser Module-wholeModule = between sc eof . parseModule---- | Parse an entire module (a list of declarations ended by---   semicolons).  The 'LoadingMode' parameter tells us whether to---   include or replace any language extensions enabled at the top---   level.  We include them when parsing a module entered at the---   REPL, and replace them when parsing a standalone module.-parseModule :: LoadingMode -> Parser Module-parseModule mode = do-  exts     <- S.fromList <$> many parseExtension-  let extFun = case mode of-        Standalone -> withExts-        REPL       -> withAdditionalExts--  extFun exts $ do-    imports  <- many parseImport-    topLevel <- many parseTopLevel-    let theMod = mkModule exts imports topLevel-    return theMod-    where-      groupTLs :: [DocThing] -> [TopLevel] -> TLResults-      groupTLs _ [] = emptyTLResults-      groupTLs revDocs (TLDoc doc : rest)-        = groupTLs (doc : revDocs) rest-      groupTLs revDocs (TLDecl decl@(DType (TypeDecl x _)) : rest)-        = groupTLs [] rest-          & tlDecls %~ (decl :)-          & tlDocs  %~ ((x, reverse revDocs) :)-      groupTLs revDocs (TLDecl decl@(DTyDef (TypeDefn x _ _)) : rest)-        = groupTLs [] rest-          & tlDecls %~ (decl :)-          & tlDocs  %~ ((string2Name x, reverse revDocs) :)-      groupTLs _ (TLDecl defn : rest)-        = groupTLs [] rest-          & tlDecls %~ (defn :)-      groupTLs _ (TLExpr t : rest)-        = groupTLs [] rest & tlTerms %~ (t:)--      defnGroups :: [Decl] -> [Decl]-      defnGroups []                = []-      defnGroups (d@DType{}  : ds)  = d : defnGroups ds-      defnGroups (d@DTyDef{} : ds)  = d : defnGroups ds-      defnGroups (DDefn (TermDefn x bs) : ds)  = DDefn (TermDefn x (bs ++ concatMap (\(TermDefn _ cs) -> cs) grp)) : defnGroups rest-        where-          (grp, rest) = matchDefn ds-          matchDefn :: [Decl] -> ([TermDefn], [Decl])-          matchDefn (DDefn t@(TermDefn x' _) : ds2) | x == x' = (t:ts, ds2')-            where-              (ts, ds2') = matchDefn ds2-          matchDefn ds2 = ([], ds2)--      mkModule exts imps tls = Module exts imps (defnGroups decls) docs terms-        where-          TLResults decls docs terms = groupTLs [] tls---- | Parse an extension.-parseExtension :: Parser Ext-parseExtension = L.nonIndented sc $-  reserved "using" *> parseExtName---- | Parse the name of a language extension (case-insensitive).-parseExtName :: Parser Ext-parseExtName = choice (map parseOneExt allExtsList) <?> "language extension name"-  where-    parseOneExt ext = ext <$ lexeme (string' (show ext) :: Parser String)---- | Parse an import, of the form @import <modulename>@.-parseImport :: Parser String-parseImport = L.nonIndented sc $-  reserved "import" *> parseModuleName---- | Parse the name of a module.-parseModuleName :: Parser String-parseModuleName = lexeme $-  intercalate "/" <$> (some (alphaNumChar <|> oneOf "_-") `sepBy` char '/') <* optional (string ".disco")---- | Parse a top level item (either documentation or a declaration),---   which must start at the left margin.-parseTopLevel :: Parser TopLevel-parseTopLevel = L.nonIndented sc $-      TLDoc  <$> parseDocThing-  <|> TLDecl <$> parseDecl         -- See Note [Parsing definitions and top-level expressions]-  <|> TLExpr <$> thenIndented parseTerm--  -- ~~~~ Note [Parsing definitions and top-level expressions]-  ---  -- The beginning of a definition might look the same as an-  -- expression.  e.g. is f(x,y) the start of a definition of f, or an-  -- expression with a function call?  We used to therefore wrap-  -- 'parseDecl' in 'try'.  The problem is that if a definition has a-  -- syntax error on the RHS, it would fail, backtrack, then try-  -- parsing a top-level expression and fail when it got to the =-  -- sign, giving an uninformative parse error message.-  -- See https://github.com/disco-lang/disco/issues/346.-  ---  -- The solution is that we now do more careful backtracking within-  -- parseDecl itself: when parsing a definition, we only backtrack if-  -- we don't get a complete LHS + '=' sign; once we start parsing the-  -- RHS of a definition we no longer backtrack, since it can't-  -- possibly be a valid top-level expression.---- | Parse a documentation item: either a group of lines beginning---   with @|||@ (text documentation), or a group beginning with @!!!@---   (checked examples/properties).-parseDocThing :: Parser DocThing-parseDocThing-  =   DocString   <$> some parseDocString-  <|> DocProperty <$> parseProperty---- | Parse one line of documentation beginning with @|||@.-parseDocString :: Parser String-parseDocString = label "documentation" $ L.nonIndented sc $-  string "|||"-  *> takeWhileP Nothing (`elem` " \t")-  *> takeWhileP Nothing (`notElem` "\r\n") <* sc--  -- Note we use string "|||" rather than symbol "|||" because we-  -- don't want it to consume whitespace afterwards (in particular a-  -- line with ||| by itself would cause symbol "|||" to consume the-  -- newline).---- | Parse a top-level property/unit test, of the form------   @!!! forall x1 : ty1, ..., xn : tyn. term@.------   The forall is optional.-parseProperty :: Parser Term-parseProperty = label "property" $ L.nonIndented sc $ do-  _ <- symbol "!!!"-  indented parseTerm---- | Parse a single top-level declaration (either a type declaration---   or single definition clause).-parseDecl :: Parser Decl-parseDecl = try (DType <$> parseTyDecl) <|> DDefn <$> parseDefn <|> DTyDef <$> parseTyDefn---- | Parse a top-level type declaration of the form @x : ty@.-parseTyDecl :: Parser TypeDecl-parseTyDecl = label "type declaration" $-  TypeDecl <$> ident <*> indented (colon *> parsePolyTy)---- | Parse a definition of the form @x pat1 .. patn = t@.-parseDefn :: Parser TermDefn-parseDefn = label "definition" $-  (\(x, ps) body -> TermDefn x [bind ps body])--  -- Only backtrack if we don't get a complete 'LHS ='.  Once we see-  -- an = sign, commit to parsing a definition, because it can't be a-  -- valid standalone expression anymore.  If the RHS fails, we don't-  -- want to backtrack, we just want to display the parse error.-  <$> try ((,) <$> ident <*> indented (many parseAtomicPattern) <* reservedOp "=")-  <*> indented parseTerm---- | Parse the definition of a user-defined algebraic data type.-parseTyDefn :: Parser TypeDefn-parseTyDefn = label "type defintion" $ do-  reserved "type"-  indented $ do-    name <- parseTyDef-    args <- fromMaybe [] <$> optional (parens $ parseTyVarName `sepBy1` comma)-    _ <- reservedOp "="-    TypeDefn name args <$> parseType---- | Parse the entire input as a term (with leading whitespace and---   no leftovers).-term :: Parser Term-term = between sc eof parseTerm---- | Parse a term, consisting of a @parseTerm'@ optionally---   followed by an ascription.-parseTerm :: Parser Term-parseTerm = -- trace "parseTerm" $-  ascribe <$> parseTerm' <*> optional (label "type annotation" $ colon *> parsePolyTy)-  where-    ascribe t Nothing   = t-    ascribe t (Just ty) = TAscr t ty---- | Parse a non-atomic, non-ascribed term.-parseTerm' :: Parser Term-parseTerm' = label "expression" $-      parseQuantified-  <|> parseLet-  <|> parseExpr-  <|> parseAtom---- | Parse an atomic term.-parseAtom :: Parser Term-parseAtom = label "expression" $-      parseUnit-  <|> TBool True  <$ (reserved "true" <|> reserved "True")-  <|> TBool False <$ (reserved "false" <|> reserved "False")-  <|> TChar <$> lexeme (between (char '\'') (char '\'') L.charLiteral)-  <|> TString <$> lexeme (char '"' >> manyTill L.charLiteral (char '"'))-  <|> TWild <$ try parseWild-  <|> TPrim <$> try parseStandaloneOp--  -- Note primitives are NOT reserved words, so they are just parsed-  -- as identifiers.  This means that it is possible to shadow a-  -- primitive in a local context, as it should be.  Vars are turned-  -- into prims at scope-checking time: if a var is not in scope but-  -- there is a prim of that name then it becomes a TPrim.  See the-  -- 'typecheck Infer (TVar x)' case in Disco.Typecheck.-  <|> TVar <$> ident-  <|> TPrim <$> (ensureEnabled Primitives *> parsePrim)-  <|> TRat <$> try decimal-  <|> TNat <$> natural-  <|> parseTypeOp-  <|> TApp (TPrim PrimFloor) . TParens <$> fbrack parseTerm-  <|> TApp (TPrim PrimCeil)  . TParens <$> cbrack parseTerm-  <|> parseCase-  <|> try parseAbs-  <|> bagdelims (parseContainer BagContainer)-  <|> braces    (parseContainer SetContainer)-  <|> brackets  (parseContainer ListContainer)-  <|> tuple <$> parens (parseTerm `sepBy1` comma)--parseAbs :: Parser Term-parseAbs = TApp (TPrim PrimAbs) <$> (pipe *> parseTerm <* pipe)--parseUnit :: Parser Term-parseUnit = TUnit <$ (reserved "unit" <|> void (symbol "■"))---- | Parse a wildcard, which is an underscore that isn't the start of---   an identifier.-parseWild :: Parser ()-parseWild = (lexeme . try . void) $-  string "_" <* notFollowedBy (alphaNumChar <|> oneOf "_'")---- | Parse a standalone operator name with tildes indicating argument---   slots, e.g. ~+~ for the addition operator.-parseStandaloneOp :: Parser Prim-parseStandaloneOp = asum $ concatMap mkStandaloneOpParsers (concat opTable)-  where-    mkStandaloneOpParsers :: OpInfo -> [Parser Prim]-    mkStandaloneOpParsers (OpInfo (UOpF Pre uop) syns _)-      = map (\syn -> PrimUOp uop <$ try (lexeme (string syn >> char '~'))) syns-    mkStandaloneOpParsers (OpInfo (UOpF Post uop) syns _)-      = map (\syn -> PrimUOp uop <$ try (lexeme (char '~' >> string syn))) syns-    mkStandaloneOpParsers (OpInfo (BOpF _ bop) syns _)-      = map (\syn -> PrimBOp bop <$ try (lexeme (char '~' >> string syn >> char '~'))) syns--    -- XXX TODO: improve the above so it first tries to parse a ~,-    --   then parses any postfix or infix thing; or else it looks for-    --   a prefix thing followed by a ~.  This will get rid of the-    --   need for 'try' and also potentially improve error messages.-    --   The below may come in useful.--    -- flatOpTable = concat opTable--    -- prefixOps  = [ (uop, syns) | (OpInfo (UOpF Pre uop) syns _)  <- flatOpTable ]-    -- postfixOps = [ (uop, syns) | (OpInfo (UOpF Post uop) syns _) <- flatOpTable ]-    -- infixOps   = [ (bop, syns) | (OpInfo (BOpF _ bop) syns _)    <- flatOpTable ]---- | Parse a primitive name starting with a $.-parsePrim :: Parser Prim-parsePrim = do-  void (char '$')-  x <- identifier letterChar-  case find ((==x) . primSyntax) primTable of-    Just (PrimInfo p _ _) -> return p-    Nothing               -> fail ("Unrecognized primitive $" ++ x)---- | Parse a container, like a literal list, set, bag, or a---   comprehension (not including the square or curly brackets).------ @--- <container>---   ::= '[' <container-contents> ']'---     | '{' <container-contents> '}'------ <container-contents>---   ::= empty | <nonempty-container>------ <nonempty-container>---   ::= <term> [ <ellipsis> ]---     | <term> <container-end>------ <container-end>---   ::= '|' <comprehension>---     | ',' [ <term> (',' <item>)* ] [ <ellipsis> ]------ <comprehension> ::= <qual> [ ',' <qual> ]*------ <qual>---   ::= <ident> 'in' <term>---     | <term>------ <ellipsis> ::= '..' [ <term> ]--- @--parseContainer :: Container -> Parser Term-parseContainer c = nonEmptyContainer <|> return (TContainer c [] Nothing)-  -- Careful to do this without backtracking, since backtracking can-  -- lead to bad performance in certain pathological cases (for-  -- example, a very deeply nested list).--  where-    -- Any non-empty container starts with a term, followed by some-    -- remainder (which could either be the rest of a literal-    -- container, or a container comprehension).  If there is no-    -- remainder just return a singleton container, optionally with an-    -- ellipsis.-    nonEmptyContainer = do-      t <- parseRepTerm--      containerRemainder t <|> singletonContainer t--    parseRepTerm = do-      t <- parseTerm-      n <- optional $ do-        guard (c == BagContainer)-        void hash-        parseTerm-      return (t, n)--    singletonContainer t = TContainer c [t] <$> optional parseEllipsis--    -- The remainder of a container after the first term starts with-    -- either a pipe (for a comprehension) or a comma (for a literal-    -- container).-    containerRemainder :: (Term, Maybe Term) -> Parser Term-    containerRemainder (t,n) = do-      s <- pipe <|> comma-      case (s, n) of-        ("|", Nothing) -> parseContainerComp c t-        ("|", Just _)  -> fail "no comprehension with bag repetition syntax"-        (",", _)       -> do-          -- Parse the rest of the terms in a literal container after-          -- the first, then an optional ellipsis, and return-          -- everything together.-          ts <- parseRepTerm `sepBy` comma-          e  <- optional parseEllipsis--          return $ TContainer c ((t,n):ts) e-        _   -> error "Impossible, got a symbol other than '|' or ',' in containerRemainder"---- | Parse an ellipsis at the end of a literal list, of the form---   @.. t@.  Any number > 1 of dots may be used, just for fun.-parseEllipsis :: Parser (Ellipsis Term)-parseEllipsis = do-  _ <- ellipsis-  Until <$> parseTerm---- | Parse the part of a list comprehension after the | (without---   square brackets), i.e. a list of qualifiers.------   @q [,q]*@-parseContainerComp :: Container -> Term -> Parser Term-parseContainerComp c t = do-  qs <- toTelescope <$> (parseQual `sepBy` comma)-  return (TContainerComp c $ bind qs t)---- | Parse a qualifier in a comprehension: either a binder @x in t@ or---   a guard @t@.-parseQual :: Parser Qual-parseQual = try parseSelection <|> parseQualGuard-  where-    parseSelection = label "membership expression (x in ...)" $-      QBind <$> ident <*> (selector *> (embed <$> parseTerm))-    selector = reservedOp "<-" <|> reserved "in"--    parseQualGuard = label "boolean expression" $-      QGuard . embed <$> parseTerm---- | Turn a parenthesized list of zero or more terms into the---   appropriate syntax node: one term @(t)@ is just the term itself---   (but we record the fact that it was parenthesized, in order to---   correctly turn juxtaposition into multiplication); two or more---   terms @(t1,t2,...)@ are a tuple.-tuple :: [Term] -> Term-tuple [x] = TParens x-tuple t   = TTup t---- | Parse a quantified abstraction (λ, ∀, ∃).-parseQuantified :: Parser Term-parseQuantified =-  TAbs <$> parseQuantifier-       <*> (bind <$> parsePattern `sepBy` comma <*> (dot *> parseTerm))---- | Parse a quantifier symbol (lambda, forall, or exists).-parseQuantifier :: Parser Quantifier-parseQuantifier =-      Lam <$ lambda-  <|> All <$ forall-  <|> Ex  <$ exists---- | Parse a let expression (@let x1 = t1, x2 = t2, ... in t@).-parseLet :: Parser Term-parseLet =-  TLet <$>-    (reserved "let" *>-      (bind-        <$> (toTelescope <$> (parseBinding `sepBy` comma))-        <*> (reserved "in" *> parseTerm)))---- | Parse a single binding (@x [ : ty ] = t@).-parseBinding :: Parser Binding-parseBinding = do-  x   <- ident-  mty <- optional (colon *> parsePolyTy)-  t   <- symbol "=" *> (embed <$> parseTerm)-  return $ Binding (embed <$> mty) x t---- | Parse a case expression.-parseCase :: Parser Term-parseCase = between (symbol "{?") (symbol "?}") $-  TCase <$> parseBranch `sepBy` comma---- | Parse one branch of a case expression.-parseBranch :: Parser Branch-parseBranch = flip bind <$> parseTerm <*> parseGuards---- | Parse the list of guards in a branch.  @otherwise@ can be used---   interchangeably with an empty list of guards.-parseGuards :: Parser (Telescope Guard)-parseGuards = (TelEmpty <$ reserved "otherwise") <|> (toTelescope <$> many parseGuard)---- | Parse a single guard (@if@, @if ... is@, or @let@)-parseGuard :: Parser Guard-parseGuard = try parseGPat <|> parseGBool <|> parseGLet-  where-    guardWord = reserved "if" <|> reserved "when"-    parseGBool = GBool <$> (embed <$> (guardWord *> parseTerm))-    parseGPat  = GPat <$> (embed <$> (guardWord *> parseTerm))-                      <*> (reserved "is" *> parsePattern)-    parseGLet  = GLet <$> (reserved "let" *> parseBinding)---- | Parse an atomic pattern, by parsing a term and then attempting to---   convert it to a pattern.-parseAtomicPattern :: Parser Pattern-parseAtomicPattern = label "pattern" $ do-  t <- parseAtom-  case termToPattern t of-    Nothing -> customFailure $ InvalidPattern (OT t)-    Just p  -> return p---- | Parse a pattern, by parsing a term and then attempting to convert---   it to a pattern.-parsePattern :: Parser Pattern-parsePattern = label "pattern" $ do-  t <- parseTerm-  case termToPattern t of-    Nothing -> customFailure $ InvalidPattern (OT t)-    Just p  -> return p---- | Attempt converting a term to a pattern.-termToPattern :: Term -> Maybe Pattern-termToPattern TWild       = Just PWild-termToPattern (TVar x)    = Just $ PVar x-termToPattern (TParens t) = termToPattern t-termToPattern TUnit       = Just PUnit-termToPattern (TBool b)   = Just $ PBool b-termToPattern (TNat n)    = Just $ PNat n-termToPattern (TChar c)   = Just $ PChar c-termToPattern (TString s) = Just $ PString s-termToPattern (TTup ts)   = PTup <$> mapM termToPattern ts-termToPattern (TApp (TVar i) t)-  | i == string2Name "left"  = PInj L <$> termToPattern t-  | i == string2Name "right" = PInj R <$> termToPattern t--- termToPattern (TInj s t)  = PInj s <$> termToPattern t--termToPattern (TAscr t s) = case s of-  Forall (unsafeUnbind -> ([], s')) -> PAscr <$> termToPattern t <*> pure s'-  _                                 -> Nothing--termToPattern (TBin Cons t1 t2)-  = PCons <$> termToPattern t1 <*> termToPattern t2--termToPattern (TBin Add t1 t2)-  = case (termToPattern t1, termToPattern t2) of-      (Just p, _)-        |  length (toListOf fvAny p) == 1-        && null (toListOf fvAny t2)-        -> Just $ PAdd L p t2-      (_, Just p)-        |  length (toListOf fvAny p) == 1-        && null (toListOf fvAny t1)-        -> Just $ PAdd R p t1-      _ -> Nothing-      -- If t1 is a pattern binding one variable, and t2 has no fvs,-      -- this can be a PAdd L.  Also vice versa for PAdd R.--termToPattern (TBin Mul t1 t2)-  = case (termToPattern t1, termToPattern t2) of-      (Just p, _)-        |  length (toListOf fvAny p) == 1-        && null (toListOf fvAny t2)-        -> Just $ PMul L p t2-      (_, Just p)-        |  length (toListOf fvAny p) == 1-        && null (toListOf fvAny t1)-        -> Just $ PMul R p t1-      _ -> Nothing-      -- If t1 is a pattern binding one variable, and t2 has no fvs,-      -- this can be a PMul L.  Also vice versa for PMul R.--termToPattern (TBin Sub t1 t2)-  = case termToPattern t1 of-      Just p-        |  length (toListOf fvAny p) == 1-        && null (toListOf fvAny t2)-        -> Just $ PSub p t2-      _ -> Nothing-      -- If t1 is a pattern binding one variable, and t2 has no fvs,-      -- this can be a PSub.--      -- For now we don't handle the case of t - p, since it seems-      -- less useful (and desugaring it would require extra code since-      -- subtraction is not commutative).--termToPattern (TBin Div t1 t2)-  = PFrac <$> termToPattern t1 <*> termToPattern t2--termToPattern (TUn Neg t) = PNeg <$> termToPattern t--termToPattern (TContainer ListContainer ts Nothing)-  = PList <$> mapM (termToPattern . fst) ts--termToPattern _           = Nothing---- | Parse an expression built out of unary and binary operators.-parseExpr :: Parser Term-parseExpr = fixJuxtMul . fixChains <$> (makeExprParser parseAtom table <?> "expression")-  where-    table-        -- Special case for function application, with highest-        -- precedence.  Note that we parse all juxtaposition as-        -- function application first; we later go through and turn-        -- some into multiplication (fixing up the precedence-        -- appropriately) based on a syntactic analysis.-      = [ InfixL (TApp <$ string "") ]--        -- get all other operators from the opTable-      : (map . concatMap) mkOpParser opTable--    mkOpParser :: OpInfo -> [Operator Parser Term]-    mkOpParser (OpInfo op syns _) = concatMap (withOpFixity op) syns--    -- Only parse unary operators consisting of operator symbols.-    -- Alphabetic unary operators (i.e. 'not') will be parsed as-    -- applications of variable names, since if they are parsed here-    -- they will incorrectly parse even when they are a prefix of a-    -- variable name.-    withOpFixity (UOpF fx op) syn-      | any isAlpha syn = []-      | otherwise = [ufxParser fx ((reservedOp syn <?> "operator") >> return (TUn op))]--    withOpFixity (BOpF fx op) syn-      = [bfxParser fx ((reservedOp syn <?> "operator") >> return (TBin op))]--    ufxParser Pre  = Prefix-    ufxParser Post = Postfix--    bfxParser InL = InfixL-    bfxParser InR = InfixR-    bfxParser In  = InfixN--    isChainable op = op `elem` [Eq, Neq, Lt, Gt, Leq, Geq, Divides]--    -- Comparison chains like 3 < x < 5 first get parsed as 3 < (x <-    -- 5), which does not make sense.  This function looks for such-    -- nested comparison operators and turns them into a TChain.-    fixChains (TUn op t) = TUn op (fixChains t)-    fixChains (TBin op t1 (TBin op' t21 t22))-      | isChainable op && isChainable op' = TChain t1 (TLink op t21 : getLinks op' t22)-    fixChains (TBin op t1 t2) = TBin op (fixChains t1) (fixChains t2)-    fixChains (TApp t1 t2) = TApp (fixChains t1) (fixChains t2)--    -- Only recurse as long as we see TUn, TBin, or TApp which could-    -- have been generated by the expression parser.  If we see-    -- anything else we can stop.-    fixChains e = e--    getLinks op (TBin op' t1 t2)-      | isChainable op' = TLink op t1 : getLinks op' t2-    getLinks op e = [TLink op (fixChains e)]--    -- Find juxtapositions (parsed as function application) which-    -- syntactically have either a literal Nat or a parenthesized-    -- expression containing an operator as the LHS, and turn them-    -- into multiplications.  Then fix up the parse tree by rotating-    -- newly created multiplications up until their precedence is-    -- higher than the thing above them.--    fixJuxtMul :: Term -> Term--    -- Just recurse through TUn or TBin and fix precedence on the way back up.-    fixJuxtMul (TUn op t)      = fixPrec $ TUn op (fixJuxtMul t)-    fixJuxtMul (TBin op t1 t2) = fixPrec $ TBin op (fixJuxtMul t1) (fixJuxtMul t2)--    -- Possibly turn a TApp into a multiplication, if the LHS looks-    -- like a multiplicative term.  However, we must be sure to-    -- *first* recursively fix the subterms (particularly the-    -- left-hand one) *before* doing this analysis.  See-    -- <https://github.com/disco-lang/disco/issues/71> .-    fixJuxtMul (TApp t1 t2)-      | isMultiplicativeTerm t1' = fixPrec $ TBin Mul t1' t2'-      | otherwise                = fixPrec $ TApp     t1' t2'-      where-        t1' = fixJuxtMul t1-        t2' = fixJuxtMul t2--    -- Otherwise we can stop recursing, since anything other than TUn,-    -- TBin, or TApp could not have been produced by the expression-    -- parser.-    fixJuxtMul t = t--    -- A multiplicative term is one that looks like either a natural-    -- number literal, or a unary or binary operation (optionally-    -- parenthesized).  For example, 3, (-2), and (x + 5) are all-    -- multiplicative terms, so 3x, (-2)x, and (x + 5)x all get parsed-    -- as multiplication.  On the other hand, (x y) is always parsed-    -- as function application, even if x and y both turn out to have-    -- numeric types; a variable like x does not count as a-    -- multiplicative term.  Likewise, (x y) z is parsed as function-    -- application, since (x y) is not a multiplicative term: it is-    -- parenthezised, but contains a TApp rather than a TBin or TUn.-    isMultiplicativeTerm :: Term -> Bool-    isMultiplicativeTerm (TNat _)    = True-    isMultiplicativeTerm TUn{}       = True-    isMultiplicativeTerm TBin{}      = True-    isMultiplicativeTerm (TParens t) = isMultiplicativeTerm t-    isMultiplicativeTerm _           = False--    -- Fix precedence by bubbling up any new TBin terms whose-    -- precedence is less than that of the operator above them.  We-    -- don't worry at all about fixing associativity, just precedence.--    fixPrec :: Term -> Term--    -- e.g.  2y! --> (2@y)! --> fixup --> 2 * (y!)-    fixPrec (TUn uop (TBin bop t1 t2))-      | bPrec bop < uPrec uop = case uopMap M.! uop of-          OpInfo (UOpF Pre  _) _ _ -> TBin bop (TUn uop t1) t2-          OpInfo (UOpF Post _) _ _ -> TBin bop t1 (TUn uop t2)-          _ -> error "Impossible! In fixPrec, uopMap contained OpInfo (BOpF ...)"--    fixPrec (TBin bop1 (TBin bop2 t1 t2) t3)-      | bPrec bop2 < bPrec bop1 = TBin bop2 t1 (fixPrec $ TBin bop1 t2 t3)--    -- e.g. x^2y --> x^(2@y) --> x^(2*y) --> (x^2) * y-    fixPrec (TBin bop1 t1 (TBin bop2 t2 t3))-      | bPrec bop2 < bPrec bop1 = TBin bop2 (fixPrec $ TBin bop1 t1 t2) t3--    fixPrec t = t---- | Parse an atomic type.-parseAtomicType :: Parser Type-parseAtomicType = label "type" $-      TyVoid <$ reserved "Void"-  <|> TyUnit <$ reserved "Unit"-  <|> TyBool <$ (reserved "Boolean" <|> reserved "Bool")-  <|> TyProp <$ (reserved "Proposition" <|> reserved "Prop")-  <|> TyC    <$ reserved "Char"-  -- <|> try parseTyFin-  <|> TyN    <$ (reserved "Natural" <|> reserved "Nat" <|> reserved "N" <|> reserved "ℕ")-  <|> TyZ    <$ (reserved "Integer" <|> reserved "Int" <|> reserved "Z" <|> reserved "ℤ")-  <|> TyF    <$ (reserved "Fractional" <|> reserved "Frac" <|> reserved "F" <|> reserved "𝔽")-  <|> TyQ    <$ (reserved "Rational" <|> reserved "Q" <|> reserved "ℚ")-  <|> TyCon  <$> parseCon <*> (fromMaybe [] <$> optional (parens (parseType `sepBy1` comma)))-  <|> TyVar  <$> parseTyVar-  <|> parens parseType---- parseTyFin :: Parser Type--- parseTyFin = TyFin  <$> (reserved "Fin" *> natural)---          <|> TyFin  <$> (lexeme (string "Z" <|> string "ℤ") *> natural)--parseCon :: Parser Con-parseCon =-      CList  <$  reserved "List"-  <|> CBag   <$  reserved "Bag"-  <|> CSet   <$  reserved "Set"-  <|> CGraph <$  reserved "Graph"-  <|> CMap   <$  reserved "Map"-  <|> CUser  <$> parseTyDef--parseTyDef :: Parser String-parseTyDef =  identifier upperChar--parseTyVarName :: Parser String-parseTyVarName = identifier lowerChar--parseTyVar :: Parser (Name Type)-parseTyVar = string2Name <$> parseTyVarName--parsePolyTy :: Parser PolyType-parsePolyTy = closeType <$> parseType---- | Parse a type expression built out of binary operators.-parseType :: Parser Type-parseType = makeExprParser parseAtomicType table-  where-    table = [ [ infixR "*" (:*:)-              , infixR "×" (:*:) ]-            , [ infixR "+" (:+:)-              , infixR "⊎" (:+:)-              ]-            , [ infixR "->" (:->:)-              , infixR "→"  (:->:)-              ]-            ]--    infixR name fun = InfixR (reservedOp name >> return fun)--parseTyOp :: Parser TyOp-parseTyOp =-        Enumerate <$ reserved "enumerate"-    <|> Count     <$ reserved "count"+-- |+-- Module      :  Disco.Parser+-- Copyright   :  disco team and contributors+-- Maintainer  :  byorgey@gmail.com+--+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Parser to convert concrete Disco syntax into an (untyped, surface+-- language) AST.+module Disco.Parser (+  -- * Parser type and utilities+  DiscoParseError (..),+  Parser,+  runParser,+  withExts,+  indented,+  thenIndented,++  -- * Lexer++  -- ** Basic lexemes+  sc,+  lexeme,+  symbol,+  reservedOp,+  natural,+  reserved,+  reservedWords,+  ident,++  -- ** Punctuation+  parens,+  braces,+  angles,+  brackets,+  semi,+  comma,+  colon,+  dot,+  pipe,+  lambda,++  -- * Disco parser++  -- ** Modules+  wholeModule,+  parseModule,+  parseExtName,+  parseTopLevel,+  parseDecl,+  parseImport,+  parseModuleName,++  -- ** Terms+  term,+  parseTerm,+  parseTerm',+  parseExpr,+  parseAtom,+  parseContainer,+  parseEllipsis,+  parseContainerComp,+  parseQual,+  parseLet,+  parseTypeOp,++  -- ** Case and patterns+  parseCase,+  parseBranch,+  parseGuards,+  parseGuard,+  parsePattern,+  parseAtomicPattern,++  -- ** Types+  parseType,+  parseAtomicType,+  parsePolyTy,+)+where++import Unbound.Generics.LocallyNameless (+  Name,+  bind,+  embed,+  fvAny,+  name2String,+  string2Name,+ )+import Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)++import Control.Monad.Combinators.Expr+import Text.Megaparsec hiding (+  State,+  runParser,+ )+import qualified Text.Megaparsec as MP+import Text.Megaparsec.Char+import qualified Text.Megaparsec.Char.Lexer as L++import Control.Lens (+  makeLenses,+  toListOf,+  use,+  (%=),+  (%~),+  (&),+  (.=),+ )+import Control.Monad.State+import Data.Char (isAlpha, isDigit)+import Data.Foldable (asum)+import Data.List (find, intercalate)+import qualified Data.Map as M+import Data.Maybe (fromMaybe, isNothing)+import Data.Ratio+import Data.Set (Set)+import qualified Data.Set as S++import Disco.AST.Surface+import Disco.Extensions+import Disco.Module+import Disco.Pretty (prettyStr)+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Types+import Polysemy (run)++------------------------------------------------------------+-- Lexer++-- Some of the basic setup code for the parser taken from+-- https://markkarpov.com/megaparsec/parsing-simple-imperative-language.html++-- | Currently required indent level.+data IndentMode where+  NoIndent ::+    -- | Don't require indent.+    IndentMode+  ThenIndent ::+    -- | Parse one token without+    --   indent, then switch to @Indent@.+    IndentMode+  Indent ::+    -- | Require everything to be indented at+    --   least one space.+    IndentMode++-- | Extra custom state for the parser.+data ParserState = ParserState+  { _indentMode :: IndentMode+  -- ^ Currently required level of indentation.+  , _enabledExts :: Set Ext+  -- ^ Set of enabled language extensions+  --   (some of which may affect parsing).+  }++makeLenses ''ParserState++initParserState :: ParserState+initParserState = ParserState NoIndent S.empty++-- OpaqueTerm is a wrapper around Term just to make ShowErrorComponent+-- happy, which requires Eq and Ord instances; but we can't make Term+-- an instance of either.+newtype OpaqueTerm = OT Term+instance Show OpaqueTerm where+  show (OT t) = show t+instance Eq OpaqueTerm where+  _ == _ = True+instance Ord OpaqueTerm where+  compare _ _ = EQ++data DiscoParseError+  = ReservedVarName String+  | InvalidPattern OpaqueTerm+  | MissingAscr+  | MultiArgLambda+  deriving (Show, Eq, Ord)++instance ShowErrorComponent DiscoParseError where+  showErrorComponent (ReservedVarName x) = "keyword \"" ++ x ++ "\" cannot be used as a variable name"+  showErrorComponent (InvalidPattern (OT t)) = "Invalid pattern: " ++ run (prettyStr t)+  showErrorComponent MissingAscr = "Variables introduced by ∀ or ∃ must have a type"+  showErrorComponent MultiArgLambda = "Anonymous functions (lambdas) can only have a single argument.\nInstead of \\x, y. ... you can write  \\x. \\y. ...\nhttps://disco-lang.readthedocs.io/en/latest/reference/anonymous-func.html"+  errorComponentLen (ReservedVarName x) = length x+  errorComponentLen (InvalidPattern _) = 1+  errorComponentLen MissingAscr = 1+  errorComponentLen MultiArgLambda = 1++-- | A parser is a megaparsec parser of strings, with an extra layer+--   of state to keep track of the current indentation level and+--   language extensions, and some custom error messages.+type Parser = StateT ParserState (MP.Parsec DiscoParseError String)++-- | Run a parser from the initial state.+runParser :: Parser a -> FilePath -> String -> Either (ParseErrorBundle String DiscoParseError) a+runParser = MP.runParser . flip evalStateT initParserState++-- | Run a parser under a specified 'IndentMode'.+withIndentMode :: IndentMode -> Parser a -> Parser a+withIndentMode m p = do+  indentMode .= m+  res <- p+  indentMode .= NoIndent+  return res++-- | @indented p@ is just like @p@, except that every token must not+--   start in the first column.+indented :: Parser a -> Parser a+indented = withIndentMode Indent++-- | @indented p@ is just like @p@, except that every token after the+--   first must not start in the first column.+thenIndented :: Parser a -> Parser a+thenIndented = withIndentMode ThenIndent++-- | @requireIndent p@ possibly requires @p@ to be indented, depending+--   on the current '_indentMode'.  Used in the definition of+--   'lexeme' and 'symbol'.+requireIndent :: Parser a -> Parser a+requireIndent p = do+  l <- use indentMode+  case l of+    ThenIndent -> do+      a <- p+      indentMode .= Indent+      return a+    Indent -> L.indentGuard sc GT pos1 >> p+    NoIndent -> p++-- | Locally set the enabled extensions within a subparser.+withExts :: Set Ext -> Parser a -> Parser a+withExts exts p = do+  oldExts <- use enabledExts+  enabledExts .= exts+  a <- p+  enabledExts .= oldExts+  return a++-- | Locally enable some additional extensions within a subparser.+withAdditionalExts :: Set Ext -> Parser a -> Parser a+withAdditionalExts exts p = do+  oldExts <- use enabledExts+  enabledExts %= S.union exts+  a <- p+  enabledExts .= oldExts+  return a++-- | Ensure that a specific extension is enabled, fail if not.+ensureEnabled :: Ext -> Parser ()+ensureEnabled e = do+  exts <- use enabledExts+  guard $ e `S.member` exts++-- | Generically consume whitespace, including comments.+sc :: Parser ()+sc = L.space space1 lineComment empty {- no block comments in disco -}+ where+  lineComment = L.skipLineComment "--"++-- | Parse a lexeme, that is, a parser followed by consuming+--   whitespace.+lexeme :: Parser a -> Parser a+lexeme p = requireIndent $ L.lexeme sc p++-- | Parse a given string as a lexeme.+symbol :: String -> Parser String+symbol s = requireIndent $ L.symbol sc s++-- | Parse a reserved operator.+reservedOp :: String -> Parser ()+reservedOp s = (lexeme . try) (string s *> notFollowedBy (oneOf opChar))++-- | Characters that can occur in an operator symbol.+opChar :: [Char]+opChar = "~!@#$%^&*-+=|<>?/\\."++parens, braces, angles, brackets, bagdelims, fbrack, cbrack :: Parser a -> Parser a+parens = between (symbol "(") (symbol ")")+braces = between (symbol "{") (symbol "}")+angles = between (symbol "<") (symbol ">")+brackets = between (symbol "[") (symbol "]")+bagdelims = between (symbol "⟅") (symbol "⟆")+fbrack = between (symbol "⌊") (symbol "⌋")+cbrack = between (symbol "⌈") (symbol "⌉")++semi, comma, colon, dot, pipe, hash :: Parser String+semi = symbol ";"+comma = symbol ","+colon = symbol ":"+dot = symbol "."+pipe = symbol "|"+hash = symbol "#"++-- | A literal ellipsis of two or more dots, @..@+ellipsis :: Parser String+ellipsis = label "ellipsis (..)" $ concat <$> ((:) <$> dot <*> some dot)++-- | The symbol that starts an anonymous function (either a backslash+--   or a Greek λ).+lambda :: Parser String+lambda = symbol "\\" <|> symbol "λ"++forall :: Parser ()+forall = void (symbol "∀") <|> reserved "forall"++exists :: Parser ()+exists = void (symbol "∃") <|> reserved "exists"++-- | Parse a natural number.+natural :: Parser Integer+natural = lexeme L.decimal <?> "natural number"++-- | Parse a nonnegative decimal of the form @xxx.yyyy[zzz]@, where+--   the @y@s and bracketed @z@s are both optional as long as the+--   other is present.  (In other words, there must be something after+--   the period.) For example, this parser accepts all of the+--   following:+--+--   > 2.0+--   > 2.333+--   > 2.33[45]+--   > 2.[45]+--+--   The idea is that brackets surround an infinitely repeating+--   sequence of digits.+--+--   We used to accept @2.@ with no trailing digits, but no longer do.+--   See https://github.com/disco-lang/disco/issues/245 and Note+--   [Trailing period].+decimal :: Parser Rational+decimal =+  lexeme+    ( readDecimal+        <$> some digit+        <* char '.'+        <*> fractionalPart+    )+ where+  digit = satisfy isDigit+  fractionalPart =+    -- either some digits optionally followed by bracketed digits...+    (,) <$> some digit <*> optional (brackets (some digit))+      -- ...or just bracketed digits.+      <|> ([],) <$> (Just <$> brackets (some digit))++  readDecimal a (b, mrep) =+    read a % 1 -- integer part++      -- next part is just b/10^n+      + (if null b then 0 else read b) % (10 ^ length b)+      -- repeating part+      + readRep (length b) mrep++  readRep _ Nothing = 0+  readRep offset (Just rep) = read rep % (10 ^ offset * (10 ^ length rep - 1))++-- If s = 0.[rep] then 10^(length rep) * s = rep.[rep], so+-- 10^(length rep) * s - s = rep, so+--+--   s = rep/(10^(length rep) - 1).+--+-- We also have to divide by 10^(length b) to shift it over+-- past any non-repeating prefix.++-- ~~~~ Note [Trailing period]+--+-- We used to accept numbers with nothing after the trailing period,+-- such as @2.@. However, this caused some problems with parsing:+--+--   - First, https://github.com/disco-lang/disco/issues/99 which we+--     solved by making sure there was not another period after the+--     trailing period.+--   - Next, https://github.com/disco-lang/disco/issues/245.+--+-- I first tried solving #245 by disallowing *any* operator character+-- after the trailing period, but then some tests in the test suite+-- started failing, where we had written things like @1./(10^5)@.  The+-- problem is that when a period is followed by another operator+-- symbol, sometimes we might want them to be parsed as an operator+-- (as in @2.-4@, #245), and sometimes we might not (as in+-- @1./(10^5)@).  So in the end it seems simpler and cleaner to+-- require at least a 0 digit after the period --- just like pretty+-- much every other programming language and just like standard+-- mathematical practice.++-- | Parse a reserved word.+reserved :: String -> Parser ()+reserved w = (lexeme . try) $ string w *> notFollowedBy alphaNumChar++-- | The list of all reserved words.+reservedWords :: [String]+reservedWords =+  [ "unit"+  , "true"+  , "false"+  , "True"+  , "False"+  , "let"+  , "in"+  , "is"+  , "if"+  , "when"+  , "otherwise"+  , "and"+  , "or"+  , "mod"+  , "choose"+  , "implies"+  , "iff"+  , "min"+  , "max"+  , "union"+  , "∪"+  , "intersect"+  , "∩"+  , "subset"+  , "⊆"+  , "elem"+  , "∈"+  , "enumerate"+  , "count"+  , "divides"+  , "Void"+  , "Unit"+  , "Bool"+  , "Boolean"+  , "Proposition"+  , "Prop"+  , "Char"+  , "Nat"+  , "Natural"+  , "Int"+  , "Integer"+  , "Frac"+  , "Fractional"+  , "Rational"+  , "Fin"+  , "List"+  , "Bag"+  , "Set"+  , "Graph"+  , "Map"+  , "N"+  , "Z"+  , "F"+  , "Q"+  , "ℕ"+  , "ℤ"+  , "𝔽"+  , "ℚ"+  , "∀"+  , "forall"+  , "∃"+  , "exists"+  , "type"+  , "import"+  , "using"+  ]++-- | Parse an identifier, i.e. any non-reserved string beginning with+--   a given type of character and continuing with alphanumerics,+--   underscores, and apostrophes.+identifier :: Parser Char -> Parser String+identifier begin = (lexeme . try) (p >>= check) <?> "variable name"+ where+  p = (:) <$> begin <*> many identChar+  identChar = alphaNumChar <|> oneOf "_'"+  check x+    | x `elem` reservedWords = do+        -- back up to beginning of bad token to report correct position+        updateParserState (\s -> s {stateOffset = stateOffset s - length x})+        customFailure $ ReservedVarName x+    | otherwise = return x++-- | Parse an 'identifier' and turn it into a 'Name'.+ident :: Parser (Name Term)+ident = string2Name <$> identifier letterChar++------------------------------------------------------------+-- Parser++-- | Results from parsing a block of top-level things.+data TLResults = TLResults+  { _tlDecls :: [Decl]+  , _tlDocs :: [(Name Term, [DocThing])]+  , _tlTerms :: [Term]+  }++emptyTLResults :: TLResults+emptyTLResults = TLResults [] [] []++makeLenses ''TLResults++-- | Parse the entire input as a module (with leading whitespace and+--   no leftovers).+wholeModule :: LoadingMode -> Parser Module+wholeModule = between sc eof . parseModule++-- | Parse an entire module (a list of declarations ended by+--   semicolons).  The 'LoadingMode' parameter tells us whether to+--   include or replace any language extensions enabled at the top+--   level.  We include them when parsing a module entered at the+--   REPL, and replace them when parsing a standalone module.+parseModule :: LoadingMode -> Parser Module+parseModule mode = do+  exts <- S.fromList <$> many parseExtension+  let extFun = case mode of+        Standalone -> withExts+        REPL -> withAdditionalExts++  extFun exts $ do+    imports <- many parseImport+    topLevel <- many parseTopLevel+    let theMod = mkModule exts imports topLevel+    return theMod+ where+  groupTLs :: [DocThing] -> [TopLevel] -> TLResults+  groupTLs _ [] = emptyTLResults+  groupTLs revDocs (TLDoc doc : rest) =+    groupTLs (doc : revDocs) rest+  groupTLs revDocs (TLDecl decl@(DType (TypeDecl x _)) : rest) =+    groupTLs [] rest+      & tlDecls %~ (decl :)+      & tlDocs %~ ((x, reverse revDocs) :)+  groupTLs revDocs (TLDecl decl@(DTyDef (TypeDefn x _ _)) : rest) =+    groupTLs [] rest+      & tlDecls %~ (decl :)+      & tlDocs %~ ((string2Name x, reverse revDocs) :)+  groupTLs _ (TLDecl defn : rest) =+    groupTLs [] rest+      & tlDecls %~ (defn :)+  groupTLs _ (TLExpr t : rest) =+    groupTLs [] rest & tlTerms %~ (t :)++  defnGroups :: [Decl] -> [Decl]+  defnGroups [] = []+  defnGroups (d@DType {} : ds) = d : defnGroups ds+  defnGroups (d@DTyDef {} : ds) = d : defnGroups ds+  defnGroups (DDefn (TermDefn x bs) : ds) = DDefn (TermDefn x (bs ++ concatMap (\(TermDefn _ cs) -> cs) grp)) : defnGroups rest+   where+    (grp, rest) = matchDefn ds+    matchDefn :: [Decl] -> ([TermDefn], [Decl])+    matchDefn (DDefn t@(TermDefn x' _) : ds2) | x == x' = (t : ts, ds2')+     where+      (ts, ds2') = matchDefn ds2+    matchDefn ds2 = ([], ds2)++  mkModule exts imps tls = Module exts imps (defnGroups decls) docs terms+   where+    TLResults decls docs terms = groupTLs [] tls++-- | Parse an extension.+parseExtension :: Parser Ext+parseExtension =+  L.nonIndented sc $+    reserved "using" *> parseExtName++-- | Parse the name of a language extension (case-insensitive).+parseExtName :: Parser Ext+parseExtName = choice (map parseOneExt allExtsList) <?> "language extension name"+ where+  parseOneExt ext = ext <$ lexeme (string' (show ext) :: Parser String)++-- | Parse an import, of the form @import <modulename>@.+parseImport :: Parser String+parseImport =+  L.nonIndented sc $+    reserved "import" *> parseModuleName++-- | Parse the name of a module.+parseModuleName :: Parser String+parseModuleName =+  lexeme $+    intercalate "/" <$> (some (alphaNumChar <|> oneOf "_-") `sepBy` char '/') <* optional (string ".disco")++-- | Parse a top level item (either documentation or a declaration),+--   which must start at the left margin.+parseTopLevel :: Parser TopLevel+parseTopLevel =+  L.nonIndented sc $+    TLDoc <$> parseDocThing+      <|> TLDecl <$> parseDecl -- See Note [Parsing definitions and top-level expressions]+      <|> TLExpr <$> thenIndented parseTerm++-- ~~~~ Note [Parsing definitions and top-level expressions]+--+-- The beginning of a definition might look the same as an+-- expression.  e.g. is f(x,y) the start of a definition of f, or an+-- expression with a function call?  We used to therefore wrap+-- 'parseDecl' in 'try'.  The problem is that if a definition has a+-- syntax error on the RHS, it would fail, backtrack, then try+-- parsing a top-level expression and fail when it got to the =+-- sign, giving an uninformative parse error message.+-- See https://github.com/disco-lang/disco/issues/346.+--+-- The solution is that we now do more careful backtracking within+-- parseDecl itself: when parsing a definition, we only backtrack if+-- we don't get a complete LHS + '=' sign; once we start parsing the+-- RHS of a definition we no longer backtrack, since it can't+-- possibly be a valid top-level expression.++-- | Parse a documentation item: either a group of lines beginning+--   with @|||@ (text documentation), or a group beginning with @!!!@+--   (checked examples/properties).+parseDocThing :: Parser DocThing+parseDocThing =+  DocString <$> some parseDocString+    <|> DocProperty <$> parseProperty++-- | Parse one line of documentation beginning with @|||@.+parseDocString :: Parser String+parseDocString =+  label "documentation" $+    L.nonIndented sc $+      string "|||"+        *> takeWhileP Nothing (`elem` " \t")+        *> takeWhileP Nothing (`notElem` "\r\n")+        <* sc++-- Note we use string "|||" rather than symbol "|||" because we+-- don't want it to consume whitespace afterwards (in particular a+-- line with ||| by itself would cause symbol "|||" to consume the+-- newline).++-- | Parse a top-level property/unit test, which is just @!!!@+--   followed by an arbitrary term.+parseProperty :: Parser Term+parseProperty = label "property" $ L.nonIndented sc $ do+  _ <- symbol "!!!"+  indented parseTerm++-- | Parse a single top-level declaration (either a type declaration+--   or single definition clause).+parseDecl :: Parser Decl+parseDecl = try (DType <$> parseTyDecl) <|> DDefn <$> parseDefn <|> DTyDef <$> parseTyDefn++-- | Parse a top-level type declaration of the form @x : ty@.+parseTyDecl :: Parser TypeDecl+parseTyDecl =+  label "type declaration" $+    TypeDecl <$> ident <*> indented (colon *> parsePolyTy)++-- | Parse a definition of the form @x pat1 .. patn = t@.+parseDefn :: Parser TermDefn+parseDefn =+  label "definition" $+    (\(x, ps) body -> TermDefn x [bind ps body])+      -- Only backtrack if we don't get a complete 'LHS ='.  Once we see+      -- an = sign, commit to parsing a definition, because it can't be a+      -- valid standalone expression anymore.  If the RHS fails, we don't+      -- want to backtrack, we just want to display the parse error.+      <$> try ((,) <$> ident <*> indented (many parseAtomicPattern) <* reservedOp "=")+      <*> indented parseTerm++-- | Parse the definition of a user-defined algebraic data type.+parseTyDefn :: Parser TypeDefn+parseTyDefn = label "type defintion" $ do+  reserved "type"+  indented $ do+    name <- parseTyDef+    args <- fromMaybe [] <$> optional (parens $ parseTyVarName `sepBy1` comma)+    _ <- reservedOp "="+    TypeDefn name args <$> parseType++-- | Parse the entire input as a term (with leading whitespace and+--   no leftovers).+term :: Parser Term+term = between sc eof parseTerm++-- | Parse a term, consisting of a @parseTerm'@ optionally+--   followed by an ascription.+parseTerm :: Parser Term+parseTerm =+  -- trace "parseTerm" $+  ascribe <$> parseTerm' <*> optional (label "type annotation" $ colon *> parsePolyTy)+ where+  ascribe t Nothing = t+  ascribe t (Just ty) = TAscr t ty++-- | Parse a non-atomic, non-ascribed term.+parseTerm' :: Parser Term+parseTerm' =+  label "expression" $+    parseQuantified+      <|> parseLet+      <|> parseExpr+      <|> parseAtom++-- | Parse an atomic term.+parseAtom :: Parser Term+parseAtom =+  label "expression" $+    parseUnit+      <|> TBool True <$ (reserved "true" <|> reserved "True")+      <|> TBool False <$ (reserved "false" <|> reserved "False")+      <|> TChar <$> lexeme (between (char '\'') (char '\'') L.charLiteral)+      <|> TString <$> lexeme (char '"' >> manyTill L.charLiteral (char '"'))+      <|> TWild <$ try parseWild+      <|> TPrim <$> try parseStandaloneOp+      -- Note primitives are NOT reserved words, so they are just parsed+      -- as identifiers.  This means that it is possible to shadow a+      -- primitive in a local context, as it should be.  Vars are turned+      -- into prims at scope-checking time: if a var is not in scope but+      -- there is a prim of that name then it becomes a TPrim.  See the+      -- 'typecheck Infer (TVar x)' case in Disco.Typecheck.+      <|> TVar <$> ident+      <|> TPrim <$> (ensureEnabled Primitives *> parsePrim)+      <|> TRat <$> try decimal+      <|> TNat <$> natural+      <|> parseTypeOp+      <|> TApp (TPrim PrimFloor) . TParens <$> fbrack parseTerm+      <|> TApp (TPrim PrimCeil) . TParens <$> cbrack parseTerm+      <|> parseCase+      <|> try parseAbs+      <|> bagdelims (parseContainer BagContainer)+      <|> braces (parseContainer SetContainer)+      <|> brackets (parseContainer ListContainer)+      <|> tuple <$> parens (parseTerm `sepBy1` comma)++parseAbs :: Parser Term+parseAbs = TApp (TPrim PrimAbs) <$> (pipe *> parseTerm <* pipe)++parseUnit :: Parser Term+parseUnit = TUnit <$ (reserved "unit" <|> void (symbol "■"))++-- | Parse a wildcard, which is an underscore that isn't the start of+--   an identifier.+parseWild :: Parser ()+parseWild =+  (lexeme . try . void) $+    string "_" <* notFollowedBy (alphaNumChar <|> oneOf "_'")++-- | Parse a standalone operator name with tildes indicating argument+--   slots, e.g. ~+~ for the addition operator.+parseStandaloneOp :: Parser Prim+parseStandaloneOp = asum $ concatMap mkStandaloneOpParsers (concat opTable)+ where+  mkStandaloneOpParsers :: OpInfo -> [Parser Prim]+  mkStandaloneOpParsers (OpInfo (UOpF Pre uop) syns _) =+    map (\syn -> PrimUOp uop <$ try (lexeme (string syn >> char '~'))) syns+  mkStandaloneOpParsers (OpInfo (UOpF Post uop) syns _) =+    map (\syn -> PrimUOp uop <$ try (lexeme (char '~' >> string syn))) syns+  mkStandaloneOpParsers (OpInfo (BOpF _ bop) syns _) =+    map (\syn -> PrimBOp bop <$ try (lexeme (char '~' >> string syn >> char '~'))) syns++-- XXX TODO: improve the above so it first tries to parse a ~,+--   then parses any postfix or infix thing; or else it looks for+--   a prefix thing followed by a ~.  This will get rid of the+--   need for 'try' and also potentially improve error messages.+--   The below may come in useful.++-- flatOpTable = concat opTable++-- prefixOps  = [ (uop, syns) | (OpInfo (UOpF Pre uop) syns _)  <- flatOpTable ]+-- postfixOps = [ (uop, syns) | (OpInfo (UOpF Post uop) syns _) <- flatOpTable ]+-- infixOps   = [ (bop, syns) | (OpInfo (BOpF _ bop) syns _)    <- flatOpTable ]++-- | Parse a primitive name starting with a $.+parsePrim :: Parser Prim+parsePrim = do+  void (char '$')+  x <- identifier letterChar+  case find ((== x) . primSyntax) primTable of+    Just (PrimInfo p _ _) -> return p+    Nothing -> fail ("Unrecognized primitive $" ++ x)++-- | Parse a container, like a literal list, set, bag, or a+--   comprehension (not including the square or curly brackets).+--+-- @+-- <container-contents>+--   ::= <nonempty-container> | empty+--+-- <nonempty-container> ::= <term> <container-end>+--+-- <container-end>+--   ::= '|' <comprehension>+--        | (',' <term>)* [ <ellipsis> ]+--+-- <comprehension> ::= <qual> [ ',' <qual> ]*+--+-- <qual>+--   ::= <ident> 'in' <term>+--     | <term>+--+-- <ellipsis> ::= [ ',' ] '..' [ ',' ] <term>+-- @+parseContainer :: Container -> Parser Term+parseContainer c = nonEmptyContainer <|> return (TContainer c [] Nothing)+ where+  -- Careful to do this without backtracking, since backtracking can+  -- lead to bad performance in certain pathological cases (for+  -- example, a very deeply nested list).++  -- Any non-empty container starts with a term, followed by some+  -- remainder (which could either be the rest of a literal+  -- container, or a container comprehension).  If there is no+  -- remainder just return a "singleton" container (which could+  -- include a trailing ellipsis + final term).+  nonEmptyContainer = parseRepTerm >>= containerRemainder++  parseRepTerm = do+    t <- parseTerm+    n <- optional $ do+      guard (c == BagContainer)+      void hash+      parseTerm+    return (t, n)++  -- The remainder of a container after the first term starts with+  -- either a pipe (for a comprehension) or a comma (for a literal+  -- container).+  containerRemainder :: (Term, Maybe Term) -> Parser Term+  containerRemainder (t, n) =+    (guard (isNothing n) *> parseContainerComp c t) <|> parseLitContainerRemainder t n++  parseLitContainerRemainder :: Term -> Maybe Term -> Parser Term+  parseLitContainerRemainder t n = do+    -- Wrapping the (',' term) production in 'try' is important: if+    -- it consumes a comma but then fails when parsing a term, we+    -- want to be able to backtrack so we can potentially parse an+    -- ellipsis beginning with a comma.+    ts <- many (try (comma *> parseRepTerm))+    e <- optional parseEllipsis+    return $ TContainer c ((t, n) : ts) e++-- | Parse an ellipsis at the end of a literal list, of the form+--   @.. t@.  Any number > 1 of dots may be used, just for fun.+parseEllipsis :: Parser (Ellipsis Term)+parseEllipsis = optional comma *> ellipsis *> optional comma *> (Until <$> parseTerm)++-- | Parse the part of a list comprehension after the | (without+--   square brackets), i.e. a list of qualifiers.+--+--   @q [,q]*@+parseContainerComp :: Container -> Term -> Parser Term+parseContainerComp c t = do+  _ <- pipe+  qs <- toTelescope <$> (parseQual `sepBy` comma)+  return (TContainerComp c $ bind qs t)++-- | Parse a qualifier in a comprehension: either a binder @x in t@ or+--   a guard @t@.+parseQual :: Parser Qual+parseQual = parseSelection <|> parseQualGuard+ where+  parseSelection =+    label "membership expression (x in ...)" $+      QBind <$> try (ident <* selector) <*> (embed <$> parseTerm)+  selector = reservedOp "<-" <|> reserved "in"++  parseQualGuard =+    label "boolean expression" $+      QGuard . embed <$> parseTerm++-- | Turn a parenthesized list of zero or more terms into the+--   appropriate syntax node: one term @(t)@ is just the term itself+--   (but we record the fact that it was parenthesized, in order to+--   correctly turn juxtaposition into multiplication); two or more+--   terms @(t1,t2,...)@ are a tuple.+tuple :: [Term] -> Term+tuple [x] = TParens x+tuple t = TTup t++-- | Parse a quantified abstraction (λ, ∀, ∃).+parseQuantified :: Parser Term+parseQuantified = do+  q <- parseQuantifier+  TAbs q <$> (bind <$> parseArgs (q /= Lam) <*> (dot *> parseTerm))+ where+  parseArgs notLam = (parsePattern notLam `sepBy1` comma) >>= checkMulti+   where+    -- ∀ and ∃ can have multiple bindings separated by commas,+    -- like ∀ x:N, y:N. ...  but we don't allow this for λ.++    checkMulti :: [Pattern] -> Parser [Pattern]+    checkMulti ps+      | notLam = return ps+      | otherwise = case ps of+          [p] -> return [p]+          _ -> customFailure MultiArgLambda++-- | Parse a quantifier symbol (lambda, forall, or exists).+parseQuantifier :: Parser Quantifier+parseQuantifier =+  Lam <$ lambda+    <|> All <$ forall+    <|> Ex <$ exists++-- | Parse a let expression (@let x1 = t1, x2 = t2, ... in t@).+parseLet :: Parser Term+parseLet =+  TLet+    <$> ( reserved "let"+            *> ( bind+                  <$> (toTelescope <$> (parseBinding `sepBy` comma))+                  <*> (reserved "in" *> parseTerm)+               )+        )++-- | Parse a single binding (@x [ : ty ] = t@).+parseBinding :: Parser Binding+parseBinding = do+  x <- ident+  mty <- optional (colon *> parsePolyTy)+  t <- symbol "=" *> (embed <$> parseTerm)+  return $ Binding (embed <$> mty) x t++-- | Parse a case expression.+parseCase :: Parser Term+parseCase =+  between (symbol "{?") (symbol "?}") $+    TCase <$> parseBranch `sepBy` comma++-- | Parse one branch of a case expression.+parseBranch :: Parser Branch+parseBranch = flip bind <$> parseTerm <*> parseGuards++-- | Parse the list of guards in a branch.  @otherwise@ can be used+--   interchangeably with an empty list of guards.+parseGuards :: Parser (Telescope Guard)+parseGuards = (TelEmpty <$ reserved "otherwise") <|> (toTelescope <$> many parseGuard)++-- | Parse a single guard (@if@, @if ... is ...@, or @let@)+parseGuard :: Parser Guard+parseGuard = parseGCond <|> parseGLet+ where+  guardWord = reserved "if" <|> reserved "when"+  parseGCond = do+    guardWord+    t <- parseTerm+    parseGPat t <|> parseGBool t+  parseGPat t = GPat (embed t) <$> (reserved "is" *> parsePattern False)+  parseGBool t = pure $ GBool (embed t)+  parseGLet = GLet <$> (reserved "let" *> parseBinding)++-- | Parse an atomic pattern, by parsing a term and then attempting to+--   convert it to a pattern.+parseAtomicPattern :: Parser Pattern+parseAtomicPattern = label "pattern" $ do+  t <- parseAtom+  case termToPattern t of+    Nothing -> customFailure $ InvalidPattern (OT t)+    Just p -> return $ maybe p (PNonlinear p) (findDuplicatePVar p)++-- | Parse a pattern, by parsing a term and then attempting to convert+--   it to a pattern.  The Bool parameter says whether to require+--   a type ascription.+parsePattern :: Bool -> Parser Pattern+parsePattern requireAscr = label "pattern" $ do+  t <- parseTerm+  case termToPattern t of+    Nothing -> customFailure $ InvalidPattern (OT t)+    Just p+      | requireAscr && not (hasAscr p) -> customFailure MissingAscr+      | otherwise -> return $ maybe p (PNonlinear p) (findDuplicatePVar p)++-- | Does a pattern either have a top-level ascription, or consist of+--   a tuple with each component recursively having ascriptions?+--   This is required for patterns bound by ∀ and ∃ quantifiers.+hasAscr :: Pattern -> Bool+hasAscr PAscr {} = True+hasAscr (PTup ps) = all hasAscr ps+hasAscr _ = False++-- | Lazy monadic variant of find.+findM :: Monad m => (a -> m (Maybe b)) -> [a] -> m (Maybe b)+findM _ [] = return Nothing+findM p (a : as) = do+  b <- p a+  case b of+    Just x -> return $ Just x+    _ -> findM p as++-- | Does a pattern have the same variable repeated more than once?+findDuplicatePVar :: Pattern -> Maybe (Name Term)+findDuplicatePVar = flip evalState S.empty . go+ where+  go :: Pattern -> State (Set String) (Maybe (Name Term))+  go (PVar x) = do+    let xName = name2String x+    seen <- gets (S.member xName)+    if seen+      then return (Just x)+      else do+        modify (S.insert xName)+        return Nothing+  go (PAscr p _) = go p+  go (PTup ps) = findM go ps+  go (PInj _ p) = go p+  go (PCons p1 p2) = findM go [p1, p2]+  go (PList ps) = findM go ps+  go (PAdd _ p _) = go p+  go (PMul _ p _) = go p+  go (PSub p _) = go p+  go (PNeg p) = go p+  go (PFrac p1 p2) = findM go [p1, p2]+  go _ = return Nothing++-- | Attempt converting a term to a pattern.+termToPattern :: Term -> Maybe Pattern+termToPattern TWild = Just PWild+termToPattern (TVar x) = Just $ PVar x+termToPattern (TParens t) = termToPattern t+termToPattern TUnit = Just PUnit+termToPattern (TBool b) = Just $ PBool b+termToPattern (TNat n) = Just $ PNat n+termToPattern (TChar c) = Just $ PChar c+termToPattern (TString s) = Just $ PString s+termToPattern (TTup ts) = PTup <$> mapM termToPattern ts+termToPattern (TApp (TVar i) t)+  | i == string2Name "left" = PInj L <$> termToPattern t+  | i == string2Name "right" = PInj R <$> termToPattern t+-- termToPattern (TInj s t)  = PInj s <$> termToPattern t++termToPattern (TAscr t s) = case s of+  Forall (unsafeUnbind -> ([], s')) -> PAscr <$> termToPattern t <*> pure s'+  _ -> Nothing+termToPattern (TBin Cons t1 t2) =+  PCons <$> termToPattern t1 <*> termToPattern t2+termToPattern (TBin Add t1 t2) =+  case (termToPattern t1, termToPattern t2) of+    (Just p, _)+      | length (toListOf fvAny p) == 1+          && null (toListOf fvAny t2) ->+          Just $ PAdd L p t2+    (_, Just p)+      | length (toListOf fvAny p) == 1+          && null (toListOf fvAny t1) ->+          Just $ PAdd R p t1+    _ -> Nothing+-- If t1 is a pattern binding one variable, and t2 has no fvs,+-- this can be a PAdd L.  Also vice versa for PAdd R.++termToPattern (TBin Mul t1 t2) =+  case (termToPattern t1, termToPattern t2) of+    (Just p, _)+      | length (toListOf fvAny p) == 1+          && null (toListOf fvAny t2) ->+          Just $ PMul L p t2+    (_, Just p)+      | length (toListOf fvAny p) == 1+          && null (toListOf fvAny t1) ->+          Just $ PMul R p t1+    _ -> Nothing+-- If t1 is a pattern binding one variable, and t2 has no fvs,+-- this can be a PMul L.  Also vice versa for PMul R.++termToPattern (TBin Sub t1 t2) =+  case termToPattern t1 of+    Just p+      | length (toListOf fvAny p) == 1+          && null (toListOf fvAny t2) ->+          Just $ PSub p t2+    _ -> Nothing+-- If t1 is a pattern binding one variable, and t2 has no fvs,+-- this can be a PSub.++-- For now we don't handle the case of t - p, since it seems+-- less useful (and desugaring it would require extra code since+-- subtraction is not commutative).++termToPattern (TBin Div t1 t2) =+  PFrac <$> termToPattern t1 <*> termToPattern t2+termToPattern (TUn Neg t) = PNeg <$> termToPattern t+termToPattern (TContainer ListContainer ts Nothing) =+  PList <$> mapM (termToPattern . fst) ts+termToPattern _ = Nothing++-- | Parse an expression built out of unary and binary operators.+parseExpr :: Parser Term+parseExpr = fixJuxtMul . fixChains <$> (makeExprParser parseAtom table <?> "expression")+ where+  table =+    -- Special case for function application, with highest+    -- precedence.  Note that we parse all juxtaposition as+    -- function application first; we later go through and turn+    -- some into multiplication (fixing up the precedence+    -- appropriately) based on a syntactic analysis.+    [InfixL (TApp <$ string "")]+      -- get all other operators from the opTable+      : (map . concatMap) mkOpParser opTable++  mkOpParser :: OpInfo -> [Operator Parser Term]+  mkOpParser (OpInfo op syns _) = concatMap (withOpFixity op) syns++  -- Only parse unary operators consisting of operator symbols.+  -- Alphabetic unary operators (i.e. 'not') will be parsed as+  -- applications of variable names, since if they are parsed here+  -- they will incorrectly parse even when they are a prefix of a+  -- variable name.+  withOpFixity (UOpF fx op) syn+    | any isAlpha syn = []+    | otherwise = [ufxParser fx ((reservedOp syn <?> "operator") >> return (TUn op))]+  withOpFixity (BOpF fx op) syn =+    [bfxParser fx ((reservedOp syn <?> "operator") >> return (TBin op))]++  ufxParser Pre = Prefix+  ufxParser Post = Postfix++  bfxParser InL = InfixL+  bfxParser InR = InfixR+  bfxParser In = InfixN++  isChainable op = op `elem` [Eq, Neq, Lt, Gt, Leq, Geq, Divides]++  -- Comparison chains like 3 < x < 5 first get parsed as 3 < (x <+  -- 5), which does not make sense.  This function looks for such+  -- nested comparison operators and turns them into a TChain.+  fixChains (TUn op t) = TUn op (fixChains t)+  fixChains (TBin op t1 (TBin op' t21 t22))+    | isChainable op && isChainable op' = TChain t1 (TLink op t21 : getLinks op' t22)+  fixChains (TBin op t1 t2) = TBin op (fixChains t1) (fixChains t2)+  fixChains (TApp t1 t2) = TApp (fixChains t1) (fixChains t2)+  -- Only recurse as long as we see TUn, TBin, or TApp which could+  -- have been generated by the expression parser.  If we see+  -- anything else we can stop.+  fixChains e = e++  getLinks op (TBin op' t1 t2)+    | isChainable op' = TLink op t1 : getLinks op' t2+  getLinks op e = [TLink op (fixChains e)]++  -- Find juxtapositions (parsed as function application) which+  -- syntactically have either a literal Nat or a parenthesized+  -- expression containing an operator as the LHS, and turn them+  -- into multiplications.  Then fix up the parse tree by rotating+  -- newly created multiplications up until their precedence is+  -- higher than the thing above them.++  fixJuxtMul :: Term -> Term++  -- Just recurse through TUn or TBin and fix precedence on the way back up.+  fixJuxtMul (TUn op t) = fixPrec $ TUn op (fixJuxtMul t)+  fixJuxtMul (TBin op t1 t2) = fixPrec $ TBin op (fixJuxtMul t1) (fixJuxtMul t2)+  -- Possibly turn a TApp into a multiplication, if the LHS looks+  -- like a multiplicative term.  However, we must be sure to+  -- \*first* recursively fix the subterms (particularly the+  -- left-hand one) *before* doing this analysis.  See+  -- <https://github.com/disco-lang/disco/issues/71> .+  fixJuxtMul (TApp t1 t2)+    | isMultiplicativeTerm t1' = fixPrec $ TBin Mul t1' t2'+    | otherwise = fixPrec $ TApp t1' t2'+   where+    t1' = fixJuxtMul t1+    t2' = fixJuxtMul t2++  -- Otherwise we can stop recursing, since anything other than TUn,+  -- TBin, or TApp could not have been produced by the expression+  -- parser.+  fixJuxtMul t = t++  -- A multiplicative term is one that looks like either a natural+  -- number literal, or a unary or binary operation (optionally+  -- parenthesized).  For example, 3, (-2), and (x + 5) are all+  -- multiplicative terms, so 3x, (-2)x, and (x + 5)x all get parsed+  -- as multiplication.  On the other hand, (x y) is always parsed+  -- as function application, even if x and y both turn out to have+  -- numeric types; a variable like x does not count as a+  -- multiplicative term.  Likewise, (x y) z is parsed as function+  -- application, since (x y) is not a multiplicative term: it is+  -- parenthezised, but contains a TApp rather than a TBin or TUn.+  isMultiplicativeTerm :: Term -> Bool+  isMultiplicativeTerm (TNat _) = True+  isMultiplicativeTerm TUn {} = True+  isMultiplicativeTerm TBin {} = True+  isMultiplicativeTerm (TParens t) = isMultiplicativeTerm t+  isMultiplicativeTerm _ = False++  -- Fix precedence by bubbling up any new TBin terms whose+  -- precedence is less than that of the operator above them.  We+  -- don't worry at all about fixing associativity, just precedence.++  fixPrec :: Term -> Term++  -- e.g.  2y! --> (2@y)! --> fixup --> 2 * (y!)+  fixPrec (TUn uop (TBin bop t1 t2))+    | bPrec bop < uPrec uop = case uopMap M.! uop of+        OpInfo (UOpF Pre _) _ _ -> TBin bop (TUn uop t1) t2+        OpInfo (UOpF Post _) _ _ -> TBin bop t1 (TUn uop t2)+        _ -> error "Impossible! In fixPrec, uopMap contained OpInfo (BOpF ...)"+  fixPrec (TBin bop1 (TBin bop2 t1 t2) t3)+    | bPrec bop2 < bPrec bop1 = TBin bop2 t1 (fixPrec $ TBin bop1 t2 t3)+  -- e.g. x^2y --> x^(2@y) --> x^(2*y) --> (x^2) * y+  fixPrec (TBin bop1 t1 (TBin bop2 t2 t3))+    | bPrec bop2 < bPrec bop1 = TBin bop2 (fixPrec $ TBin bop1 t1 t2) t3+  fixPrec t = t++-- | Parse an atomic type.+parseAtomicType :: Parser Type+parseAtomicType =+  label "type" $+    TyVoid <$ reserved "Void"+      <|> TyUnit <$ reserved "Unit"+      <|> TyBool <$ (reserved "Boolean" <|> reserved "Bool")+      <|> TyProp <$ (reserved "Proposition" <|> reserved "Prop")+      <|> TyC <$ reserved "Char"+      -- <|> try parseTyFin+      <|> TyN <$ (reserved "Natural" <|> reserved "Nat" <|> reserved "N" <|> reserved "ℕ")+      <|> TyZ <$ (reserved "Integer" <|> reserved "Int" <|> reserved "Z" <|> reserved "ℤ")+      <|> TyF <$ (reserved "Fractional" <|> reserved "Frac" <|> reserved "F" <|> reserved "𝔽")+      <|> TyQ <$ (reserved "Rational" <|> reserved "Q" <|> reserved "ℚ")+      <|> TyCon <$> parseCon <*> (fromMaybe [] <$> optional (parens (parseType `sepBy1` comma)))+      <|> TyVar <$> parseTyVar+      <|> parens parseType++-- parseTyFin :: Parser Type+-- parseTyFin = TyFin  <$> (reserved "Fin" *> natural)+--          <|> TyFin  <$> (lexeme (string "Z" <|> string "ℤ") *> natural)++parseCon :: Parser Con+parseCon =+  CList <$ reserved "List"+    <|> CBag <$ reserved "Bag"+    <|> CSet <$ reserved "Set"+    <|> CGraph <$ reserved "Graph"+    <|> CMap <$ reserved "Map"+    <|> CUser <$> parseTyDef++parseTyDef :: Parser String+parseTyDef = identifier upperChar++parseTyVarName :: Parser String+parseTyVarName = identifier lowerChar++parseTyVar :: Parser (Name Type)+parseTyVar = string2Name <$> parseTyVarName++parsePolyTy :: Parser PolyType+parsePolyTy = closeType <$> parseType++-- | Parse a type expression built out of binary operators.+parseType :: Parser Type+parseType = makeExprParser parseAtomicType table+ where+  table =+    [+      [ infixR "*" (:*:)+      , infixR "×" (:*:)+      ]+    ,+      [ infixR "+" (:+:)+      , infixR "⊎" (:+:)+      ]+    ,+      [ infixR "->" (:->:)+      , infixR "→" (:->:)+      ]+    ]++  infixR name fun = InfixR (reservedOp name >> return fun)++parseTyOp :: Parser TyOp+parseTyOp =+  Enumerate <$ reserved "enumerate"+    <|> Count <$ reserved "count"  parseTypeOp :: Parser Term parseTypeOp = TTyOp <$> parseTyOp <*> parseAtomicType
src/Disco/Pretty.hs view
@@ -1,8 +1,9 @@-{-# LANGUAGE DerivingVia               #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoMonomorphismRestriction #-}-{-# LANGUAGE OverloadedStrings         #-} ------------------------------------------------------------------------------+-- TODO: the calls to 'error' should be replaced with logging/error capabilities.+ -- | -- Module      :  Disco.Pretty -- Copyright   :  disco team and contributors@@ -11,40 +12,31 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Various pretty-printing facilities for disco.------------------------------------------------------------------------------------- TODO: the calls to 'error' should be replaced with logging/error capabilities.--module Disco.Pretty-  ( module Disco.Pretty.DSL-  , module Disco.Pretty-  , module Disco.Pretty.Prec-  , Doc-  )-  where--import           Prelude                          hiding ((<>))--import           Data.Bifunctor-import           Data.Char                        (isAlpha)-import           Data.Map                         (Map)-import qualified Data.Map                         as M-import           Data.Ratio-import           Data.Set                         (Set)-import qualified Data.Set                         as S--import           Disco.Effects.LFresh-import           Polysemy--import           Polysemy.Reader+module Disco.Pretty (+  module Disco.Pretty.DSL,+  module Disco.Pretty,+  module Disco.Pretty.Prec,+  Doc,+)+where -import           Text.PrettyPrint                 (Doc)-import           Unbound.Generics.LocallyNameless (Name)+import Prelude hiding ((<>)) -import           Disco.Pretty.DSL-import           Disco.Pretty.Prec-import           Disco.Syntax.Operators+import Data.Bifunctor+import Data.Char (isAlpha)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Ratio+import Data.Set (Set)+import qualified Data.Set as S+import Disco.Effects.LFresh+import Disco.Pretty.DSL+import Disco.Pretty.Prec+import Disco.Syntax.Operators+import Polysemy+import Polysemy.Reader+import Prettyprinter (Doc)+import Unbound.Generics.LocallyNameless (Name)  ------------------------------------------------------------ -- Utilities for handling precedence and associativity@@ -54,7 +46,7 @@ --   associativity of a term is, and optionally surround it with --   parentheses depending on the precedence and associativity of its --   parent.-withPA :: Member (Reader PA) r => PA -> Sem r Doc -> Sem r Doc+withPA :: Member (Reader PA) r => PA -> Sem r (Doc ann) -> Sem r (Doc ann) withPA pa = mparens pa . setPA pa  -- | Locally set the precedence and associativity within a@@ -65,20 +57,20 @@ -- | Mark a subcomputation as pretty-printing a term on the left of an --   operator (so parentheses can be inserted appropriately, depending --   on the associativity).-lt :: Member (Reader PA) r => Sem r Doc -> Sem r Doc+lt :: Member (Reader PA) r => Sem r (Doc ann) -> Sem r (Doc ann) lt = local (\(PA p _) -> PA p InL)  -- | Mark a subcomputation as pretty-printing a term on the right of --   an operator (so parentheses can be inserted appropriately, --   depending on the associativity).-rt :: Member (Reader PA) r => Sem r Doc -> Sem r Doc+rt :: Member (Reader PA) r => Sem r (Doc ann) -> Sem r (Doc ann) rt = local (\(PA p _) -> PA p InR)  -- | Optionally surround a pretty-printed term with parentheses, --   depending on its precedence and associativity (given as the 'PA' --   argument) and that of its context (given by the ambient 'Reader --   PA' effect).-mparens :: Member (Reader PA) r => PA -> Sem r Doc -> Sem r Doc+mparens :: Member (Reader PA) r => PA -> Sem r (Doc ann) -> Sem r (Doc ann) mparens pa doc = do   parentPA <- ask   (if pa < parentPA then parens else id) doc@@ -87,12 +79,12 @@ -- Pretty type class  class Pretty t where-  pretty :: Members '[Reader PA, LFresh] r => t -> Sem r Doc+  pretty :: Members '[Reader PA, LFresh] r => t -> Sem r (Doc ann)  prettyStr :: Pretty t => t -> Sem r String prettyStr = renderDoc . runLFresh . pretty -pretty' :: Pretty t => t -> Sem r Doc+pretty' :: Pretty t => t -> Sem r (Doc ann) pretty' = runReader initPA . runLFresh . pretty  ------------------------------------------------------------@@ -103,7 +95,7 @@  instance (Pretty k, Pretty v) => Pretty (Map k v) where   pretty m = do-    let es = map (\(k,v) -> pretty k <+> "->" <+> pretty v) (M.assocs m)+    let es = map (\(k, v) -> pretty k <+> "->" <+> pretty v) (M.assocs m)     ds <- setPA initPA $ punctuate "," es     braces (hsep ds) @@ -119,13 +111,13 @@ instance Pretty TyOp where   pretty = \case     Enumerate -> text "enumerate"-    Count     -> text "count"+    Count -> text "count"  -- | Pretty-print a unary operator, by looking up its concrete syntax --   in the 'uopMap'. instance Pretty UOp where   pretty op = case M.lookup op uopMap of-    Just (OpInfo _ (syn:_) _) ->+    Just (OpInfo _ (syn : _) _) ->       text $ syn ++ (if all isAlpha syn then " " else "")     _ -> error $ "UOp " ++ show op ++ " not in uopMap!" @@ -133,8 +125,8 @@ --   in the 'bopMap'. instance Pretty BOp where   pretty op = case M.lookup op bopMap of-    Just (OpInfo _ (syn:_) _) -> text syn-    _                         -> error $ "BOp " ++ show op ++ " not in bopMap!"+    Just (OpInfo _ (syn : _) _) -> text syn+    _ -> error $ "BOp " ++ show op ++ " not in bopMap!"  -------------------------------------------------- -- Pretty-printing decimals@@ -144,19 +136,19 @@ --   in square brackets. prettyDecimal :: Rational -> String prettyDecimal r = printedDecimal+ where+  (n, d) = properFraction r :: (Integer, Rational)+  (expan, len) = digitalExpansion 10 (numerator d) (denominator d)+  printedDecimal+    | length first102 > 101 || length first102 == 101 && last first102 /= 0 =+        show n ++ "." ++ concatMap show (take 100 expan) ++ "..."+    | rep == [0] =+        show n ++ "." ++ (if null pre then "0" else concatMap show pre)+    | otherwise =+        show n ++ "." ++ concatMap show pre ++ "[" ++ concatMap show rep ++ "]"    where-     (n,d) = properFraction r :: (Integer, Rational)-     (expan, len) = digitalExpansion 10 (numerator d) (denominator d)-     printedDecimal-       | length first102 > 101 || length first102 == 101 && last first102 /= 0-         = show n ++ "." ++ concatMap show (take 100 expan) ++ "..."-       | rep == [0]-         = show n ++ "." ++ (if null pre then "0" else concatMap show pre)-       | otherwise-         = show n ++ "." ++ concatMap show pre ++ "[" ++ concatMap show rep ++ "]"-       where-         (pre, rep) = splitAt len expan-         first102   = take 102 expan+    (pre, rep) = splitAt len expan+    first102 = take 102 expan  -- Given a list, find the indices of the list giving the first and -- second occurrence of the first element to repeat, or Nothing if@@ -166,9 +158,9 @@  findRep' :: Ord a => M.Map a Int -> Int -> [a] -> ([a], Int) findRep' _ _ [] = error "Impossible. Empty list in findRep'"-findRep' prevs ix (x:xs)+findRep' prevs ix (x : xs)   | x `M.member` prevs = ([], prevs M.! x)-  | otherwise          = first (x:) $ findRep' (M.insert x ix prevs) (ix+1) xs+  | otherwise = first (x :) $ findRep' (M.insert x ix prevs) (ix + 1) xs  -- | @digitalExpansion b n d@ takes the numerator and denominator of a --   fraction n/d between 0 and 1, and returns a pair of (1) a list of@@ -185,7 +177,7 @@ --   looking for the first time that the remainder repeats. digitalExpansion :: Integer -> Integer -> Integer -> ([Integer], Int) digitalExpansion b n d = digits-  where-    longDivStep (_, r) = (b*r) `divMod` d-    res       = tail $ iterate longDivStep (0,n)-    digits    = first (map fst) (findRep res)+ where+  longDivStep (_, r) = (b * r) `divMod` d+  res = tail $ iterate longDivStep (0, n)+  digits = first (map fst) (findRep res)
src/Disco/Pretty/DSL.hs view
@@ -1,4 +1,5 @@------------------------------------------------------------------------------+{-# OPTIONS_GHC -fno-warn-orphans #-}+ -- | -- Module      :  Disco.Pretty.DSL -- Copyright   :  disco team and contributors@@ -7,26 +8,20 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Adapter DSL on top of Text.PrettyPrint for Applicative pretty-printing.-----------------------------------------------------------------------------------{-# OPTIONS_GHC -fno-warn-orphans #-}- module Disco.Pretty.DSL where -import           Control.Applicative hiding (empty)-import           Data.String         (IsString (..))-import           Prelude             hiding ((<>))--import           Polysemy-import           Polysemy.Reader--import           Text.PrettyPrint    (Doc)-import qualified Text.PrettyPrint    as PP--import           Disco.Pretty.Prec+import Control.Applicative hiding (empty)+import Data.String (IsString (..))+import Disco.Pretty.Prec+import Polysemy+import Polysemy.Reader+import Prettyprinter (Doc)+import qualified Prettyprinter as PP+import Prettyprinter.Internal (Doc (Empty)) -- XXX comment me+import Prettyprinter.Render.String (renderString)+import Prelude hiding ((<>)) -instance IsString (Sem r Doc) where+instance IsString (Sem r (Doc ann)) where   fromString = text  ------------------------------------------------------------@@ -35,73 +30,79 @@ -- Each combinator here mirrors one from Text.PrettyPrint, but -- operates over a generic functor/monad. -vcat :: Applicative f => [f Doc] -> f Doc-vcat ds  = PP.vcat <$> sequenceA ds+vcat :: Applicative f => [f (Doc ann)] -> f (Doc ann)+vcat ds = PP.vcat <$> sequenceA ds -hcat :: Applicative f => [f Doc] -> f Doc-hcat ds  = PP.hcat <$> sequenceA ds+hcat :: Applicative f => [f (Doc ann)] -> f (Doc ann)+hcat ds = PP.hcat <$> sequenceA ds -hsep :: Applicative f => [f Doc] -> f Doc-hsep ds  = PP.hsep <$> sequenceA ds+hsep :: Applicative f => [f (Doc ann)] -> f (Doc ann)+hsep ds = PP.hsep <$> sequenceA ds -parens :: Functor f => f Doc -> f Doc-parens   = fmap PP.parens+parens :: Functor f => f (Doc ann) -> f (Doc ann)+parens = fmap PP.parens -brackets :: Functor f => f Doc -> f Doc+brackets :: Functor f => f (Doc ann) -> f (Doc ann) brackets = fmap PP.brackets -braces :: Functor f => f Doc -> f Doc+braces :: Functor f => f (Doc ann) -> f (Doc ann) braces = fmap PP.braces -bag :: Applicative f => f Doc -> f Doc+bag :: Applicative f => f (Doc ann) -> f (Doc ann) bag p = text "⟅" <> p <> text "⟆" -quotes :: Functor f => f Doc -> f Doc-quotes = fmap PP.quotes+quotes :: Functor f => f (Doc ann) -> f (Doc ann)+quotes = fmap PP.squotes -doubleQuotes :: Functor f => f Doc -> f Doc-doubleQuotes = fmap PP.doubleQuotes+doubleQuotes :: Functor f => f (Doc ann) -> f (Doc ann)+doubleQuotes = fmap PP.dquotes -text :: Applicative m => String -> m Doc-text     = pure . PP.text+text :: Applicative m => String -> m (Doc ann)+text = pure . fromString -integer :: Applicative m => Integer -> m Doc-integer  = pure . PP.integer+integer :: Applicative m => Integer -> m (Doc ann)+integer = pure . PP.pretty -nest :: Functor f => Int -> f Doc -> f Doc+nest :: Functor f => Int -> f (Doc ann) -> f (Doc ann) nest n d = PP.nest n <$> d -hang :: Applicative f => f Doc -> Int -> f Doc -> f Doc-hang d1 n d2 = PP.hang <$> d1 <*> pure n <*> d2+indent :: Functor f => Int -> f (Doc ann) -> f (Doc ann)+indent n d = PP.indent n <$> d -empty :: Applicative m => m Doc-empty    = pure PP.empty+hang :: Applicative f => f (Doc ann) -> Int -> f (Doc ann) -> f (Doc ann)+hang d1 n d2 = d1 <+> nest n d2 -(<+>) :: Applicative f => f Doc -> f Doc -> f Doc+empty :: Applicative m => m (Doc ann)+empty = pure PP.emptyDoc++(<+>) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann) (<+>) = liftA2 (PP.<+>) -(<>) :: Applicative f => f Doc -> f Doc -> f Doc-(<>)  = liftA2 (PP.<>)+(<>) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann)+(<>) = liftA2 (PP.<>) -($+$) :: Applicative f => f Doc -> f Doc -> f Doc-($+$) = liftA2 (PP.$+$)+($+$) :: Applicative f => f (Doc ann) -> f (Doc ann) -> f (Doc ann)+d1 $+$ d2 = f <$> d1 <*> d2+ where+  f x1 Empty = x1+  f x1 x2 = PP.vcat [x1, x2] -punctuate :: Applicative f => f Doc -> [f Doc] -> f [f Doc]+punctuate :: Applicative f => f (Doc ann) -> [f (Doc ann)] -> f [f (Doc ann)] punctuate p ds = map pure <$> (PP.punctuate <$> p <*> sequenceA ds) -intercalate :: Monad f => f Doc -> [f Doc] -> f Doc+intercalate :: Monad f => f (Doc ann) -> [f (Doc ann)] -> f (Doc ann) intercalate p ds = do   ds' <- punctuate p ds   hsep ds' -bulletList :: Applicative f => f Doc -> [f Doc] -> f Doc+bulletList :: Applicative f => f (Doc ann) -> [f (Doc ann)] -> f (Doc ann) bulletList bullet = vcat . map (hang bullet 2)  ------------------------------------------------------------ -- Running a pretty-printer -renderDoc :: Sem (Reader PA ': r) Doc -> Sem r String-renderDoc = fmap PP.render . runReader initPA+renderDoc :: Sem (Reader PA ': r) (Doc ann) -> Sem r String+renderDoc = fmap renderDoc' . runReader initPA -renderDoc' :: Doc -> String-renderDoc' = PP.render+renderDoc' :: Doc ann -> String+renderDoc' = renderString . PP.layoutPretty PP.defaultLayoutOptions
src/Disco/Pretty/Prec.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Pretty.Prec -- Copyright   :  disco team and contributors@@ -7,12 +10,9 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Precedence and associativity for pretty-printing.---------------------------------------------------------------------------------- module Disco.Pretty.Prec where -import           Disco.Syntax.Operators+import Disco.Syntax.Operators  -- Types for storing precedence + associativity together 
src/Disco/Property.hs view
@@ -1,5 +1,7 @@- -----------------------------------------------------------------------------+-----------------------------------------------------------------------------+{-# LANGUAGE OverloadedStrings #-}+ -- | -- Module      :  Disco.Property -- Copyright   :  disco team and contributors@@ -8,19 +10,37 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Properties of disco functions.---------------------------------------------------------------------------------+module Disco.Property (+  -- * Generation+  generateSamples, -module Disco.Property-       where+  -- * Utility+  invertMotive,+  invertPropResult, +  -- * Pretty-printing+  prettyTestResult,+)+where++import Prelude hiding ((<>))++import Data.Char (toLower) import qualified Data.Enumeration.Invertible as E-import qualified Test.QuickCheck             as QC -import           Disco.Effects.Random-import           Polysemy+import Disco.Effects.Random+import Polysemy -import           Disco.Value+import Disco.AST.Typed+import Disco.Effects.Input+import Disco.Effects.LFresh+import Disco.Error+import Disco.Pretty+import Disco.Syntax.Prims+import Disco.Typecheck.Erase (eraseProperty)+import Disco.Types (TyDefCtx)+import Disco.Value+import Polysemy.Reader  -- | Toggles which outcome (finding or not finding the thing being --   searched for) qualifies as success, without changing the thing@@ -33,21 +53,125 @@ invertPropResult :: TestResult -> TestResult invertPropResult res@(TestResult b r env)   | TestRuntimeError _ <- r = res-  | otherwise               = TestResult (not b) r env+  | otherwise = TestResult (not b) r env +randomLarge :: Member Random r => [Integer] -> Sem r [Integer]+randomLarge [] = return []+randomLarge [_] = return []+randomLarge (x : y : xs) = (:) <$> randomR (x, y) <*> randomLarge (y : xs)+ -- | Select samples from an enumeration according to a search type. Also returns --   a 'SearchType' describing the results, which may be 'Exhaustive' if the --   enumeration is no larger than the number of samples requested. generateSamples :: Member Random r => SearchType -> E.IEnumeration a -> Sem r ([a], SearchType)-generateSamples Exhaustive e           = return (E.enumerate e, Exhaustive)+generateSamples Exhaustive e = return (E.enumerate e, Exhaustive) generateSamples (Randomized n m) e   | E.Finite k <- E.card e, k <= n + m = return (E.enumerate e, Exhaustive)-  | otherwise                          = do-    let small = [0 .. n]-    rs <- runGen . mapM sizedNat $ [n .. n + m]-    let samples = map (E.select e) $ small ++ rs-    return (samples, Randomized n m)-  where-    sizedNat k = QC.resize (fromIntegral k) QC.arbitrarySizedNatural+  | otherwise = do+      let small = [0 .. n]+      rs <- randomLarge [100, 1000, 10000, 100000, 1000000]+      let samples = map (E.select e) $ small ++ rs+      return (samples, Randomized n m)  -- XXX do shrinking for randomly generated test cases?++------------------------------------------------------------+-- Pretty-printing for test results+------------------------------------------------------------++prettyResultCertainty :: Members '[LFresh, Reader PA] r => TestReason -> AProperty -> String -> Sem r (Doc ann)+prettyResultCertainty r prop res =+  (if resultIsCertain r then "Certainly" else "Possibly") <+> text res <> ":" <+> pretty (eraseProperty prop)++prettyTestReason ::+  Members '[Input TyDefCtx, LFresh, Reader PA] r =>+  Bool ->+  AProperty ->+  TestReason ->+  Sem r (Doc ann)+prettyTestReason _ _ TestBool = empty+prettyTestReason b _ (TestFound (TestResult _ _ env))+  | b = prettyTestEnv "Found example:" env+  | not b = prettyTestEnv "Found counterexample:" env+prettyTestReason b _ (TestNotFound Exhaustive)+  | b = "No counterexamples exist; all possible values were checked."+  | not b = "No example exists; all possible values were checked."+prettyTestReason b _ (TestNotFound (Randomized n m))+  | b = "Checked" <+> text (show (n + m)) <+> "possibilities without finding a counterexample."+  | not b = "No example was found; checked" <+> text (show (n + m)) <+> "possibilities."+prettyTestReason _ _ (TestEqual t a1 a2) =+  bulletList+    "-"+    [ "Left side:  " <> prettyValue t a1+    , "Right side: " <> prettyValue t a2+    ]+prettyTestReason _ _ (TestLt t a1 a2) =+  bulletList+    "-"+    [ "Left side:  " <> prettyValue t a1+    , "Right side: " <> prettyValue t a2+    ]+prettyTestReason _ _ (TestRuntimeError ee) =+  nest 2 $+    "Test failed with an error:"+      $+$ pretty (EvalErr ee)+-- \$+$+-- prettyTestEnv "Example inputs that caused the error:" env+-- See #364+prettyTestReason b (ATApp _ (ATPrim _ (PrimBOp _)) (ATTup _ [p1, p2])) (TestBin _ tr1 tr2) =+  bulletList+    "-"+    [ nest 2 $ "Left side:" $+$ prettyTestResult' b p1 tr1+    , nest 2 $ "Right side:" $+$ prettyTestResult' b p2 tr2+    ]+-- See Note [prettyTestReason fallback]+prettyTestReason _ _ _ = empty++-- ~~~~ Note [prettyTestReason fallback]+--+-- prettyTestReason can do a decent job printing out reasons for a+-- test result when operators like /\, \/, etc. are written+-- explicitly; then it can structurally recurse on the original Prop+-- expression in parllel with the TestReason.  However, it is possible+-- to e.g. write a function which returns a Prop, making the structure+-- of the Prop expression opaque.  For example, consider this example+-- (from test/prop-higher-order):+--+-- !!! all [true, true, true, false, true]+-- all : List(Prop) -> Prop+-- all ps = reduce(~/\~, true, ps)+--+-- This test is false, and the TestReason ends up with a bunch of+-- nested TestBin LAnd.  However, the proposition is literally a+-- function application so we cannot see that it matches the structure+-- of the test result.  So we just give up and decline to print a+-- reason.++prettyTestResult' ::+  Members '[Input TyDefCtx, LFresh, Reader PA] r =>+  Bool ->+  AProperty ->+  TestResult ->+  Sem r (Doc ann)+prettyTestResult' _ prop (TestResult bool tr _) =+  prettyResultCertainty tr prop (map toLower (show bool))+    $+$ prettyTestReason bool prop tr++prettyTestResult ::+  Members '[Input TyDefCtx, LFresh, Reader PA] r =>+  AProperty ->+  TestResult ->+  Sem r (Doc ann)+prettyTestResult prop (TestResult b r env) = prettyTestResult' b prop (TestResult b r env)++prettyTestEnv ::+  Members '[Input TyDefCtx, LFresh, Reader PA] r =>+  String ->+  TestEnv ->+  Sem r (Doc ann)+prettyTestEnv _ (TestEnv []) = empty+prettyTestEnv s (TestEnv vs) = nest 2 $ text s $+$ vcat (map prettyBind vs)+ where+  maxNameLen = maximum . map (\(n, _, _) -> length n) $ vs+  prettyBind (x, ty, v) =+    text x <> text (replicate (maxNameLen - length x) ' ') <+> "=" <+> prettyValue ty v
src/Disco/Report.hs view
@@ -1,14 +1,5 @@- -------------------------------------------------------------------------------- |--- Module      :  Disco.Report--- Copyright   :  disco team and contributors--- Maintainer  :  byorgey@gmail.com------ SPDX-License-Identifier: BSD-3-Clause------ XXX---+ -----------------------------------------------------------------------------  -- The benefit of having our own deeply-embedded type for pretty@@ -18,16 +9,24 @@ -- interface of the pretty-printing library currently being used, so -- that a lot of code could just be kept unchanged. +-- |+-- Module      :  Disco.Report+-- Copyright   :  disco team and contributors+-- Maintainer  :  byorgey@gmail.com+--+-- SPDX-License-Identifier: BSD-3-Clause+--+-- XXX module Disco.Report where -import           Data.List (intersperse)+import Data.List (intersperse)  data Report-  = RTxt   String-  | RSeq   [Report]-  | RVSeq  [Report]-  | RList  [Report]-  | RNest  Report+  = RTxt String+  | RSeq [Report]+  | RVSeq [Report]+  | RList [Report]+  | RNest Report   deriving (Show)  text :: String -> Report@@ -52,4 +51,3 @@ nest = RNest  -------------------------------------------------------------
src/Disco/Subst.hs view
@@ -1,6 +1,11 @@ {-# LANGUAGE OverloadedStrings #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Subst -- Copyright   :  disco team and contributors@@ -8,41 +13,39 @@ -- -- The "Disco.Subst" module defines a generic type of substitutions -- that map variable names to values.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Subst-  ( -- * Substitutions--    Substitution(..), dom--    -- ** Constructing/destructing substitutions--  , idS, (|->), fromList, toList--    -- ** Substitution operations+module Disco.Subst (+  -- * Substitutions+  Substitution (..),+  dom, -  , (@@), compose, applySubst, lookup+  -- ** Constructing/destructing substitutions+  idS,+  (|->),+  fromList,+  toList, -  )-  where+  -- ** Substitution operations+  (@@),+  compose,+  applySubst,+  lookup,+)+where -import           Prelude                          hiding (lookup)+import Prelude hiding (lookup) -import           Unbound.Generics.LocallyNameless (Name, Subst, substs)+import Unbound.Generics.LocallyNameless (Name, Subst, substs) -import           Data.Coerce+import Data.Coerce -import           Data.Map                         (Map)-import qualified Data.Map                         as M-import           Data.Set                         (Set)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Set (Set) -import           Disco.Effects.LFresh-import           Disco.Pretty-import           Polysemy-import           Polysemy.Reader+import Disco.Effects.LFresh+import Disco.Pretty+import Polysemy+import Polysemy.Reader  -- | A value of type @Substitution a@ is a substitution which maps some set of --   names (the /domain/, see 'dom') to values of type @a@.@@ -57,7 +60,7 @@ --   See also "Disco.Types", which defines 'S' as an alias for --   substitutions on types (the most common kind in the disco --   codebase).-newtype Substitution a = Substitution { getSubst :: Map (Name a) a }+newtype Substitution a = Substitution {getSubst :: Map (Name a) a}   deriving (Eq, Ord, Show)  instance Functor Substitution where@@ -69,7 +72,7 @@     ds <- punctuate "," es     braces (hsep ds) -prettyMapping :: (Pretty a, Members '[Reader PA, LFresh] r) => Name a -> a -> Sem r Doc+prettyMapping :: (Pretty a, Members '[Reader PA, LFresh] r) => Name a -> a -> Sem r (Doc ann) prettyMapping x a = pretty x <+> "->" <+> pretty a  -- | The domain of a substitution is the set of names for which the
src/Disco/Syntax/Operators.hs view
@@ -1,6 +1,12 @@-{-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-}+ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Syntax.Operators -- Copyright   :  disco team and contributors@@ -8,77 +14,121 @@ -- -- Unary and binary operators along with information like precedence, -- fixity, and concrete syntax.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Syntax.Operators-       ( -- * Operators-         UOp(..), BOp(..), TyOp(..)--         -- * Operator info-       , UFixity(..), BFixity(..), OpFixity(..), OpInfo(..)+module Disco.Syntax.Operators (+  -- * Operators+  UOp (..),+  BOp (..),+  TyOp (..), -         -- * Operator tables and lookup-       , opTable, uopMap, bopMap-       , uPrec, bPrec, assoc, funPrec+  -- * Operator info+  UFixity (..),+  BFixity (..),+  OpFixity (..),+  OpInfo (..), -       ) where+  -- * Operator tables and lookup+  opTable,+  uopMap,+  bopMap,+  uPrec,+  bPrec,+  assoc,+  funPrec,+) where -import           Data.Data                        (Data)-import           GHC.Generics                     (Generic)-import           Unbound.Generics.LocallyNameless+import Data.Data (Data)+import GHC.Generics (Generic)+import Unbound.Generics.LocallyNameless -import           Data.Map                         (Map, (!))-import qualified Data.Map                         as M+import Data.Map (Map, (!))+import qualified Data.Map as M  ------------------------------------------------------------ -- Operators ------------------------------------------------------------  -- | Unary operators.-data UOp = Neg   -- ^ Arithmetic negation (@-@)-         | Not   -- ^ Logical negation (@not@)-         | Fact  -- ^ Factorial (@!@)+data UOp+  = -- | Arithmetic negation (@-@)+    Neg+  | -- | Logical negation (@not@)+    Not+  | -- | Factorial (@!@)+    Fact   deriving (Show, Read, Eq, Ord, Generic, Data, Alpha, Subst t)  -- | Binary operators.-data BOp = Add      -- ^ Addition (@+@)-         | Sub      -- ^ Subtraction (@-@)-         | SSub     -- ^ Saturating Subtraction (@.-@ / @∸@)-         | Mul      -- ^ Multiplication (@*@)-         | Div      -- ^ Division (@/@)-         | Exp      -- ^ Exponentiation (@^@)-         | IDiv     -- ^ Integer division (@//@)-         | Eq       -- ^ Equality test (@==@)-         | Neq      -- ^ Not-equal (@/=@)-         | Lt       -- ^ Less than (@<@)-         | Gt       -- ^ Greater than (@>@)-         | Leq      -- ^ Less than or equal (@<=@)-         | Geq      -- ^ Greater than or equal (@>=@)-         | Min      -- ^ Minimum (@min@)-         | Max      -- ^ Maximum (@max@)-         | And      -- ^ Logical and (@&&@ / @and@)-         | Or       -- ^ Logical or (@||@ / @or@)-         | Impl     -- ^ Logical implies (@->@ / @implies@)-         | Iff      -- ^ Logical biconditional (@<->@ / @iff@)-         | Mod      -- ^ Modulo (@mod@)-         | Divides  -- ^ Divisibility test (@|@)-         | Choose   -- ^ Binomial and multinomial coefficients (@choose@)-         | Cons     -- ^ List cons (@::@)-         | CartProd -- ^ Cartesian product of sets (@**@ / @⨯@)-         | Union    -- ^ Union of two sets (@union@ / @∪@)-         | Inter    -- ^ Intersection of two sets (@intersect@ / @∩@)-         | Diff     -- ^ Difference between two sets (@\@)-         | Elem     -- ^ Element test (@∈@)-         | Subset   -- ^ Subset test (@⊆@)-         | ShouldEq -- ^ Equality assertion (@=!=@)+data BOp+  = -- | Addition (@+@)+    Add+  | -- | Subtraction (@-@)+    Sub+  | -- | Saturating Subtraction (@.-@ / @∸@)+    SSub+  | -- | Multiplication (@*@)+    Mul+  | -- | Division (@/@)+    Div+  | -- | Exponentiation (@^@)+    Exp+  | -- | Integer division (@//@)+    IDiv+  | -- | Equality test (@==@)+    Eq+  | -- | Not-equal (@/=@)+    Neq+  | -- | Less than (@<@)+    Lt+  | -- | Greater than (@>@)+    Gt+  | -- | Less than or equal (@<=@)+    Leq+  | -- | Greater than or equal (@>=@)+    Geq+  | -- | Minimum (@min@)+    Min+  | -- | Maximum (@max@)+    Max+  | -- | Logical and (@&&@ / @and@)+    And+  | -- | Logical or (@||@ / @or@)+    Or+  | -- | Logical implies (@->@ / @implies@)+    Impl+  | -- | Logical biconditional (@<->@ / @iff@)+    Iff+  | -- | Modulo (@mod@)+    Mod+  | -- | Divisibility test (@|@)+    Divides+  | -- | Binomial and multinomial coefficients (@choose@)+    Choose+  | -- | List cons (@::@)+    Cons+  | -- | Cartesian product of sets (@**@ / @⨯@)+    CartProd+  | -- | Union of two sets (@union@ / @∪@)+    Union+  | -- | Intersection of two sets (@intersect@ / @∩@)+    Inter+  | -- | Difference between two sets (@\@)+    Diff+  | -- | Element test (@∈@)+    Elem+  | -- | Subset test (@⊆@)+    Subset+  | -- | Equality assertion (@=!=@)+    ShouldEq+  | -- | Less than assertion (@!<@)+    ShouldLt   deriving (Show, Read, Eq, Ord, Generic, Data, Alpha, Subst t)  -- | Type operators.-data TyOp = Enumerate -- ^ List all values of a type-          | Count     -- ^ Count how many values there are of a type+data TyOp+  = -- | List all values of a type+    Enumerate+  | -- | Count how many values there are of a type+    Count   deriving (Show, Eq, Ord, Generic, Data, Alpha, Subst t)  ------------------------------------------------------------@@ -87,34 +137,38 @@  -- | Fixities of unary operators (either pre- or postfix). data UFixity-  = Pre     -- ^ Unary prefix.-  | Post    -- ^ Unary postfix.+  = -- | Unary prefix.+    Pre+  | -- | Unary postfix.+    Post   deriving (Eq, Ord, Enum, Bounded, Show, Generic)  -- | Fixity/associativity of infix binary operators (either left, --   right, or non-associative). data BFixity-  = InL   -- ^ Left-associative infix.-  | InR   -- ^ Right-associative infix.-  | In    -- ^ Infix.+  = -- | Left-associative infix.+    InL+  | -- | Right-associative infix.+    InR+  | -- | Infix.+    In   deriving (Eq, Ord, Enum, Bounded, Show, Generic)  -- | Operators together with their fixity.-data OpFixity =-    UOpF UFixity UOp+data OpFixity+  = UOpF UFixity UOp   | BOpF BFixity BOp   deriving (Eq, Show, Generic)  -- | An @OpInfo@ record contains information about an operator, such --   as the operator itself, its fixity, a list of concrete syntax --   representations, and a numeric precedence level.-data OpInfo =-  OpInfo+data OpInfo = OpInfo   { opFixity :: OpFixity-  , opSyns   :: [String]-  , opPrec   :: Int+  , opSyns :: [String]+  , opPrec :: Int   }-  deriving Show+  deriving (Show)  ------------------------------------------------------------ -- Operator table@@ -127,74 +181,92 @@ opTable :: [[OpInfo]] opTable =   assignPrecLevels-  [ [ uopInfo Pre  Not     ["not", "¬"]-    ]-  , [ uopInfo Post Fact    ["!"]-    ]-  , [ bopInfo InR  Exp     ["^"]-    ]-  , [ uopInfo Pre  Neg     ["-"]-    ]-  , [ bopInfo In   Choose  ["choose"]-    ]-  , [ bopInfo InR  CartProd ["><", "⨯"]-    ]-  , [ bopInfo InL  Union   ["union", "∪"]-    , bopInfo InL  Inter   ["intersect", "∩"]-    , bopInfo InL  Diff    ["\\"]-    ]-  , [ bopInfo InL  Min     ["min"]-    , bopInfo InL  Max     ["max"]-    ]-  , [ bopInfo InL  Mul     ["*"]-    , bopInfo InL  Div     ["/"]-    , bopInfo InL  Mod     ["mod", "%"]-    , bopInfo InL  IDiv    ["//"]-    ]-  , [ bopInfo InL  Add     ["+"]-    , bopInfo InL  Sub     ["-"]-    , bopInfo InL  SSub    [".-", "∸"]-    ]-  , [ bopInfo InR  Cons    ["::"]-    ]-  , [ bopInfo InR  Eq      ["=="]-    , bopInfo InR  ShouldEq ["=!="]-    , bopInfo InR  Neq     ["/=", "≠"]-    , bopInfo InR  Lt      ["<"]-    , bopInfo InR  Gt      [">"]-    , bopInfo InR  Leq     ["<=", "≤"]-    , bopInfo InR  Geq     [">=", "≥"]-    , bopInfo InR  Divides ["divides"]-    , bopInfo InL  Subset  ["subset", "⊆"]-    , bopInfo InL  Elem    ["elem", "∈"]-    ]-  , [ bopInfo InR  And     ["/\\", "and", "∧", "&&"]-    ]-  , [ bopInfo InR  Or      ["\\/", "or", "∨", "||"]-    ]-  , [ bopInfo InR Impl     ["->", "==>", "→", "implies"]-    , bopInfo InR Iff      ["<->", "<==>", "↔", "iff"]+    [+      [ uopInfo Pre Not ["not", "¬"]+      ]+    ,+      [ uopInfo Post Fact ["!"]+      ]+    ,+      [ bopInfo InR Exp ["^"]+      ]+    ,+      [ uopInfo Pre Neg ["-"]+      ]+    ,+      [ bopInfo In Choose ["choose"]+      ]+    ,+      [ bopInfo InR CartProd ["><", "⨯"]+      ]+    ,+      [ bopInfo InL Union ["union", "∪"]+      , bopInfo InL Inter ["intersect", "∩"]+      , bopInfo InL Diff ["\\"]+      ]+    ,+      [ bopInfo InL Min ["min"]+      , bopInfo InL Max ["max"]+      ]+    ,+      [ bopInfo InL Mul ["*"]+      , bopInfo InL Div ["/"]+      , bopInfo InL Mod ["mod", "%"]+      , bopInfo InL IDiv ["//"]+      ]+    ,+      [ bopInfo InL Add ["+"]+      , bopInfo InL Sub ["-"]+      , bopInfo InL SSub [".-", "∸"]+      ]+    ,+      [ bopInfo InR Cons ["::"]+      ]+    ,+      [ bopInfo InR Eq ["=="]+      , bopInfo InR ShouldEq ["=!="]+      , bopInfo InR ShouldLt ["!<"]+      , bopInfo InR Neq ["/=", "≠", "!="]+      , bopInfo InR Lt ["<"]+      , bopInfo InR Gt [">"]+      , bopInfo InR Leq ["<=", "≤", "=<"]+      , bopInfo InR Geq [">=", "≥", "=>"]+      , bopInfo InR Divides ["divides"]+      , bopInfo InL Subset ["subset", "⊆"]+      , bopInfo InL Elem ["elem", "∈"]+      ]+    ,+      [ bopInfo InR And ["/\\", "and", "∧", "&&"]+      ]+    ,+      [ bopInfo InR Or ["\\/", "or", "∨", "||"]+      ]+    ,+      [ bopInfo InR Impl ["->", "==>", "→", "implies"]+      , bopInfo InR Iff ["<->", "<==>", "↔", "iff"]+      ]     ]-  ]-  where-    uopInfo fx op syns = OpInfo (UOpF fx op) syns (-1)-    bopInfo fx op syns = OpInfo (BOpF fx op) syns (-1)+ where+  uopInfo fx op syns = OpInfo (UOpF fx op) syns (-1)+  bopInfo fx op syns = OpInfo (BOpF fx op) syns (-1) -    -- Start at precedence level 2 so we can give level 1 to ascription, and level 0-    -- to the ambient context + parentheses etc.-    assignPrecLevels table = zipWith assignPrecs (reverse [2 .. length table+1]) table-    assignPrecs p ops      = map (assignPrec p) ops-    assignPrec  p op       = op { opPrec = p }+  -- Start at precedence level 2 so we can give level 1 to ascription, and level 0+  -- to the ambient context + parentheses etc.+  assignPrecLevels table = zipWith assignPrecs (reverse [2 .. length table + 1]) table+  assignPrec p op = op {opPrec = p}+  assignPrecs p = map (assignPrec p)  -- | A map from all unary operators to their associated 'OpInfo' records. uopMap :: Map UOp OpInfo-uopMap = M.fromList $-  [ (op, info) | opLevel <- opTable, info@(OpInfo (UOpF _ op) _ _) <- opLevel ]+uopMap =+  M.fromList $+    [(op, info) | opLevel <- opTable, info@(OpInfo (UOpF _ op) _ _) <- opLevel]  -- | A map from all binary operators to their associatied 'OpInfo' records. bopMap :: Map BOp OpInfo-bopMap = M.fromList $-  [ (op, info) | opLevel <- opTable, info@(OpInfo (BOpF _ op) _ _) <- opLevel ]+bopMap =+  M.fromList $+    [(op, info) | opLevel <- opTable, info@(OpInfo (BOpF _ op) _ _) <- opLevel]  -- | A convenient function for looking up the precedence of a unary operator. uPrec :: UOp -> Int@@ -209,9 +281,9 @@ assoc op =   case M.lookup op bopMap of     Just (OpInfo (BOpF fx _) _ _) -> fx-    _                             -> error $ "BOp " ++ show op ++ " not in bopMap!"+    _ -> error $ "BOp " ++ show op ++ " not in bopMap!"  -- | The precedence level of function application (higher than any --   other precedence level). funPrec :: Int-funPrec = length opTable+1+funPrec = length opTable + 1
src/Disco/Syntax/Prims.hs view
@@ -1,6 +1,12 @@-{-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-}+ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Syntax.Prims -- Copyright   :  disco team and contributors@@ -8,24 +14,22 @@ -- -- Concrete syntax for the prims (i.e. built-in constants) supported -- by the language.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Syntax.Prims-       ( Prim(..)-       , PrimInfo(..), primTable, toPrim, primMap-       ) where+module Disco.Syntax.Prims (+  Prim (..),+  PrimInfo (..),+  primTable,+  toPrim,+  primMap,+) where -import           GHC.Generics                     (Generic)-import           Unbound.Generics.LocallyNameless+import GHC.Generics (Generic)+import Unbound.Generics.LocallyNameless -import           Data.Map                         (Map)-import qualified Data.Map                         as M+import Data.Map (Map)+import qualified Data.Map as M -import           Data.Data                        (Data)-import           Disco.Syntax.Operators+import Data.Data (Data)+import Disco.Syntax.Operators  ------------------------------------------------------------ -- Prims@@ -33,57 +37,120 @@  -- | Primitives, /i.e./ built-in constants. data Prim where-  PrimUOp        :: UOp -> Prim -- ^ Unary operator-  PrimBOp        :: BOp -> Prim -- ^ Binary operator--  PrimLeft       :: Prim        -- ^ Left injection into a sum type.-  PrimRight      :: Prim        -- ^ Right injection into a sum type.--  PrimSqrt       :: Prim        -- ^ Integer square root (@sqrt@)-  PrimFloor      :: Prim        -- ^ Floor of fractional type (@floor@)-  PrimCeil       :: Prim        -- ^ Ceiling of fractional type (@ceiling@)-  PrimAbs        :: Prim        -- ^ Absolute value (@abs@)--  PrimPower      :: Prim        -- ^ Power set (XXX or bag?)--  PrimList       :: Prim        -- ^ Container -> list conversion-  PrimBag        :: Prim        -- ^ Container -> bag conversion-  PrimSet        :: Prim        -- ^ Container -> set conversion--  PrimB2C        :: Prim        -- ^ bag -> set of counts conversion-  PrimC2B        :: Prim        -- ^ set of counts -> bag conversion-  PrimUC2B       :: Prim        -- ^ unsafe set of counts -> bag conversion-                                --   that assumes all distinct-  PrimMapToSet   :: Prim        -- ^ Map k v -> Set (k × v)-  PrimSetToMap   :: Prim        -- ^ Set (k × v) -> Map k v--  PrimSummary    :: Prim        -- ^ Get Adjacency list of Graph-  PrimVertex     :: Prim        -- ^ Construct a graph Vertex-  PrimEmptyGraph :: Prim        -- ^ Empty graph-  PrimOverlay    :: Prim        -- ^ Overlay two Graphs-  PrimConnect    :: Prim        -- ^ Connect Graph to another with directed edges--  PrimInsert     :: Prim        -- ^ Insert into map-  PrimLookup     :: Prim        -- ^ Get value associated with key in map--  PrimEach       :: Prim        -- ^ Each operation for containers-  PrimReduce     :: Prim        -- ^ Reduce operation for containers-  PrimFilter     :: Prim        -- ^ Filter operation for containers-  PrimJoin       :: Prim        -- ^ Monadic join for containers-  PrimMerge      :: Prim        -- ^ Generic merge operation for bags/sets--  PrimIsPrime    :: Prim        -- ^ Efficient primality test-  PrimFactor     :: Prim        -- ^ Factorization-  PrimFrac       :: Prim        -- ^ Turn a rational into a pair (num, denom)--  PrimCrash      :: Prim        -- ^ Crash--  PrimUntil      :: Prim        -- ^ @[x, y, z .. e]@--  PrimHolds      :: Prim        -- ^ Test whether a proposition holds--  PrimLookupSeq  :: Prim        -- ^ Lookup OEIS sequence-  PrimExtendSeq  :: Prim        -- ^ Extend OEIS sequence+  PrimUOp ::+    UOp ->+    -- | Unary operator+    Prim+  PrimBOp ::+    BOp ->+    -- | Binary operator+    Prim+  PrimLeft ::+    -- | Left injection into a sum type.+    Prim+  PrimRight ::+    -- | Right injection into a sum type.+    Prim+  PrimSqrt ::+    -- | Integer square root (@sqrt@)+    Prim+  PrimFloor ::+    -- | Floor of fractional type (@floor@)+    Prim+  PrimCeil ::+    -- | Ceiling of fractional type (@ceiling@)+    Prim+  PrimAbs ::+    -- | Absolute value (@abs@)+    Prim+  PrimPower ::+    -- | Power set (XXX or bag?)+    Prim+  PrimList ::+    -- | Container -> list conversion+    Prim+  PrimBag ::+    -- | Container -> bag conversion+    Prim+  PrimSet ::+    -- | Container -> set conversion+    Prim+  PrimB2C ::+    -- | bag -> set of counts conversion+    Prim+  PrimC2B ::+    -- | set of counts -> bag conversion+    Prim+  PrimUC2B ::+    -- | unsafe set of counts -> bag conversion+    --   that assumes all distinct+    Prim+  PrimMapToSet ::+    -- | Map k v -> Set (k × v)+    Prim+  PrimSetToMap ::+    -- | Set (k × v) -> Map k v+    Prim+  PrimSummary ::+    -- | Get Adjacency list of Graph+    Prim+  PrimVertex ::+    -- | Construct a graph Vertex+    Prim+  PrimEmptyGraph ::+    -- | Empty graph+    Prim+  PrimOverlay ::+    -- | Overlay two Graphs+    Prim+  PrimConnect ::+    -- | Connect Graph to another with directed edges+    Prim+  PrimInsert ::+    -- | Insert into map+    Prim+  PrimLookup ::+    -- | Get value associated with key in map+    Prim+  PrimEach ::+    -- | Each operation for containers+    Prim+  PrimReduce ::+    -- | Reduce operation for containers+    Prim+  PrimFilter ::+    -- | Filter operation for containers+    Prim+  PrimJoin ::+    -- | Monadic join for containers+    Prim+  PrimMerge ::+    -- | Generic merge operation for bags/sets+    Prim+  PrimIsPrime ::+    -- | Efficient primality test+    Prim+  PrimFactor ::+    -- | Factorization+    Prim+  PrimFrac ::+    -- | Turn a rational into a pair (num, denom)+    Prim+  PrimCrash ::+    -- | Crash+    Prim+  PrimUntil ::+    -- | @[x, y, z .. e]@+    Prim+  PrimHolds ::+    -- | Test whether a proposition holds+    Prim+  PrimLookupSeq ::+    -- | Lookup OEIS sequence+    Prim+  PrimExtendSeq ::+    -- | Extend OEIS sequence+    Prim   deriving (Show, Read, Eq, Ord, Generic, Alpha, Subst t, Data)  ------------------------------------------------------------@@ -96,84 +163,72 @@ --   the basic language.  Unexposed prims can only be referenced by --   enabling the Primitives language extension and prefixing their --   name by @$@.-data PrimInfo =-  PrimInfo-  { thePrim     :: Prim-  , primSyntax  :: String+data PrimInfo = PrimInfo+  { thePrim :: Prim+  , primSyntax :: String   , primExposed :: Bool-    -- Is the prim available in the normal syntax of the language?-    ---    --   primExposed = True means that the bare primSyntax can be used-    --   in the surface syntax, and the prim will be pretty-printed as-    --   the primSyntax.-    ---    --   primExposed = False means that the only way to enter it is to-    --   enable the Primitives language extension and write a $-    --   followed by the primSyntax.  The prim will be pretty-printed with a $-    --   prefix.-    ---    --   In no case is a prim a reserved word.+  -- Is the prim available in the normal syntax of the language?+  --+  --   primExposed = True means that the bare primSyntax can be used+  --   in the surface syntax, and the prim will be pretty-printed as+  --   the primSyntax.+  --+  --   primExposed = False means that the only way to enter it is to+  --   enable the Primitives language extension and write a $+  --   followed by the primSyntax.  The prim will be pretty-printed with a $+  --   prefix.+  --+  --   In no case is a prim a reserved word.   }  -- | A table containing a 'PrimInfo' record for every non-operator --   'Prim' recognized by the language. primTable :: [PrimInfo] primTable =-  [ PrimInfo PrimLeft      "left"           True-  , PrimInfo PrimRight     "right"          True--  , PrimInfo (PrimUOp Not) "not"            True-  , PrimInfo PrimSqrt      "sqrt"           True-  , PrimInfo PrimFloor     "floor"          True-  , PrimInfo PrimCeil      "ceiling"        True-  , PrimInfo PrimAbs       "abs"            True--  , PrimInfo PrimPower     "power"          True--  , PrimInfo PrimList      "list"           True-  , PrimInfo PrimBag       "bag"            True-  , PrimInfo PrimSet       "set"            True--  , PrimInfo PrimB2C       "bagCounts"      True-  , PrimInfo PrimC2B       "bagFromCounts"  True-  , PrimInfo PrimUC2B      "unsafeBagFromCounts" False-  , PrimInfo PrimMapToSet  "mapToSet"       True-  , PrimInfo PrimSetToMap  "map"            True--  , PrimInfo PrimSummary   "summary"        True-  , PrimInfo PrimVertex    "vertex"         True-  , PrimInfo PrimEmptyGraph "emptyGraph"     True-  , PrimInfo PrimOverlay   "overlay"        True-  , PrimInfo PrimConnect   "connect"        True--  , PrimInfo PrimInsert    "insert"         True-  , PrimInfo PrimLookup    "lookup"         True--  , PrimInfo PrimEach      "each"           True-  , PrimInfo PrimReduce    "reduce"         True-  , PrimInfo PrimFilter    "filter"         True-  , PrimInfo PrimJoin      "join"           False-  , PrimInfo PrimMerge     "merge"          False--  , PrimInfo PrimIsPrime   "isPrime"        False-  , PrimInfo PrimFactor    "factor"         False-  , PrimInfo PrimFrac      "frac"           False--  , PrimInfo PrimCrash     "crash"          False--  , PrimInfo PrimUntil     "until"          False--  , PrimInfo PrimHolds     "holds"          True-+  [ PrimInfo PrimLeft "left" True+  , PrimInfo PrimRight "right" True+  , PrimInfo (PrimUOp Not) "not" True+  , PrimInfo PrimSqrt "sqrt" True+  , PrimInfo PrimFloor "floor" True+  , PrimInfo PrimCeil "ceiling" True+  , PrimInfo PrimAbs "abs" True+  , PrimInfo PrimPower "power" True+  , PrimInfo PrimList "list" True+  , PrimInfo PrimBag "bag" True+  , PrimInfo PrimSet "set" True+  , PrimInfo PrimB2C "bagCounts" True+  , PrimInfo PrimC2B "bagFromCounts" True+  , PrimInfo PrimUC2B "unsafeBagFromCounts" False+  , PrimInfo PrimMapToSet "mapToSet" True+  , PrimInfo PrimSetToMap "map" True+  , PrimInfo PrimSummary "summary" True+  , PrimInfo PrimVertex "vertex" True+  , PrimInfo PrimEmptyGraph "emptyGraph" True+  , PrimInfo PrimOverlay "overlay" True+  , PrimInfo PrimConnect "connect" True+  , PrimInfo PrimInsert "insert" True+  , PrimInfo PrimLookup "lookup" True+  , PrimInfo PrimEach "each" True+  , PrimInfo PrimReduce "reduce" True+  , PrimInfo PrimFilter "filter" True+  , PrimInfo PrimJoin "join" False+  , PrimInfo PrimMerge "merge" False+  , PrimInfo PrimIsPrime "isPrime" False+  , PrimInfo PrimFactor "factor" False+  , PrimInfo PrimFrac "frac" False+  , PrimInfo PrimCrash "crash" False+  , PrimInfo PrimUntil "until" False+  , PrimInfo PrimHolds "holds" True   , PrimInfo PrimLookupSeq "lookupSequence" False   , PrimInfo PrimExtendSeq "extendSequence" False   ]  -- | Find any exposed prims with the given name. toPrim :: String -> [Prim]-toPrim x = [ p | PrimInfo p syn True <- primTable, syn == x ]+toPrim x = [p | PrimInfo p syn True <- primTable, syn == x]  -- | A convenient map from each 'Prim' to its info record. primMap :: Map Prim PrimInfo-primMap = M.fromList $-  [ (p, pinfo) | pinfo@(PrimInfo p _ _) <- primTable ]+primMap =+  M.fromList $+    [(p, pinfo) | pinfo@(PrimInfo p _ _) <- primTable]
src/Disco/Typecheck.hs view
@@ -1,1680 +1,1703 @@-{-# LANGUAGE MultiWayIf               #-}-{-# LANGUAGE NondecreasingIndentation #-}-{-# LANGUAGE OverloadedStrings        #-}---------------------------------------------------------------------------------- |--- Module      :  Disco.Typecheck--- Copyright   :  disco team and contributors--- Maintainer  :  byorgey@gmail.com------ SPDX-License-Identifier: BSD-3-Clause------ Typecheck the Disco surface language and transform it into a--- type-annotated AST.-----------------------------------------------------------------------------------module Disco.Typecheck where--import           Control.Arrow                           ((&&&))-import           Control.Lens                            ((^..))-import           Control.Monad.Except-import           Control.Monad.Trans.Maybe-import           Data.Bifunctor                          (first)-import           Data.Coerce-import qualified Data.Foldable                           as F-import           Data.List                               (group, sort)-import           Data.Map                                (Map)-import qualified Data.Map                                as M-import           Data.Maybe                              (isJust)-import           Data.Set                                (Set)-import qualified Data.Set                                as S-import           Prelude                                 as P hiding (lookup)--import           Unbound.Generics.LocallyNameless        (Alpha, Bind, Name,-                                                          bind, embed,-                                                          name2String,-                                                          string2Name, substs,-                                                          unembed)-import           Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)--import           Disco.Effects.Fresh-import           Polysemy                                hiding (embed)-import           Polysemy.Error-import           Polysemy.Output-import           Polysemy.Reader-import           Polysemy.Writer--import           Disco.AST.Surface-import           Disco.AST.Typed-import           Disco.Context                           hiding (filter)-import qualified Disco.Context                           as Ctx-import           Disco.Messages-import           Disco.Module-import           Disco.Names-import           Disco.Subst                             (applySubst)-import qualified Disco.Subst                             as Subst-import           Disco.Syntax.Operators-import           Disco.Syntax.Prims-import           Disco.Typecheck.Constraints-import           Disco.Typecheck.Util-import           Disco.Types-import           Disco.Types.Rules----------------------------------------------------------------- Container utilities---------------------------------------------------------------containerTy :: Container -> Type -> Type-containerTy c ty = TyCon (containerToCon c) [ty]--containerToCon :: Container -> Con-containerToCon ListContainer = CList-containerToCon BagContainer  = CBag-containerToCon SetContainer  = CSet----------------------------------------------------------------- Telescopes----------------------------------------------------------------- | Infer the type of a telescope, given a way to infer the type of---   each item along with a context of variables it binds; each such---   context is then added to the overall context when inferring---   subsequent items in the telescope.-inferTelescope-  :: (Alpha b, Alpha tyb, Member (Reader TyCtx) r)-  => (b -> Sem r (tyb, TyCtx)) -> Telescope b -> Sem r (Telescope tyb, TyCtx)-inferTelescope inferOne tel = do-  (tel1, ctx) <- go (fromTelescope tel)-  return (toTelescope tel1, ctx)-  where-    go []     = return ([], emptyCtx)-    go (b:bs) = do-      (tyb, ctx) <- inferOne b-      extends ctx $ do-      (tybs, ctx') <- go bs-      return (tyb:tybs, ctx <> ctx')----------------------------------------------------------------- Modules----------------------------------------------------------------- | Check all the types and extract all relevant info (docs,---   properties, types) from a module, returning a 'ModuleInfo' record---   on success.  This function does not handle imports at all; any---   imports should already be checked and passed in as the second---   argument.-checkModule-  :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh] r-  => ModuleName -> Map ModuleName ModuleInfo -> Module -> Sem r ModuleInfo-checkModule name imports (Module es _ m docs terms) = do-  let (typeDecls, defns, tydefs) = partitionDecls m-      importTyCtx = mconcat (imports ^.. traverse . miTys)-      -- XXX this isn't right, if multiple modules define the same type synonyms.-      -- Need to use a normal Ctx for tydefs too.-      importTyDefnCtx = M.unions (imports ^.. traverse . miTydefs)-  tyDefnCtx <- mapError noLoc $ makeTyDefnCtx tydefs-  withTyDefns (tyDefnCtx `M.union` importTyDefnCtx) $ do-    tyCtx     <- mapError noLoc $ makeTyCtx name typeDecls-    extends importTyCtx $ extends tyCtx $ do-      mapM_ (checkTyDefn name) tydefs-      adefns <- mapM (checkDefn name) defns-      let defnCtx = ctxForModule name (map (getDefnName &&& id) adefns)-          docCtx = ctxForModule name docs-          dups = filterDups . map getDefnName $ adefns-      case dups of-        (x:_) -> throw $ noLoc $ DuplicateDefns (coerce x)-        [] -> do-          aprops <- mapError noLoc $ checkProperties docCtx  -- XXX location?-          aterms <- mapError noLoc $ mapM inferTop terms     -- XXX location?-          return $ ModuleInfo name imports (map ((name .-) . getDeclName) typeDecls) docCtx aprops tyCtx tyDefnCtx defnCtx aterms es-  where getDefnName :: Defn -> Name ATerm-        getDefnName (Defn n _ _ _) = n--        getDeclName :: TypeDecl -> Name Term-        getDeclName (TypeDecl n _) = n------------------------------------------------------- Type definitions---- | Turn a list of type definitions into a 'TyDefCtx', checking---   for duplicate names among the definitions and also any type---   definitions already in the context.-makeTyDefnCtx :: Members '[Reader TyDefCtx, Error TCError] r => [TypeDefn] -> Sem r TyDefCtx-makeTyDefnCtx tydefs = do-  oldTyDefs <- ask @TyDefCtx-  let oldNames = M.keys oldTyDefs-      newNames = map (\(TypeDefn x _ _) -> x) tydefs-      dups = filterDups $ newNames ++ oldNames--  let convert (TypeDefn x args body)-        = (x, TyDefBody args (flip substs body . zip (map string2Name args)))--  case dups of-    (x:_) -> throw (DuplicateTyDefns x)-    []    -> return . M.fromList $ map convert tydefs---- | Check the validity of a type definition.-checkTyDefn :: Members '[Reader TyDefCtx, Error LocTCError] r => ModuleName -> TypeDefn -> Sem r ()-checkTyDefn name defn@(TypeDefn x args body) = mapError (LocTCError (Just (name .- string2Name x))) $ do--  -- First, make sure the body is a valid type, i.e. everything inside-  -- it is well-kinded.-  checkTypeValid body--  -- Now make sure it is not directly cyclic (i.e. ensure it is a-  -- "productive" definition).-  _ <- checkCyclicTy (TyUser x (map (TyVar . string2Name) args)) S.empty--  -- Make sure it does not use any unbound type variables or undefined-  -- types.-  checkUnboundVars defn--  -- Make sure it does not use any polymorphic recursion (polymorphic-  -- recursion isn't allowed at the moment since it can make the-  -- subtyping checker diverge).-  checkPolyRec defn---- | Check if a given type is cyclic. A type 'ty' is cyclic if:------   1. 'ty' is the name of a user-defined type.---   2. Repeated expansions of the type yield nothing but other user-defined types.---   3. An expansion of one of those types yields another type that has---      been previously encountered.------   In other words, repeatedly expanding the definition can get us---   back to exactly where we started.------   The function returns the set of TyDefs encountered during---   expansion if the TyDef is not cyclic.-checkCyclicTy :: Members '[Reader TyDefCtx, Error TCError] r => Type -> Set String -> Sem r (Set String)-checkCyclicTy (TyUser name args) set = do-  case S.member name set of-    True -> throw $ CyclicTyDef name-    False -> do-      ty <- lookupTyDefn name args-      checkCyclicTy ty (S.insert name set)--checkCyclicTy _ set = return set---- | Ensure that a type definition does not use any unbound type---   variables or undefined types.-checkUnboundVars :: Members '[Reader TyDefCtx, Error TCError] r => TypeDefn -> Sem r ()-checkUnboundVars (TypeDefn _ args body) = go body-  where-    go (TyAtom (AVar (U x)))-      | name2String x `elem` args = return ()-      | otherwise                 = throw $ UnboundTyVar x-    go (TyAtom _)        = return ()-    go (TyUser name tys) = lookupTyDefn name tys >> mapM_ go tys-    go (TyCon _ tys)     = mapM_ go tys---- | Check for polymorphic recursion: starting from a user-defined---   type, keep expanding its definition recursively, ensuring that---   any recursive references to the defined type have only type variables---   as arguments.-checkPolyRec :: Member (Error TCError) r => TypeDefn -> Sem r ()-checkPolyRec (TypeDefn name args body) = go body-  where-    go (TyCon (CUser x) tys)-      | x == name && not (all isTyVar tys) =-        throw $ NoPolyRec name args tys-      | otherwise = return ()-    go (TyCon _ tys) = mapM_ go tys-    go _             = return ()---- | Keep only the duplicate elements from a list.------   >>> filterDups [1,3,2,1,1,4,2]---   [1,2]-filterDups :: Ord a => [a] -> [a]-filterDups = map head . filter ((>1) . length) . group . sort------------------------------------------------------- Type declarations---- | Given a list of type declarations from a module, first check that---   there are no duplicate type declarations, and that the types are---   well-formed; then create a type context containing the given---   declarations.-makeTyCtx :: Members '[Reader TyDefCtx, Error TCError] r => ModuleName -> [TypeDecl] -> Sem r TyCtx-makeTyCtx name decls = do-  let dups = filterDups . map (\(TypeDecl x _) -> x) $ decls-  case dups of-    (x:_) -> throw (DuplicateDecls x)-    []    -> do-      checkCtx declCtx-      return declCtx-  where-    declCtx = ctxForModule name $ map (\(TypeDecl x ty) -> (x,ty)) decls---- | Check that all the types in a context are valid.-checkCtx :: Members '[Reader TyDefCtx, Error TCError] r => TyCtx -> Sem r ()-checkCtx = mapM_ checkPolyTyValid . Ctx.elems------------------------------------------------------- Top-level definitions---- | Type check a top-level definition in the given module.-checkDefn-  :: Members '[Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh, Output Message] r-  => ModuleName -> TermDefn -> Sem r Defn-checkDefn name (TermDefn x clauses) = mapError (LocTCError (Just (name .- x))) $ do--  -- Check that all clauses have the same number of patterns-  checkNumPats clauses--  -- Get the declared type signature of x-  Forall sig <- lookup (name .- x) >>= maybe (throw $ NoType x) return-    -- If x isn't in the context, it's because no type was declared for it, so-    -- throw an error.-  (nms, ty) <- unbind sig--  -- Try to decompose the type into a chain of arrows like pty1 ->-  -- pty2 -> pty3 -> ... -> bodyTy, according to the number of-  -- patterns, and lazily unrolling type definitions along the way.-  (patTys, bodyTy) <- decomposeDefnTy (numPats (head clauses)) ty--  ((acs, _), theta) <- solve $ do-    aclauses <- forAll nms $ mapM (checkClause patTys bodyTy) clauses-    return (aclauses, ty)--  return $ applySubst theta (Defn (coerce x) patTys bodyTy acs)-  where-    numPats = length . fst . unsafeUnbind--    checkNumPats []     = return ()   -- This can't happen, but meh-    checkNumPats [_]    = return ()-    checkNumPats (c:cs)-      | all ((==0) . numPats) (c:cs) = throw (DuplicateDefns x)-      | not (all ((== numPats c) . numPats) cs) = throw NumPatterns-               -- XXX more info, this error actually means # of-               -- patterns don't match across different clauses-      | otherwise = return ()--    -- | Check a clause of a definition against a list of pattern types and a body type.-    checkClause-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => [Type] -> Type -> Bind [Pattern] Term -> Sem r Clause-    checkClause patTys bodyTy clause = do-      (pats, body) <- unbind clause--      -- At this point we know that every clause has the same number of patterns,-      -- which is the same as the length of the list patTys.  So we can just use-      -- zipWithM to check all the patterns.-      (ctxs, aps) <- unzip <$> zipWithM checkPattern pats patTys-      at  <- extends (mconcat ctxs) $ check body bodyTy-      return $ bind aps at--    -- Decompose a type that must be of the form t1 -> t2 -> ... -> tn -> t{n+1}.-    decomposeDefnTy :: Members '[Reader TyDefCtx, Error TCError] r => Int -> Type -> Sem r ([Type], Type)-    decomposeDefnTy 0 ty = return ([], ty)-    decomposeDefnTy n (TyUser tyName args) = lookupTyDefn tyName args >>= decomposeDefnTy n-    decomposeDefnTy n (ty1 :->: ty2) = first (ty1:) <$> decomposeDefnTy (n-1) ty2-    decomposeDefnTy _n _ty = throw NumPatterns-      -- XXX include more info. More argument patterns than arrows in the type.------------------------------------------------------- Properties---- | Given a context mapping names to documentation, extract the---   properties attached to each name and typecheck them.-checkProperties-  :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output Message] r-  => Ctx Term Docs -> Sem r (Ctx ATerm [AProperty])-checkProperties docs =-  Ctx.coerceKeys . Ctx.filter (not . P.null)-    <$> (traverse . traverse) checkProperty properties-  where-    properties :: Ctx Term [Property]-    properties = fmap (\ds -> [p | DocProperty p <- ds]) docs---- | Check the types of the terms embedded in a property.-checkProperty-  :: Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output Message] r-  => Property -> Sem r AProperty-checkProperty prop = do-  (at, theta) <- solve $ check prop TyProp-  -- XXX do we need to default container variables here?-  return $ applySubst theta at----------------------------------------------------------------- Type checking/inference-------------------------------------------------------------------------------------------------------------------- Checking types/kinds------------------------------------------------------- | Check that a sigma type is a valid type.  See 'checkTypeValid'.-checkPolyTyValid :: Members '[Reader TyDefCtx, Error TCError] r => PolyType -> Sem r ()-checkPolyTyValid (Forall b) = do-  let (_, ty) = unsafeUnbind b-  checkTypeValid ty---- | Disco doesn't need kinds per se, since all types must be fully---   applied.  But we do need to check that every type is applied to---   the correct number of arguments.-checkTypeValid :: Members '[Reader TyDefCtx, Error TCError] r => Type -> Sem r ()-checkTypeValid (TyAtom _)    = return ()-checkTypeValid (TyCon c tys) = do-  k <- conArity c-  if | n < k     -> throw (NotEnoughArgs c)-     | n > k     -> throw (TooManyArgs c)-     | otherwise -> mapM_ checkTypeValid tys-  where-    n = length tys--conArity :: Members '[Reader TyDefCtx, Error TCError] r => Con -> Sem r Int-conArity (CContainer _) = return 1-conArity CGraph = return 1-conArity (CUser name)    = do-  d <- ask @TyDefCtx-  case M.lookup name d of-    Nothing               -> throw (NotTyDef name)-    Just (TyDefBody as _) -> return (length as)-conArity _              = return 2  -- (->, *, +, map)------------------------------------------------------- Checking modes------------------------------------------------------- | Typechecking can be in one of two modes: inference mode means we---   are trying to synthesize a valid type for a term; checking mode---   means we are trying to show that a term has a given type.-data Mode = Infer | Check Type-  deriving Show---- | Check that a term has the given type.  Either throws an error, or---   returns the term annotated with types for all subterms.------   This function is provided for convenience; it simply calls---   'typecheck' with an appropriate 'Mode'.-check-  :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Term -> Type -> Sem r ATerm-check t ty = typecheck (Check ty) t---- | Check that a term has the given polymorphic type.-checkPolyTy-  :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Term -> PolyType -> Sem r ATerm-checkPolyTy t (Forall sig) = do-  (as, tau) <- unbind sig-  (at, cst) <- withConstraint $ check t tau-  case as of-    [] -> constraint cst-    _  -> constraint $ CAll (bind as cst)-  return at---- | Infer the type of a term.  If it succeeds, it returns the term---   with all subterms annotated.------   This function is provided for convenience; it simply calls---   'typecheck' with an appropriate 'Mode'.-infer-  :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Term -> Sem r ATerm-infer = typecheck Infer---- | Top-level type inference algorithm: infer a (polymorphic) type---   for a term by running type inference, solving the resulting---   constraints, and quantifying over any remaining type variables.-inferTop-  :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r-  => Term -> Sem r (ATerm, PolyType)-inferTop t = do--  -- Run inference on the term and try to solve the resulting-  -- constraints.-  (at, theta) <- solve $ infer t--  debug "Final annotated term (before substitution and container monomorphizing):"-  debugPretty at--      -- Apply the resulting substitution.-  let at' = applySubst theta at--      -- Find any remaining container variables.-      cvs = containerVars (getType at')--      -- Replace them all with List.-      at'' = applySubst (Subst.fromList $ zip (S.toList cvs) (repeat (TyAtom (ABase CtrList)))) at'--  -- Finally, quantify over any remaining type variables and return-  -- the term along with the resulting polymorphic type.-  return (at'', closeType (getType at''))---- | Top-level type checking algorithm: check that a term has a given---   polymorphic type by running type checking and solving the---   resulting constraints.-checkTop-  :: Members '[Output Message, Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r-  => Term -> PolyType -> Sem r ATerm-checkTop t ty = do-  (at, theta) <- solve $ checkPolyTy t ty-  return $ applySubst theta at------------------------------------------------------- The typecheck function------------------------------------------------------- | The main workhorse of the typechecker.  Instead of having two---   functions, one for inference and one for checking, 'typecheck'---   takes a 'Mode'.  This cuts down on code duplication in many---   cases, and allows all the checking and inference code related to---   a given AST node to be placed together.-typecheck-  :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Mode -> Term -> Sem r ATerm---- ~~~~ Note [Pattern coverage]--- In several places we have clauses like------   inferPrim (PrimBOp op) | op `elem` [And, Or, Impl, Iff]------ since the typing rules for all the given operators are the same.--- The only problem is that the pattern coverage checker (sensibly)--- doesn't look at guards in general, so it thinks that there are TBin--- cases still uncovered.------ However, we *don't* just want to add a catch-all case at the end,--- because the coverage checker is super helpful in alerting us when--- there's a missing typechecking case after modifying the language in--- some way. The (not ideal) solution for now is to add some--- additional explicit cases that simply call 'error', which will--- never be reached but which assure the coverage checker that we have--- handled those cases.------ The ideal solution would be to use or-patterns, if Haskell had them--- (see https://github.com/ghc-proposals/ghc-proposals/pull/43).------------------------------------------------------- Defined types---- To check at a user-defined type, expand its definition and recurse.--- This case has to be first, so in all other cases we know the type--- will not be a TyUser.-typecheck (Check (TyUser name args)) t = lookupTyDefn name args >>= check t------------------------------------------------------- Parens---- Recurse through parens; they are not represented explicitly in the--- resulting ATerm.-typecheck mode (TParens t) = typecheck mode t------------------------------------------------------- Variables---- Resolve variable names and infer their types.  We don't need a--- checking case; checking the type of a variable will fall through to--- this case.-typecheck Infer (TVar x) = do--  -- Pick the first method that succeeds; if none do, throw an unbound-  -- variable error.-  mt <- runMaybeT . F.asum . map MaybeT $ [tryLocal, tryModule, tryPrim]-  maybe (throw (Unbound x)) return mt-  where-    -- 1. See if the variable name is bound locally.-    tryLocal = do-      mty <- Ctx.lookup (localName x)-      case mty of-        Just (Forall sig) -> do-          (_, ty) <- unbind sig-          return . Just $ ATVar ty (localName (coerce x))-        Nothing -> return Nothing--    -- 2. See if the variable name is bound in some in-scope module,-    -- throwing an ambiguity error if it is bound in multiple modules.-    tryModule = do-      bs <- Ctx.lookupNonLocal x-      case bs of-        [(m,Forall sig)] -> do-          (_, ty) <- unbind sig-          return . Just $ ATVar ty (m .- coerce x)-        []       -> return Nothing-        _        -> throw $ Ambiguous x (map fst bs)--    -- 3. See if we should convert it to a primitive.-    tryPrim =-      case toPrim (name2String x) of-        (prim:_) -> Just <$> typecheck Infer (TPrim prim)-        _        -> return Nothing------------------------------------------------------- Primitives--typecheck Infer (TPrim prim) = do-  ty <- inferPrim prim-  return $ ATPrim ty prim--  where-    inferPrim :: Members '[Writer Constraint, Fresh] r => Prim -> Sem r Type--    -----------------------------------------    -- Left/right--    inferPrim PrimLeft = do-      a <- freshTy-      b <- freshTy-      return $ a :->: (a :+: b)--    inferPrim PrimRight = do-      a <- freshTy-      b <- freshTy-      return $ b :->: (a :+: b)--    -----------------------------------------    -- Logic--    --- XXX restore typing rules for logical operations on Props-    --- once the evaluator can handle them.--    inferPrim (PrimBOp op) | op `elem` [And, Or, Impl, Iff] = do-      return $ TyBool :*: TyBool :->: TyBool-      -- a <- freshTy-      -- constraint $ CQual (bopQual op) a-      -- return $ a :*: a :->: a--    -- See Note [Pattern coverage] ------------------------------    inferPrim (PrimBOp And)  = error "inferPrim And should be unreachable"-    inferPrim (PrimBOp Or)   = error "inferPrim Or should be unreachable"-    inferPrim (PrimBOp Impl) = error "inferPrim Impl should be unreachable"-    inferPrim (PrimBOp Iff)  = error "inferPrim Iff should be unreachable"-    --------------------------------------------------------------    inferPrim (PrimUOp Not) = do-      return $ TyBool :->: TyBool-      -- a <- freshTy-      -- constraint $ CQual QBool a-      -- return $ a :->: a--    -----------------------------------------    -- Container conversion--    inferPrim conv | conv `elem` [PrimList, PrimBag, PrimSet] = do-      c <- freshAtom   -- make a unification variable for the container type-      a <- freshTy     -- make a unification variable for the element type--      -- converting to a set or bag requires being able to sort the elements-      when (conv /= PrimList) $ constraint $ CQual QCmp a--      return $ TyContainer c a :->: primCtrCon conv a--      where-        primCtrCon PrimList = TyList-        primCtrCon PrimBag  = TyBag-        primCtrCon _        = TySet--    -- See Note [Pattern coverage] ------------------------------    inferPrim PrimList = error "inferPrim PrimList should be unreachable"-    inferPrim PrimBag  = error "inferPrim PrimBag should be unreachable"-    inferPrim PrimSet  = error "inferPrim PrimSet should be unreachable"-    --------------------------------------------------------------    inferPrim PrimB2C = do-      a <- freshTy-      return $ TyBag a :->: TySet (a :*: TyN)--    inferPrim PrimC2B = do-      a <- freshTy-      c <- freshAtom-      constraint $ CQual QCmp a-      return $ TyContainer c (a :*: TyN) :->: TyBag a--    inferPrim PrimUC2B = do-      a <- freshTy-      c <- freshAtom-      return $ TyContainer c (a :*: TyN) :->: TyBag a--    inferPrim PrimMapToSet  = do-      k <- freshTy-      v <- freshTy-      constraint $ CQual QSimple k-      return $ TyMap k v :->: TySet (k :*: v)--    inferPrim PrimSetToMap  = do-      k <- freshTy-      v <- freshTy-      constraint $ CQual QSimple k-      return $ TySet (k :*: v) :->: TyMap k v--    inferPrim PrimSummary = do-      a <- freshTy-      constraint $ CQual QSimple a-      return $ TyGraph a :->: TyMap a (TySet a)--    inferPrim PrimVertex = do-      a <- freshTy-      constraint $ CQual QSimple a-      return $ a :->: TyGraph a--    inferPrim PrimEmptyGraph = do-      a <- freshTy-      constraint $ CQual QSimple a-      return $ TyGraph a--    inferPrim PrimOverlay = do-      a <- freshTy-      constraint $ CQual QSimple a-      return $ TyGraph a :*: TyGraph a :->: TyGraph a--    inferPrim PrimConnect = do-      a <- freshTy-      constraint $ CQual QSimple a-      return $ TyGraph a :*: TyGraph a :->: TyGraph a--    inferPrim PrimInsert = do-      a <- freshTy-      b <- freshTy-      constraint $ CQual QSimple a-      return $ a :*: b :*: TyMap a b :->: TyMap a b--    inferPrim PrimLookup = do-      a <- freshTy-      b <- freshTy-      constraint $ CQual QSimple a-      return $ a :*: TyMap a b :->: (TyUnit :+: b)-    -----------------------------------------    -- Container primitives--    inferPrim (PrimBOp Cons) = do-      a <- freshTy-      return $ a :*: TyList a :->: TyList a--    -- XXX see https://github.com/disco-lang/disco/issues/160-    -- each : (a -> b) × c a -> c b-    inferPrim PrimEach = do-      c <- freshAtom-      a <- freshTy-      b <- freshTy-      return $ (a :->: b) :*: TyContainer c a :->: TyContainer c b--    -- XXX should eventually be (a * a -> a) * c a -> a,-    --   with a check that the function has the right properties.-    -- reduce : (a * a -> a) * a * c a -> a-    inferPrim PrimReduce = do-      c <- freshAtom-      a <- freshTy-      return $ (a :*: a :->: a) :*: a :*: TyContainer c a :->: a--    -- filter : (a -> Bool) × c a -> c a-    inferPrim PrimFilter = do-      c <- freshAtom-      a <- freshTy-      return $ (a :->: TyBool) :*: TyContainer c a :->: TyContainer c a--    -- join : c (c a) -> c a-    inferPrim PrimJoin = do-      c <- freshAtom-      a <- freshTy-      return $ TyContainer c (TyContainer c a) :->: TyContainer c a--    -- merge : (N × N -> N) × c a × c a -> c a   (c = bag or set)-    inferPrim PrimMerge = do-      c <- freshAtom-      a <- freshTy-      constraint $ COr-        [ CEq (TyAtom (ABase CtrBag)) (TyAtom c)-        , CEq (TyAtom (ABase CtrSet)) (TyAtom c)-        ]-      let ca = TyContainer c a-      return $ (TyN :*: TyN :->: TyN) :*: ca :*: ca :->: ca--    inferPrim (PrimBOp CartProd) = do-      a <- freshTy-      b <- freshTy-      c <- freshAtom-      return $ TyContainer c a :*: TyContainer c b :->: TyContainer c (a :*: b)--    inferPrim (PrimBOp setOp) | setOp `elem` [Union, Inter, Diff, Subset] = do-      a <- freshTy-      c <- freshAtom-      constraint $ COr-        [ CEq (TyAtom (ABase CtrBag)) (TyAtom c)-        , CEq (TyAtom (ABase CtrSet)) (TyAtom c)-        ]-      let ca = TyContainer c a-      let resTy = case setOp of {Subset -> TyBool; _ -> ca}-      return $ ca :*: ca :->: resTy--    -- See Note [Pattern coverage] ------------------------------    inferPrim (PrimBOp Union)  = error "inferPrim Union should be unreachable"-    inferPrim (PrimBOp Inter)  = error "inferPrim Inter should be unreachable"-    inferPrim (PrimBOp Diff)   = error "inferPrim Diff should be unreachable"-    inferPrim (PrimBOp Subset) = error "inferPrim Subset should be unreachable"-    --------------------------------------------------------------    inferPrim (PrimBOp Elem) = do-      a <- freshTy-      c <- freshAtom--      constraint $ CQual QCmp a--      return $ a :*: TyContainer c a :->: TyBool--    -----------------------------------------    -- Arithmetic--    inferPrim (PrimBOp IDiv) = do-      a <- freshTy-      resTy <- cInt a-      return $ a :*: a :->: resTy--    inferPrim (PrimBOp Mod) = do-      a <- freshTy-      constraint $ CSub a TyZ-      return $ a :*: a :->: a--    inferPrim (PrimBOp op) | op `elem` [Add, Mul, Sub, Div, SSub] = do-      a <- freshTy-      constraint $ CQual (bopQual op) a-      return $ a :*: a :->: a--    -- See Note [Pattern coverage] ------------------------------    inferPrim (PrimBOp Add ) = error "inferPrim Add should be unreachable"-    inferPrim (PrimBOp Mul ) = error "inferPrim Mul should be unreachable"-    inferPrim (PrimBOp Sub ) = error "inferPrim Sub should be unreachable"-    inferPrim (PrimBOp Div ) = error "inferPrim Div should be unreachable"-    inferPrim (PrimBOp SSub) = error "inferPrim SSub should be unreachable"-    --------------------------------------------------------------    inferPrim (PrimUOp Neg) = do-      a <- freshTy-      constraint $ CQual QSub a-      return $ a :->: a--    inferPrim (PrimBOp Exp) = do-      a <- freshTy-      b <- freshTy-      resTy <- cExp a b-      return $ a :*: b :->: resTy--    -----------------------------------------    -- Number theory--    inferPrim PrimIsPrime = return $ TyN :->: TyBool-    inferPrim PrimFactor  = return $ TyN :->: TyBag TyN--    inferPrim PrimFrac    = return $ TyQ :->: (TyZ :*: TyN)--    inferPrim (PrimBOp Divides) = do-      a <- freshTy-      constraint $ CQual QNum a-      return $ a :*: a :->: TyBool--    -----------------------------------------    -- Choose--    -- For now, a simple typing rule for multinomial coefficients that-    -- requires everything to be Nat.  However, they can be extended to-    -- handle negative or fractional arguments.-    inferPrim (PrimBOp Choose) = do-      b <- freshTy--      -- b can be either Nat (a binomial coefficient)-      -- or a list of Nat (a multinomial coefficient).-      constraint $ COr [CEq b TyN, CEq b (TyList TyN)]-      return $ TyN :*: b :->: TyN--    -----------------------------------------    -- Ellipses--    -- Actually 'until' supports more types than this, e.g. Q instead-    -- of N, but this is good enough.  This case is here just for-    -- completeness---in case someone enables primitives and uses it-    -- directly---but typically 'until' is generated only during-    -- desugaring of a container with ellipsis, after typechecking, in-    -- which case it can be assigned a more appropriate type directly.--    inferPrim PrimUntil   = return $ TyN :*: TyList TyN :->: TyList TyN--    -----------------------------------------    -- Crash--    inferPrim PrimCrash   = do-      a <- freshTy-      return $ TyString :->: a--    -----------------------------------------    -- Propositions--    -- 'holds' converts a Prop into a Bool (but might not terminate).-    inferPrim PrimHolds = return $ TyProp :->: TyBool--    -- An equality assertion =!= is just like a comparison ==, except-    -- the result is a Prop.-    inferPrim (PrimBOp ShouldEq) = do-      ty <- freshTy-      constraint $ CQual QCmp ty-      return $ ty :*: ty :->: TyProp--    -----------------------------------------    -- Comparisons--    -- Infer the type of a comparison. A comparison always has type-    -- Bool, but we have to make sure the subterms have compatible-    -- types.  We also generate a QCmp qualifier, for two reasons:-    -- one, we need to know whether e.g. a comparison was done at a-    -- certain type, so we can decide whether the type is allowed to-    -- be completely polymorphic or not.  Also, comparison of Props is-    -- not allowed.-    inferPrim (PrimBOp op) | op `elem` [Eq, Neq, Lt, Gt, Leq, Geq] = do-      ty <- freshTy-      constraint $ CQual QCmp ty-      return $ ty :*: ty :->: TyBool--    -- See Note [Pattern coverage] ------------------------------    inferPrim (PrimBOp Eq)  = error "inferPrim Eq should be unreachable"-    inferPrim (PrimBOp Neq) = error "inferPrim Neq should be unreachable"-    inferPrim (PrimBOp Lt)  = error "inferPrim Lt should be unreachable"-    inferPrim (PrimBOp Gt)  = error "inferPrim Gt should be unreachable"-    inferPrim (PrimBOp Leq) = error "inferPrim Leq should be unreachable"-    inferPrim (PrimBOp Geq) = error "inferPrim Geq should be unreachable"-    --------------------------------------------------------------    inferPrim (PrimBOp op) | op `elem` [Min, Max] = do-      ty <- freshTy-      constraint $ CQual QCmp ty-      return $ ty :*: ty :->: ty--    -- See Note [Pattern coverage] ------------------------------    inferPrim (PrimBOp Min) = error "inferPrim Min should be unreachable"-    inferPrim (PrimBOp Max) = error "inferPrim Max should be unreachable"-    --------------------------------------------------------------    -----------------------------------------    -- Special arithmetic functions: fact, sqrt, floor, ceil, abs--    inferPrim (PrimUOp Fact) = return $ TyN :->: TyN-    inferPrim PrimSqrt = return $ TyN :->: TyN--    inferPrim p | p `elem` [PrimFloor, PrimCeil] = do-      argTy <- freshTy-      resTy <- cInt argTy-      return $ argTy :->: resTy--    -- See Note [Pattern coverage] ------------------------------    inferPrim PrimFloor = error "inferPrim Floor should be unreachable"-    inferPrim PrimCeil  = error "inferPrim Ceil should be unreachable"-    --------------------------------------------------------------    inferPrim PrimAbs = do-      argTy <- freshTy-      resTy <- freshTy-      cAbs argTy resTy `cOr` cSize argTy resTy-      return $ argTy :->: resTy--    -----------------------------------------    -- power set/bag--    inferPrim PrimPower = do-      a <- freshTy-      c <- freshAtom--      constraint $ CQual QCmp a-      constraint $ COr-        [ CEq (TyAtom (ABase CtrSet)) (TyAtom c)-        , CEq (TyAtom (ABase CtrBag)) (TyAtom c)-        ]--      return $ TyContainer c a :->: TyContainer c (TyContainer c a)--    inferPrim PrimLookupSeq = return $ TyList TyN :->: (TyUnit :+: TyString)-    inferPrim PrimExtendSeq = return $ TyList TyN :->: TyList TyN------------------------------------------------------- Base types---- A few trivial cases for base types.-typecheck Infer             TUnit        = return ATUnit-typecheck Infer             (TBool b)    = return $ ATBool TyBool b-typecheck Infer             (TChar c)    = return $ ATChar c-typecheck Infer             (TString cs) = return $ ATString cs--- typecheck (Check (TyFin n)) (TNat x)     = return $ ATNat (TyFin n) x-typecheck Infer             (TNat n)     = return $ ATNat TyN n-typecheck Infer             (TRat r)     = return $ ATRat r--typecheck _                 TWild        = throw NoTWild------------------------------------------------------- Abstractions (lambdas and quantifiers)---- Lambdas and quantifiers are similar enough that we can share a--- bunch of the code, but their typing rules are a bit different.  In--- particular a lambda------   \(x1:ty1), (x2:ty2) ... . body------ is going to have a type like ty1 -> ty2 -> ... -> resTy, whereas a--- quantifier like------   ∃(x1:ty1), (x2:ty2) ... . body------ is just going to have the type Prop.  The similarity is that in--- both cases we have to generate unification variables for any--- binders with omitted type annotations, and typecheck the body under--- an extended context.---- It's only helpful to do lambdas in checking mode, since the--- provided function type can provide information about the types of--- the arguments.  For other quantifiers we can just fall back to--- inference mode.-typecheck (Check checkTy) tm@(TAbs Lam body) = do-  (args, t) <- unbind body--  -- First check that the given type is of the form ty1 -> ty2 ->-  -- ... -> resTy, where the types ty1, ty2 ... match up with any-  -- types declared for the arguments to the lambda (e.g.  (x:tyA)-  -- (y:tyB) -> ...).-  (ctx, typedArgs, resTy) <- checkArgs args checkTy tm--  -- Then check the type of the body under a context extended with-  -- types for all the arguments.-  extends ctx $-    ATAbs Lam checkTy <$> (bind (coerce typedArgs) <$> check t resTy)--  where--    -- Given the patterns and their optional type annotations in the-    -- head of a lambda (e.g.  @x (y:Z) (f : N -> N) -> ...@), and the-    -- type at which we are checking the lambda, ensure that:-    ---    --   - The type is of the form @ty1 -> ty2 -> ... -> resTy@ and-    --     there are enough @ty1@, @ty2@, ... to match all the arguments.-    --   - Each pattern successfully checks at its corresponding type.-    ---    -- If it succeeds, return a context binding variables to their-    -- types (as determined by the patterns and the input types) which-    -- we can use to extend when checking the body, a list of the typed-    -- patterns, and the result type of the function.-    checkArgs-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => [Pattern] -> Type -> Term -> Sem r (TyCtx, [APattern], Type)--    -- If we're all out of arguments, the remaining checking type is the-    -- result, and there are no variables to bind in the context.-    checkArgs [] ty _ = return (emptyCtx, [], ty)--    -- Take the next pattern and its annotation; the checking type must-    -- be a function type ty1 -> ty2.-    checkArgs (p : args) ty term = do--      -- Ensure that ty is a function type-      (ty1, ty2) <- ensureConstr2 CArr ty (Left term)--      -- Check the argument pattern against the function domain.-      (pCtx, pTyped) <- checkPattern p ty1--      -- Check the rest of the arguments under the type ty2, returning a-      -- context with the rest of the arguments and the final result type.-      (ctx, typedArgs, resTy) <- checkArgs args ty2 term--      -- Pass the result type through, and put the pattern-bound variables-      -- in the returned context.-      return (pCtx <> ctx, pTyped : typedArgs, resTy)---- In inference mode, we handle lambdas as well as quantifiers (∀, ∃).-typecheck Infer (TAbs q lam)    = do--  -- Open it and get the argument patterns with any type annotations.-  (args, t) <- unbind lam--  -- Replace any missing type annotations with fresh type variables,-  -- and check each pattern at that variable to refine them, collecting-  -- the types of each pattern's bound variables in a context.-  tys <- mapM getAscrOrFresh args-  (pCtxs, typedPats) <- unzip <$> zipWithM checkPattern args tys--  -- In the case of ∀, ∃, have to ensure that the argument types are-  -- searchable.-  when (q `elem` [All, Ex]) $-    -- What's the difference between this and `tys`? Nothing, after-    -- the solver runs, but right now the patterns might have a-    -- concrete type from annotations inside tuples.-    forM_ (map getType typedPats) $ \ty ->-      unless (isSearchable ty) $-        throw $ NoSearch ty--  -- Extend the context with the given arguments, and then do-  -- something appropriate depending on the quantifier.-  extends (mconcat pCtxs) $ do-    case q of-      -- For lambdas, infer the type of the body, and return an appropriate-      -- function type.-      Lam -> do-        at <- infer t-        return $ ATAbs Lam (mkFunTy tys (getType at)) (bind typedPats at)--      -- For other quantifiers, check that the body has type Prop,-      -- and return Prop.-      _   -> do  -- ∀, ∃-        at <- check t TyProp-        return $ ATAbs q TyProp (bind typedPats at)-  where-    getAscrOrFresh-      :: Members '[Reader TyDefCtx, Error TCError, Fresh] r-      => Pattern -> Sem r Type-    getAscrOrFresh (PAscr _ ty) = checkTypeValid ty >> pure ty-    getAscrOrFresh _            = freshTy--    -- mkFunTy [ty1, ..., tyn] out = ty1 -> (ty2 -> ... (tyn -> out))-    mkFunTy :: [Type] -> Type -> Type-    mkFunTy tys out = foldr (:->:) out tys------------------------------------------------------- Application---- Infer the type of a function application by inferring the function--- type and then checking the argument type.  We don't need a checking--- case because checking mode doesn't help.-typecheck Infer (TApp t t')   = do-  at <- infer t-  let ty = getType at-  (ty1, ty2) <- ensureConstr2 CArr ty (Left t)-  ATApp ty2 at <$> check t' ty1------------------------------------------------------- Tuples---- Check/infer the type of a tuple.-typecheck mode1 (TTup tup) = uncurry ATTup <$> typecheckTuple mode1 tup-  where-    typecheckTuple-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Mode -> [Term] -> Sem r (Type, [ATerm])-    typecheckTuple _    []     = error "Impossible! typecheckTuple []"-    typecheckTuple mode [t]    = (getType &&& (:[])) <$> typecheck mode t-    typecheckTuple mode (t:ts) = do-      (m,ms)    <- ensureConstrMode2 CProd mode (Left $ TTup (t:ts))-      at        <- typecheck      m  t-      (ty, ats) <- typecheckTuple ms ts-      return (getType at :*: ty, at : ats)--------------------------------------------- Comparison chain--typecheck Infer (TChain t ls) =-  ATChain TyBool <$> infer t <*> inferChain t ls--  where-    inferChain-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Term -> [Link] -> Sem r [ALink]-    inferChain _  [] = return []-    inferChain t1 (TLink op t2 : links) = do-      at2 <- infer t2-      _   <- check (TBin op t1 t2) TyBool-      atl <- inferChain t2 links-      return $ ATLink op at2 : atl--------------------------------------------- Type operations--typecheck Infer (TTyOp Enumerate t) = do-  checkTypeValid t-  return $ ATTyOp (TyList t) Enumerate t--typecheck Infer (TTyOp Count t)     = do-  checkTypeValid t-  return $ ATTyOp (TyUnit :+: TyN) Count t------------------------------------------------------- Containers---- Literal containers, including ellipses-typecheck mode t@(TContainer c xs ell)  = do-  eltMode <- ensureConstrMode1 (containerToCon c) mode (Left t)-  axns  <- mapM (\(x,n) -> (,) <$> typecheck eltMode x <*> traverse (`check` TyN) n) xs-  aell  <- typecheckEllipsis eltMode ell-  resTy <- case mode of-    Infer -> do-      let tys = [ getType at | Just (Until at) <- [aell] ] ++ map (getType . fst) axns-      tyv  <- freshTy-      constraints $ map (`CSub` tyv) tys-      return $ containerTy c tyv-    Check ty -> return ty-  eltTy <- getEltTy c resTy--  -- See Note [Container literal constraints]-  when (c /= ListContainer && not (P.null xs)) $ constraint $ CQual QCmp eltTy-  when (isJust ell) $ constraint $ CQual QEnum eltTy-  return $ ATContainer resTy c axns aell--  where-    typecheckEllipsis-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Mode -> Maybe (Ellipsis Term) -> Sem r (Maybe (Ellipsis ATerm))-    typecheckEllipsis _ Nothing           = return Nothing-    typecheckEllipsis m (Just (Until tm)) = Just . Until <$> typecheck m tm---- ~~~~ Note [Container literal constraints]------ It should only be possible to construct something of type Set(a) or--- Bag(a) when a is comparable, so we can normalize the set or bag--- value.  For example, Set(N) is OK, but Set(N -> N) is not.  On the--- other hand, List(a) is fine for any type a.  We want to maintain--- the invariant that we can only actually obtain a value of type--- Set(a) or Bag(a) if a is comparable.  This means we will be able to--- write polymorphic functions that take bags or sets as input without--- having to specify any constraints --- the only way to call such--- functions is with element types that actually support comparison.--- For example, 'unions' can simply have the type Set(Set(a)) ->--- Set(a).------ Hence, container literals (along with the 'set' and 'bag'--- conversion functions) serve as "gatekeepers" to make sure we can--- only construct containers with appropriate element types.  So when--- we see a container literal, if it is a bag or set literal, we have--- to introduce an additional QCmp constraint for the element type.------ But not so fast --- with that rule, 'unions' does not type check!--- To see why, look at the definition:------   unions(ss) = foldr(~∪~, {}, list(ss))------ The empty set literal in the definition means we end up generating--- a QCmp constraint on the element type anyway.  But there is a--- solution: we refine our invariant to say that we can only--- actually obtain a *non-empty* value of type Set(a) or Bag(a) if a--- is comparable.  Empty bags and sets are allowed to have any element--- type.  This is safe because there is no way to generate a non-empty--- set from an empty one, without also making use of something like a--- non-empty set literal or conversion function.  So we add a special--- case to the rule that says we only add a QCmp constraint in the--- case of a *non-empty* set or bag literal.  Now the definition of--- 'unions' type checks perfectly well.---- Container comprehensions-typecheck mode tcc@(TContainerComp c bqt) = do-  eltMode <- ensureConstrMode1 (containerToCon c) mode (Left tcc)-  (qs, t)   <- unbind bqt-  (aqs, cx) <- inferTelescope inferQual qs-  extends cx $ do-    at <- typecheck eltMode t-    let resTy = case mode of-          Infer    -> containerTy c (getType at)-          Check ty -> ty-    return $ ATContainerComp resTy c (bind aqs at)--  where-    inferQual-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Qual -> Sem r (AQual, TyCtx)-    inferQual (QBind x (unembed -> t))  = do-      at <- infer t-      ty <- ensureConstr1 (containerToCon c) (getType at) (Left t)-      return (AQBind (coerce x) (embed at), singleCtx (localName x) (toPolyType ty))--    inferQual (QGuard (unembed -> t))   = do-      at <- check t TyBool-      return (AQGuard (embed at), emptyCtx)------------------------------------------------------- Let---- To check/infer a let expression.  Note let is non-recursive.-typecheck mode (TLet l) = do-  (bs, t2) <- unbind l--  -- Infer the types of all the variables bound by the let...-  (as, ctx) <- inferTelescope inferBinding bs--  -- ...then check/infer the body under an extended context.-  extends ctx $ do-    at2 <- typecheck mode t2-    return $ ATLet (getType at2) (bind as at2)--  where--    -- Infer the type of a binding (@x [: ty] = t@), returning a-    -- type-annotated binding along with a (singleton) context for the-    -- bound variable.  The optional type annotation on the variable-    -- determines whether we use inference or checking mode for the-    -- body.-    inferBinding-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Binding -> Sem r (ABinding, TyCtx)-    inferBinding (Binding mty x (unembed -> t)) = do-      at <- case mty of-        Just (unembed -> ty) -> checkPolyTy t ty-        Nothing              -> infer t-      return (ABinding mty (coerce x) (embed at), singleCtx (localName x) (toPolyType $ getType at))------------------------------------------------------- Case---- Check/infer a case expression.-typecheck _    (TCase []) = throw EmptyCase-typecheck mode (TCase bs) = do-  bs' <- mapM typecheckBranch bs-  resTy <- case mode of-    Check ty -> return ty-    Infer    -> do-      x <- freshTy-      constraints $ map ((`CSub` x) . getType) bs'-      return x-  return $ ATCase resTy bs'--  where-    typecheckBranch-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Branch -> Sem r ABranch-    typecheckBranch b = do-      (gs, t) <- unbind b-      (ags, ctx) <- inferTelescope inferGuard gs-      extends ctx $-        bind ags <$> typecheck mode t--    -- Infer the type of a guard, returning the type-annotated guard-    -- along with a context of types for any variables bound by the-    -- guard.-    inferGuard-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => Guard -> Sem r (AGuard, TyCtx)-    inferGuard (GBool (unembed -> t)) = do-      at <- check t TyBool-      return (AGBool (embed at), emptyCtx)-    inferGuard (GPat (unembed -> t) p) = do-      at <- infer t-      (ctx, apt) <- checkPattern p (getType at)-      return (AGPat (embed at) apt, ctx)-    inferGuard (GLet (Binding mty x (unembed -> t))) = do-      at <- case mty of-        Just (unembed -> ty) -> checkPolyTy t ty-        Nothing              -> infer t-      return-        ( AGLet (ABinding mty (coerce x) (embed at))-        , singleCtx (localName x) (toPolyType (getType at))-        )------------------------------------------------------- Type ascription---- Ascriptions are what let us flip from inference mode into--- checking mode.-typecheck Infer (TAscr t ty) = checkPolyTyValid ty >> checkPolyTy t ty------------------------------------------------------- Inference fallback---- Finally, to check anything else, we can fall back to inferring its--- type and then check that the inferred type is a *subtype* of the--- given type.  We have to be careful to call 'setType' to change the--- type at the root of the term to the requested type.-typecheck (Check ty) t = do-  at <- infer t-  constraint $ CSub (getType at) ty-  return $ setType ty at----------------------------------------------------------------- Patterns----------------------------------------------------------------- | Check that a pattern has the given type, and return a context of---   pattern variables bound in the pattern along with their types.-checkPattern-  :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Pattern -> Type -> Sem r (TyCtx, APattern)--checkPattern p (TyUser name args) = lookupTyDefn name args >>= checkPattern p--checkPattern (PVar x) ty = return (singleCtx (localName x) (toPolyType ty), APVar ty (coerce x))--checkPattern PWild    ty = return (emptyCtx, APWild ty)--checkPattern (PAscr p ty1) ty2 = do-  -- We have a pattern that promises to match ty1 and someone is asking-  -- us if it can also match ty2. So we just have to ensure what we're-  -- being asked for is a subtype of what we can promise to cover...-  constraint $ CSub ty2 ty1-  -- ... and then make sure the pattern can actually match what it promised to.-  checkPattern p ty1--checkPattern PUnit ty = do-  ensureEq ty TyUnit-  return (emptyCtx, APUnit)--checkPattern (PBool b) ty = do-  ensureEq ty TyBool-  return (emptyCtx, APBool b)--checkPattern (PChar c) ty = do-  ensureEq ty TyC-  return (emptyCtx, APChar c)--checkPattern (PString s) ty = do-  ensureEq ty TyString-  return (emptyCtx, APString s)--checkPattern (PTup tup) tupTy = do-  listCtxtAps <- checkTuplePat tup tupTy-  let (ctxs, aps) = unzip listCtxtAps-  return (mconcat ctxs, APTup (foldr1 (:*:) (map getType aps)) aps)--  where-    checkTuplePat-      :: Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-      => [Pattern] -> Type -> Sem r [(TyCtx, APattern)]-    checkTuplePat [] _   = error "Impossible! checkTuplePat []"-    checkTuplePat [p] ty = do     -- (:[]) <$> check t ty-      (ctx, apt) <- checkPattern p ty-      return [(ctx, apt)]-    checkTuplePat (p:ps) ty = do-      (ty1, ty2) <- ensureConstr2 CProd ty (Right $ PTup (p:ps))-      (ctx, apt) <- checkPattern p ty1-      rest <- checkTuplePat ps ty2-      return ((ctx, apt) : rest)--checkPattern p@(PInj L pat) ty       = do-  (ty1, ty2) <- ensureConstr2 CSum ty (Right p)-  (ctx, apt) <- checkPattern pat ty1-  return (ctx, APInj (ty1 :+: ty2) L apt)-checkPattern p@(PInj R pat) ty    = do-  (ty1, ty2) <- ensureConstr2 CSum ty (Right p)-  (ctx, apt) <- checkPattern pat ty2-  return (ctx, APInj (ty1 :+: ty2) R apt)---- we can match any supertype of TyN against a Nat pattern, OR--- any TyFin.---- XXX this isn't quite right, what if we're checking at a type--- variable but we need to solve it to be a TyFin?  Can this ever--- happen?  We would need a COr, except we can't express the--- constraint "exists m. ty = TyFin m"------ Yes, this can happen, and here's an example:------   > (\x. {? true when x is 3, false otherwise ?}) (2 : Z5)---   Unsolvable NoUnify---   > (\(x : Z5). {? true when x is 3, false otherwise ?}) (2 : Z5)---   false---- checkPattern (PNat n) (TyFin m) = return (emptyCtx, APNat (TyFin m) n)-checkPattern (PNat n) ty        = do-  constraint $ CSub TyN ty-  return (emptyCtx, APNat ty n)--checkPattern p@(PCons p1 p2) ty = do-  tyl <- ensureConstr1 CList ty (Right p)-  (ctx1, ap1) <- checkPattern p1 tyl-  (ctx2, ap2) <- checkPattern p2 (TyList tyl)-  return (ctx1 <> ctx2, APCons (TyList tyl) ap1 ap2)--checkPattern p@(PList ps) ty = do-  tyl <- ensureConstr1 CList ty (Right p)-  listCtxtAps <- mapM (`checkPattern` tyl) ps-  let (ctxs, aps) = unzip listCtxtAps-  return (mconcat ctxs, APList (TyList tyl) aps)--checkPattern (PAdd s p t) ty = do-  constraint $ CQual QNum ty-  (ctx, apt) <- checkPattern p ty-  at <- check t ty-  return (ctx, APAdd ty s apt at)--checkPattern (PMul s p t) ty = do-  constraint $ CQual QNum ty-  (ctx, apt) <- checkPattern p ty-  at <- check t ty-  return (ctx, APMul ty s apt at)--checkPattern (PSub p t) ty = do-  constraint $ CQual QNum ty-  (ctx, apt) <- checkPattern p ty-  at <- check t ty-  return (ctx, APSub ty apt at)--checkPattern (PNeg p) ty = do-  constraint $ CQual QSub ty-  tyInner <- cPos ty-  (ctx, apt) <- checkPattern p tyInner-  return (ctx, APNeg ty apt)--checkPattern (PFrac p q) ty = do-  constraint $ CQual QDiv ty-  tyP <- cInt ty-  tyQ <- cPos tyP-  (ctx1, ap1) <- checkPattern p tyP-  (ctx2, ap2) <- checkPattern q tyQ-  return (ctx1 <> ctx2, APFrac ty ap1 ap2)----------------------------------------------------------------- Constraints for abs, floor/ceiling/idiv, and exp----------------------------------------------------------------- | Constraints needed on a function type for it to be the type of---   the absolute value function.-cAbs :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r ()-cAbs argTy resTy = do-  resTy' <- cPos argTy-  constraint $ CEq resTy resTy'---- | Constraints needed on a function type for it to be the type of---   the container size operation.-cSize :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r ()-cSize argTy resTy = do-  a <- freshTy-  c <- freshAtom-  constraint $ CEq (TyContainer c a) argTy-  constraint $ CEq TyN resTy---- | Given an input type @ty@, return a type which represents the---   output type of the absolute value function, and generate---   appropriate constraints.-cPos :: Members '[Writer Constraint, Fresh] r => Type -> Sem r Type-cPos ty = do-  constraint $ CQual QNum ty   -- The input type has to be numeric.-  case ty of-    -- If the input type is a concrete base type, we can just-    -- compute the correct output type.-    TyAtom (ABase b) -> return $ TyAtom (ABase (pos b))--    -- Otherwise, generate a fresh type variable for the output type-    -- along with some constraints.-    _ -> do-      res <- freshTy--      -- Valid types for absolute value are Z -> N, Q -> F, or T -> T-      -- (e.g. Z5 -> Z5).-      constraint $ COr-        [ cAnd [CSub ty TyZ, CSub TyN res]-        , cAnd [CSub ty TyQ, CSub TyF res]-        , CEq ty res-        ]-      return res-  where-    pos Z = N-    pos Q = F-    pos t = t---- | Given an input type @ty@, return a type which represents the---   output type of the floor or ceiling functions, and generate---   appropriate constraints.-cInt :: Members '[Writer Constraint, Fresh] r => Type -> Sem r Type-cInt ty = do-  constraint $ CQual QNum ty-  case ty of-    -- If the input type is a concrete base type, we can just-    -- compute the correct output type.-    TyAtom (ABase b) -> return $ TyAtom (ABase (int b))--    -- Otherwise, generate a fresh type variable for the output type-    -- along with some constraints.-    _ -> do-      res <- freshTy--      -- Valid types for absolute value are F -> N, Q -> Z, or T -> T-      -- (e.g. Z5 -> Z5).-      constraint $ COr-        [ cAnd [CSub ty TyF, CSub TyN res]-        , cAnd [CSub ty TyQ, CSub TyZ res]-        , CEq ty res-        ]-      return res--  where-    int F = N-    int Q = Z-    int t = t---- | Given input types to the exponentiation operator, return a type---   which represents the output type, and generate appropriate---   constraints.-cExp :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r Type-cExp ty1 TyN = do-  constraint $ CQual QNum ty1-  return ty1---- We could include a special case for TyZ, but for that we would need--- a function to find a supertype of a given type that satisfies QDiv.--cExp ty1 ty2 = do--  -- Create a fresh type variable to represent the result type.  The-  -- base type has to be a subtype.-  resTy <- freshTy-  constraint $ CSub ty1 resTy--  -- Either the exponent type is N, in which case the result type has-  -- to support multiplication, or else the exponent is Z, in which-  -- case the result type also has to support division.-  constraint $ COr-    [ cAnd [CQual QNum resTy, CEq ty2 TyN]-    , cAnd [CQual QDiv resTy, CEq ty2 TyZ]-    ]-  return resTy----------------------------------------------------------------- Decomposing type constructors----------------------------------------------------------------- | Get the argument (element) type of a (known) container type.  Returns a---   fresh variable with a suitable constraint if the given type is---   not literally a container type.-getEltTy :: Members '[Writer Constraint, Fresh] r => Container -> Type -> Sem r Type-getEltTy _ (TyContainer _ e) = return e-getEltTy c ty = do-  eltTy <- freshTy-  constraint $ CEq (containerTy c eltTy) ty-  return eltTy---- | Ensure that a type's outermost constructor matches the provided---   constructor, returning the types within the matched constructor---   or throwing a type error.  If the type provided is a type---   variable, appropriate constraints are generated to guarantee the---   type variable's outermost constructor matches the provided---   constructor, and a list of fresh type variables is returned whose---   count matches the arity of the provided constructor.-ensureConstr-  :: forall r. Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Type -> Either Term Pattern -> Sem r [Type]-ensureConstr c ty targ = matchConTy c ty-  where-    matchConTy :: Con -> Type -> Sem r [Type]--    -- expand type definitions lazily-    matchConTy c1 (TyUser name args) = lookupTyDefn name args >>= matchConTy c1--    matchConTy c1 (TyCon c2 tys) = do-      matchCon c1 c2-      return tys--    matchConTy c1 tyv@(TyAtom (AVar (U _))) = do-      tyvs <- mapM (const freshTy) (arity c1)-      constraint $ CEq tyv (TyCon c1 tyvs)-      return tyvs--    matchConTy _ _ = matchError--    -- | Check whether two constructors match, which could include-    --   unifying container variables if we are matching two container-    --   types; otherwise, simply ensure that the constructors are-    --   equal.  Throw a 'matchError' if they do not match.-    matchCon :: Con -> Con -> Sem r ()-    matchCon c1 c2                            | c1 == c2 = return ()-    matchCon (CContainer v@(AVar (U _))) (CContainer ctr2) =-      constraint $ CEq (TyAtom v) (TyAtom ctr2)-    matchCon (CContainer ctr1) (CContainer v@(AVar (U _))) =-      constraint $ CEq (TyAtom ctr1) (TyAtom v)-    matchCon _ _                              = matchError--    matchError :: Sem r a-    matchError = case targ of-      Left term -> throw (NotCon c term ty)-      Right pat -> throw (PatternType c pat ty)---- | A variant of ensureConstr that expects to get exactly one---   argument type out, and throws an error if we get any other---   number.-ensureConstr1-  :: Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Type -> Either Term Pattern -> Sem r Type-ensureConstr1 c ty targ = do-  tys <- ensureConstr c ty targ-  case tys of-    [ty1] -> return ty1-    _     -> error $-      "Impossible! Wrong number of arg types in ensureConstr1 " ++ show c ++ " "-        ++ show ty ++ ": " ++ show tys---- | A variant of ensureConstr that expects to get exactly two---   argument types out, and throws an error if we get any other---   number.-ensureConstr2-  :: Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Type -> Either Term Pattern -> Sem r (Type, Type)-ensureConstr2 c ty targ  = do-  tys <- ensureConstr c ty targ-  case tys of-    [ty1, ty2] -> return (ty1, ty2)-    _          -> error $-      "Impossible! Wrong number of arg types in ensureConstr2 " ++ show c ++ " "-        ++ show ty ++ ": " ++ show tys---- | A variant of 'ensureConstr' that works on 'Mode's instead of---   'Type's.  Behaves similarly to 'ensureConstr' if the 'Mode' is---   'Check'; otherwise it generates an appropriate number of copies---   of 'Infer'.-ensureConstrMode-  :: Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Mode -> Either Term Pattern -> Sem r [Mode]-ensureConstrMode c Infer      _  = return $ map (const Infer) (arity c)-ensureConstrMode c (Check ty) tp = map Check <$> ensureConstr c ty tp---- | A variant of 'ensureConstrMode' that expects to get a single---   'Mode' and throws an error if it encounters any other number.-ensureConstrMode1-  :: Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Mode -> Either Term Pattern -> Sem r Mode-ensureConstrMode1 c m targ = do-  ms <- ensureConstrMode c m targ-  case ms of-    [m1] -> return m1-    _    -> error $-      "Impossible! Wrong number of arg types in ensureConstrMode1 " ++ show c ++ " "-        ++ show m ++ ": " ++ show ms---- | A variant of 'ensureConstrMode' that expects to get two 'Mode's---   and throws an error if it encounters any other number.-ensureConstrMode2-  :: Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r-  => Con -> Mode -> Either Term Pattern -> Sem r (Mode, Mode)-ensureConstrMode2 c m targ = do-  ms <- ensureConstrMode c m targ-  case ms of-    [m1, m2] -> return (m1, m2)-    _        -> error $-      "Impossible! Wrong number of arg types in ensureConstrMode2 " ++ show c ++ " "-        ++ show m ++ ": " ++ show ms---- | Ensure that two types are equal:---     1. Do nothing if they are literally equal---     2. Generate an equality constraint otherwise-ensureEq :: Member (Writer Constraint) r => Type -> Type -> Sem r ()-ensureEq ty1 ty2-  | ty1 == ty2 = return ()-  | otherwise  = constraint $ CEq ty1 ty2+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NondecreasingIndentation #-}+{-# LANGUAGE OverloadedStrings #-}++-----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- |+-- Module      :  Disco.Typecheck+-- Copyright   :  disco team and contributors+-- Maintainer  :  byorgey@gmail.com+--+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Typecheck the Disco surface language and transform it into a+-- type-annotated AST.+module Disco.Typecheck where++import Control.Arrow ((&&&))+import Control.Lens ((^..))+import Control.Monad.Except+import Control.Monad.Trans.Maybe+import Data.Bifunctor (first)+import Data.Coerce+import qualified Data.Foldable as F+import Data.List (group, sort)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe (isJust)+import Data.Set (Set)+import qualified Data.Set as S+import Prelude as P hiding (lookup)++import Unbound.Generics.LocallyNameless (+  Alpha,+  Bind,+  Name,+  bind,+  embed,+  name2String,+  string2Name,+  substs,+  unembed,+ )+import Unbound.Generics.LocallyNameless.Unsafe (unsafeUnbind)++import Disco.Effects.Fresh+import Polysemy hiding (embed)+import Polysemy.Error+import Polysemy.Output+import Polysemy.Reader+import Polysemy.Writer++import Disco.AST.Surface+import Disco.AST.Typed+import Disco.Context hiding (filter)+import qualified Disco.Context as Ctx+import Disco.Messages+import Disco.Module+import Disco.Names+import Disco.Subst (applySubst)+import qualified Disco.Subst as Subst+import Disco.Syntax.Operators+import Disco.Syntax.Prims+import Disco.Typecheck.Constraints+import Disco.Typecheck.Util+import Disco.Types+import Disco.Types.Rules++------------------------------------------------------------+-- Container utilities+------------------------------------------------------------++containerTy :: Container -> Type -> Type+containerTy c ty = TyCon (containerToCon c) [ty]++containerToCon :: Container -> Con+containerToCon ListContainer = CList+containerToCon BagContainer = CBag+containerToCon SetContainer = CSet++------------------------------------------------------------+-- Telescopes+------------------------------------------------------------++-- | Infer the type of a telescope, given a way to infer the type of+--   each item along with a context of variables it binds; each such+--   context is then added to the overall context when inferring+--   subsequent items in the telescope.+inferTelescope ::+  (Alpha b, Alpha tyb, Member (Reader TyCtx) r) =>+  (b -> Sem r (tyb, TyCtx)) ->+  Telescope b ->+  Sem r (Telescope tyb, TyCtx)+inferTelescope inferOne tel = do+  (tel1, ctx) <- go (fromTelescope tel)+  return (toTelescope tel1, ctx)+ where+  go [] = return ([], emptyCtx)+  go (b : bs) = do+    (tyb, ctx) <- inferOne b+    extends ctx $ do+      (tybs, ctx') <- go bs+      return (tyb : tybs, ctx <> ctx')++------------------------------------------------------------+-- Modules+------------------------------------------------------------++-- | Check all the types and extract all relevant info (docs,+--   properties, types) from a module, returning a 'ModuleInfo' record+--   on success.  This function does not handle imports at all; any+--   imports should already be checked and passed in as the second+--   argument.+checkModule ::+  Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh] r =>+  ModuleName ->+  Map ModuleName ModuleInfo ->+  Module ->+  Sem r ModuleInfo+checkModule name imports (Module es _ m docs terms) = do+  let (typeDecls, defns, tydefs) = partitionDecls m+      importTyCtx = mconcat (imports ^.. traverse . miTys)+      -- XXX this isn't right, if multiple modules define the same type synonyms.+      -- Need to use a normal Ctx for tydefs too.+      importTyDefnCtx = M.unions (imports ^.. traverse . miTydefs)+  tyDefnCtx <- mapError noLoc $ makeTyDefnCtx tydefs+  withTyDefns (tyDefnCtx `M.union` importTyDefnCtx) $ do+    tyCtx <- mapError noLoc $ makeTyCtx name typeDecls+    extends importTyCtx $ extends tyCtx $ do+      mapM_ (checkTyDefn name) tydefs+      adefns <- mapM (checkDefn name) defns+      let defnCtx = ctxForModule name (map (getDefnName &&& id) adefns)+          docCtx = ctxForModule name docs+          dups = filterDups . map getDefnName $ adefns+      case dups of+        (x : _) -> throw $ noLoc $ DuplicateDefns (coerce x)+        [] -> do+          aprops <- mapError noLoc $ checkProperties docCtx -- XXX location?+          aterms <- mapError noLoc $ mapM inferTop terms -- XXX location?+          return $ ModuleInfo name imports (map ((name .-) . getDeclName) typeDecls) docCtx aprops tyCtx tyDefnCtx defnCtx aterms es+ where+  getDefnName :: Defn -> Name ATerm+  getDefnName (Defn n _ _ _) = n++  getDeclName :: TypeDecl -> Name Term+  getDeclName (TypeDecl n _) = n++--------------------------------------------------+-- Type definitions++-- | Turn a list of type definitions into a 'TyDefCtx', checking+--   for duplicate names among the definitions and also any type+--   definitions already in the context.+makeTyDefnCtx :: Members '[Reader TyDefCtx, Error TCError] r => [TypeDefn] -> Sem r TyDefCtx+makeTyDefnCtx tydefs = do+  oldTyDefs <- ask @TyDefCtx+  let oldNames = M.keys oldTyDefs+      newNames = map (\(TypeDefn x _ _) -> x) tydefs+      dups = filterDups $ newNames ++ oldNames++  let convert (TypeDefn x args body) =+        (x, TyDefBody args (flip substs body . zip (map string2Name args)))++  case dups of+    (x : _) -> throw (DuplicateTyDefns x)+    [] -> return . M.fromList $ map convert tydefs++-- | Check the validity of a type definition.+checkTyDefn :: Members '[Reader TyDefCtx, Error LocTCError] r => ModuleName -> TypeDefn -> Sem r ()+checkTyDefn name defn@(TypeDefn x args body) = mapError (LocTCError (Just (name .- string2Name x))) $ do+  -- First, make sure the body is a valid type, i.e. everything inside+  -- it is well-kinded.+  checkTypeValid body++  -- Now make sure it is not directly cyclic (i.e. ensure it is a+  -- "productive" definition).+  _ <- checkCyclicTy (TyUser x (map (TyVar . string2Name) args)) S.empty++  -- Make sure it does not use any unbound type variables or undefined+  -- types.+  checkUnboundVars defn++  -- Make sure it does not use any polymorphic recursion (polymorphic+  -- recursion isn't allowed at the moment since it can make the+  -- subtyping checker diverge).+  checkPolyRec defn++-- | Check if a given type is cyclic. A type 'ty' is cyclic if:+--+--   1. 'ty' is the name of a user-defined type.+--   2. Repeated expansions of the type yield nothing but other user-defined types.+--   3. An expansion of one of those types yields another type that has+--      been previously encountered.+--+--   In other words, repeatedly expanding the definition can get us+--   back to exactly where we started.+--+--   The function returns the set of TyDefs encountered during+--   expansion if the TyDef is not cyclic.+checkCyclicTy :: Members '[Reader TyDefCtx, Error TCError] r => Type -> Set String -> Sem r (Set String)+checkCyclicTy (TyUser name args) set = do+  case S.member name set of+    True -> throw $ CyclicTyDef name+    False -> do+      ty <- lookupTyDefn name args+      checkCyclicTy ty (S.insert name set)+checkCyclicTy _ set = return set++-- | Ensure that a type definition does not use any unbound type+--   variables or undefined types.+checkUnboundVars :: Members '[Reader TyDefCtx, Error TCError] r => TypeDefn -> Sem r ()+checkUnboundVars (TypeDefn _ args body) = go body+ where+  go (TyAtom (AVar (U x)))+    | name2String x `elem` args = return ()+    | otherwise = throw $ UnboundTyVar x+  go (TyAtom _) = return ()+  go (TyUser name tys) = lookupTyDefn name tys >> mapM_ go tys+  go (TyCon _ tys) = mapM_ go tys++-- | Check for polymorphic recursion: starting from a user-defined+--   type, keep expanding its definition recursively, ensuring that+--   any recursive references to the defined type have only type variables+--   as arguments.+checkPolyRec :: Member (Error TCError) r => TypeDefn -> Sem r ()+checkPolyRec (TypeDefn name args body) = go body+ where+  go (TyCon (CUser x) tys)+    | x == name && not (all isTyVar tys) =+        throw $ NoPolyRec name args tys+    | otherwise = return ()+  go (TyCon _ tys) = mapM_ go tys+  go _ = return ()++-- | Keep only the duplicate elements from a list.+--+--   >>> filterDups [1,3,2,1,1,4,2]+--   [1,2]+filterDups :: Ord a => [a] -> [a]+filterDups = map head . filter ((> 1) . length) . group . sort++--------------------------------------------------+-- Type declarations++-- | Given a list of type declarations from a module, first check that+--   there are no duplicate type declarations, and that the types are+--   well-formed; then create a type context containing the given+--   declarations.+makeTyCtx :: Members '[Reader TyDefCtx, Error TCError] r => ModuleName -> [TypeDecl] -> Sem r TyCtx+makeTyCtx name decls = do+  let dups = filterDups . map (\(TypeDecl x _) -> x) $ decls+  case dups of+    (x : _) -> throw (DuplicateDecls x)+    [] -> do+      checkCtx declCtx+      return declCtx+ where+  declCtx = ctxForModule name $ map (\(TypeDecl x ty) -> (x, ty)) decls++-- | Check that all the types in a context are valid.+checkCtx :: Members '[Reader TyDefCtx, Error TCError] r => TyCtx -> Sem r ()+checkCtx = mapM_ checkPolyTyValid . Ctx.elems++--------------------------------------------------+-- Top-level definitions++-- | Type check a top-level definition in the given module.+checkDefn ::+  Members '[Reader TyCtx, Reader TyDefCtx, Error LocTCError, Fresh, Output (Message ann)] r =>+  ModuleName ->+  TermDefn ->+  Sem r Defn+checkDefn name (TermDefn x clauses) = mapError (LocTCError (Just (name .- x))) $ do+  -- Check that all clauses have the same number of patterns+  checkNumPats clauses++  -- Get the declared type signature of x+  Forall sig <- lookup (name .- x) >>= maybe (throw $ NoType x) return+  -- If x isn't in the context, it's because no type was declared for it, so+  -- throw an error.+  (nms, ty) <- unbind sig++  -- Try to decompose the type into a chain of arrows like pty1 ->+  -- pty2 -> pty3 -> ... -> bodyTy, according to the number of+  -- patterns, and lazily unrolling type definitions along the way.+  (patTys, bodyTy) <- decomposeDefnTy (numPats (head clauses)) ty++  ((acs, _), theta) <- solve $ do+    aclauses <- forAll nms $ mapM (checkClause patTys bodyTy) clauses+    return (aclauses, ty)++  return $ applySubst theta (Defn (coerce x) patTys bodyTy acs)+ where+  numPats = length . fst . unsafeUnbind++  checkNumPats [] = return () -- This can't happen, but meh+  checkNumPats [_] = return ()+  checkNumPats (c : cs)+    | all ((== 0) . numPats) (c : cs) = throw (DuplicateDefns x)+    | not (all ((== numPats c) . numPats) cs) = throw NumPatterns+    -- XXX more info, this error actually means # of+    -- patterns don't match across different clauses+    | otherwise = return ()++  -- \| Check a clause of a definition against a list of pattern types and a body type.+  checkClause ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    [Type] ->+    Type ->+    Bind [Pattern] Term ->+    Sem r Clause+  checkClause patTys bodyTy clause = do+    (pats, body) <- unbind clause++    -- At this point we know that every clause has the same number of patterns,+    -- which is the same as the length of the list patTys.  So we can just use+    -- zipWithM to check all the patterns.+    (ctxs, aps) <- unzip <$> zipWithM checkPattern pats patTys+    at <- extends (mconcat ctxs) $ check body bodyTy+    return $ bind aps at++  -- Decompose a type that must be of the form t1 -> t2 -> ... -> tn -> t{n+1}.+  decomposeDefnTy :: Members '[Reader TyDefCtx, Error TCError] r => Int -> Type -> Sem r ([Type], Type)+  decomposeDefnTy 0 ty = return ([], ty)+  decomposeDefnTy n (TyUser tyName args) = lookupTyDefn tyName args >>= decomposeDefnTy n+  decomposeDefnTy n (ty1 :->: ty2) = first (ty1 :) <$> decomposeDefnTy (n - 1) ty2+  decomposeDefnTy _n _ty = throw NumPatterns++-- XXX include more info. More argument patterns than arrows in the type.++--------------------------------------------------+-- Properties++-- | Given a context mapping names to documentation, extract the+--   properties attached to each name and typecheck them.+checkProperties ::+  Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output (Message ann)] r =>+  Ctx Term Docs ->+  Sem r (Ctx ATerm [AProperty])+checkProperties docs =+  Ctx.coerceKeys . Ctx.filter (not . P.null)+    <$> (traverse . traverse) checkProperty properties+ where+  properties :: Ctx Term [Property]+  properties = fmap (\ds -> [p | DocProperty p <- ds]) docs++-- | Check the types of the terms embedded in a property.+checkProperty ::+  Members '[Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh, Output (Message ann)] r =>+  Property ->+  Sem r AProperty+checkProperty prop = do+  (at, theta) <- solve $ check prop TyProp+  -- XXX do we need to default container variables here?+  return $ applySubst theta at++------------------------------------------------------------+-- Type checking/inference+------------------------------------------------------------++--------------------------------------------------+-- Checking types/kinds+--------------------------------------------------++-- | Check that a sigma type is a valid type.  See 'checkTypeValid'.+checkPolyTyValid :: Members '[Reader TyDefCtx, Error TCError] r => PolyType -> Sem r ()+checkPolyTyValid (Forall b) = do+  let (_, ty) = unsafeUnbind b+  checkTypeValid ty++-- | Disco doesn't need kinds per se, since all types must be fully+--   applied.  But we do need to check that every type is applied to+--   the correct number of arguments.+checkTypeValid :: Members '[Reader TyDefCtx, Error TCError] r => Type -> Sem r ()+checkTypeValid (TyAtom _) = return ()+checkTypeValid (TyCon c tys) = do+  k <- conArity c+  if+    | n < k -> throw (NotEnoughArgs c)+    | n > k -> throw (TooManyArgs c)+    | otherwise -> mapM_ checkTypeValid tys+ where+  n = length tys++conArity :: Members '[Reader TyDefCtx, Error TCError] r => Con -> Sem r Int+conArity (CContainer _) = return 1+conArity CGraph = return 1+conArity (CUser name) = do+  d <- ask @TyDefCtx+  case M.lookup name d of+    Nothing -> throw (NotTyDef name)+    Just (TyDefBody as _) -> return (length as)+conArity _ = return 2 -- (->, *, +, map)++--------------------------------------------------+-- Checking modes+--------------------------------------------------++-- | Typechecking can be in one of two modes: inference mode means we+--   are trying to synthesize a valid type for a term; checking mode+--   means we are trying to show that a term has a given type.+data Mode = Infer | Check Type+  deriving (Show)++-- | Check that a term has the given type.  Either throws an error, or+--   returns the term annotated with types for all subterms.+--+--   This function is provided for convenience; it simply calls+--   'typecheck' with an appropriate 'Mode'.+check ::+  Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Term ->+  Type ->+  Sem r ATerm+check t ty = typecheck (Check ty) t++-- | Check that a term has the given polymorphic type.+checkPolyTy ::+  Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Term ->+  PolyType ->+  Sem r ATerm+checkPolyTy t (Forall sig) = do+  (as, tau) <- unbind sig+  (at, cst) <- withConstraint $ check t tau+  case as of+    [] -> constraint cst+    _ -> constraint $ CAll (bind as cst)+  return at++-- | Infer the type of a term.  If it succeeds, it returns the term+--   with all subterms annotated.+--+--   This function is provided for convenience; it simply calls+--   'typecheck' with an appropriate 'Mode'.+infer ::+  Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Term ->+  Sem r ATerm+infer = typecheck Infer++-- | Top-level type inference algorithm: infer a (polymorphic) type+--   for a term by running type inference, solving the resulting+--   constraints, and quantifying over any remaining type variables.+inferTop ::+  Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r =>+  Term ->+  Sem r (ATerm, PolyType)+inferTop t = do+  -- Run inference on the term and try to solve the resulting+  -- constraints.+  (at, theta) <- solve $ infer t++  debug "Final annotated term (before substitution and container monomorphizing):"+  debugPretty at++  -- Apply the resulting substitution.+  let at' = applySubst theta at++      -- Find any remaining container variables.+      cvs = containerVars (getType at')++      -- Replace them all with List.+      at'' = applySubst (Subst.fromList $ zip (S.toList cvs) (repeat (TyAtom (ABase CtrList)))) at'++  -- Finally, quantify over any remaining type variables and return+  -- the term along with the resulting polymorphic type.+  return (at'', closeType (getType at''))++-- | Top-level type checking algorithm: check that a term has a given+--   polymorphic type by running type checking and solving the+--   resulting constraints.+checkTop ::+  Members '[Output (Message ann), Reader TyCtx, Reader TyDefCtx, Error TCError, Fresh] r =>+  Term ->+  PolyType ->+  Sem r ATerm+checkTop t ty = do+  (at, theta) <- solve $ checkPolyTy t ty+  return $ applySubst theta at++--------------------------------------------------+-- The typecheck function+--------------------------------------------------++-- | The main workhorse of the typechecker.  Instead of having two+--   functions, one for inference and one for checking, 'typecheck'+--   takes a 'Mode'.  This cuts down on code duplication in many+--   cases, and allows all the checking and inference code related to+--   a given AST node to be placed together.+typecheck ::+  Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Mode ->+  Term ->+  Sem r ATerm+-- ~~~~ Note [Pattern coverage]+-- In several places we have clauses like+--+--   inferPrim (PrimBOp op) | op `elem` [And, Or, Impl, Iff]+--+-- since the typing rules for all the given operators are the same.+-- The only problem is that the pattern coverage checker (sensibly)+-- doesn't look at guards in general, so it thinks that there are TBin+-- cases still uncovered.+--+-- However, we *don't* just want to add a catch-all case at the end,+-- because the coverage checker is super helpful in alerting us when+-- there's a missing typechecking case after modifying the language in+-- some way. The (not ideal) solution for now is to add some+-- additional explicit cases that simply call 'error', which will+-- never be reached but which assure the coverage checker that we have+-- handled those cases.+--+-- The ideal solution would be to use or-patterns, if Haskell had them+-- (see https://github.com/ghc-proposals/ghc-proposals/pull/43).++--------------------------------------------------+-- Defined types++-- To check at a user-defined type, expand its definition and recurse.+-- This case has to be first, so in all other cases we know the type+-- will not be a TyUser.+typecheck (Check (TyUser name args)) t = lookupTyDefn name args >>= check t+--------------------------------------------------+-- Parens++-- Recurse through parens; they are not represented explicitly in the+-- resulting ATerm.+typecheck mode (TParens t) = typecheck mode t+--------------------------------------------------+-- Variables++-- Resolve variable names and infer their types.  We don't need a+-- checking case; checking the type of a variable will fall through to+-- this case.+typecheck Infer (TVar x) = do+  -- Pick the first method that succeeds; if none do, throw an unbound+  -- variable error.+  mt <- runMaybeT . F.asum . map MaybeT $ [tryLocal, tryModule, tryPrim]+  maybe (throw (Unbound x)) return mt+ where+  -- 1. See if the variable name is bound locally.+  tryLocal = do+    mty <- Ctx.lookup (localName x)+    case mty of+      Just (Forall sig) -> do+        (_, ty) <- unbind sig+        return . Just $ ATVar ty (localName (coerce x))+      Nothing -> return Nothing++  -- 2. See if the variable name is bound in some in-scope module,+  -- throwing an ambiguity error if it is bound in multiple modules.+  tryModule = do+    bs <- Ctx.lookupNonLocal x+    case bs of+      [(m, Forall sig)] -> do+        (_, ty) <- unbind sig+        return . Just $ ATVar ty (m .- coerce x)+      [] -> return Nothing+      _ -> throw $ Ambiguous x (map fst bs)++  -- 3. See if we should convert it to a primitive.+  tryPrim =+    case toPrim (name2String x) of+      (prim : _) -> Just <$> typecheck Infer (TPrim prim)+      _ -> return Nothing++--------------------------------------------------+-- Primitives++typecheck Infer (TPrim prim) = do+  ty <- inferPrim prim+  return $ ATPrim ty prim+ where+  inferPrim :: Members '[Writer Constraint, Fresh] r => Prim -> Sem r Type++  ----------------------------------------+  -- Left/right++  inferPrim PrimLeft = do+    a <- freshTy+    b <- freshTy+    return $ a :->: (a :+: b)+  inferPrim PrimRight = do+    a <- freshTy+    b <- freshTy+    return $ b :->: (a :+: b)++  ----------------------------------------+  -- Logic++  inferPrim (PrimBOp op) | op `elem` [And, Or, Impl, Iff] = do+    a <- freshTy+    constraint $ CQual (bopQual op) a+    return $ a :*: a :->: a++  -- See Note [Pattern coverage] -----------------------------+  inferPrim (PrimBOp And) = error "inferPrim And should be unreachable"+  inferPrim (PrimBOp Or) = error "inferPrim Or should be unreachable"+  inferPrim (PrimBOp Impl) = error "inferPrim Impl should be unreachable"+  inferPrim (PrimBOp Iff) = error "inferPrim Iff should be unreachable"+  ------------------------------------------------------------++  inferPrim (PrimUOp Not) = do+    a <- freshTy+    constraint $ CQual QBool a+    return $ a :->: a++  ----------------------------------------+  -- Container conversion++  inferPrim conv | conv `elem` [PrimList, PrimBag, PrimSet] = do+    c <- freshAtom -- make a unification variable for the container type+    a <- freshTy -- make a unification variable for the element type++    -- converting to a set or bag requires being able to sort the elements+    when (conv /= PrimList) $ constraint $ CQual QCmp a++    return $ TyContainer c a :->: primCtrCon conv a+   where+    primCtrCon PrimList = TyList+    primCtrCon PrimBag = TyBag+    primCtrCon _ = TySet++  -- See Note [Pattern coverage] -----------------------------+  inferPrim PrimList = error "inferPrim PrimList should be unreachable"+  inferPrim PrimBag = error "inferPrim PrimBag should be unreachable"+  inferPrim PrimSet = error "inferPrim PrimSet should be unreachable"+  ------------------------------------------------------------++  inferPrim PrimB2C = do+    a <- freshTy+    return $ TyBag a :->: TySet (a :*: TyN)+  inferPrim PrimC2B = do+    a <- freshTy+    c <- freshAtom+    constraint $ CQual QCmp a+    return $ TyContainer c (a :*: TyN) :->: TyBag a+  inferPrim PrimUC2B = do+    a <- freshTy+    c <- freshAtom+    return $ TyContainer c (a :*: TyN) :->: TyBag a+  inferPrim PrimMapToSet = do+    k <- freshTy+    v <- freshTy+    constraint $ CQual QSimple k+    return $ TyMap k v :->: TySet (k :*: v)+  inferPrim PrimSetToMap = do+    k <- freshTy+    v <- freshTy+    constraint $ CQual QSimple k+    return $ TySet (k :*: v) :->: TyMap k v+  inferPrim PrimSummary = do+    a <- freshTy+    constraint $ CQual QSimple a+    return $ TyGraph a :->: TyMap a (TySet a)+  inferPrim PrimVertex = do+    a <- freshTy+    constraint $ CQual QSimple a+    return $ a :->: TyGraph a+  inferPrim PrimEmptyGraph = do+    a <- freshTy+    constraint $ CQual QSimple a+    return $ TyGraph a+  inferPrim PrimOverlay = do+    a <- freshTy+    constraint $ CQual QSimple a+    return $ TyGraph a :*: TyGraph a :->: TyGraph a+  inferPrim PrimConnect = do+    a <- freshTy+    constraint $ CQual QSimple a+    return $ TyGraph a :*: TyGraph a :->: TyGraph a+  inferPrim PrimInsert = do+    a <- freshTy+    b <- freshTy+    constraint $ CQual QSimple a+    return $ a :*: b :*: TyMap a b :->: TyMap a b+  inferPrim PrimLookup = do+    a <- freshTy+    b <- freshTy+    constraint $ CQual QSimple a+    return $ a :*: TyMap a b :->: (TyUnit :+: b)+  ----------------------------------------+  -- Container primitives++  inferPrim (PrimBOp Cons) = do+    a <- freshTy+    return $ a :*: TyList a :->: TyList a++  -- XXX see https://github.com/disco-lang/disco/issues/160+  -- each : (a -> b) × c a -> c b+  inferPrim PrimEach = do+    c <- freshAtom+    a <- freshTy+    b <- freshTy+    return $ (a :->: b) :*: TyContainer c a :->: TyContainer c b++  -- XXX should eventually be (a * a -> a) * c a -> a,+  --   with a check that the function has the right properties.+  -- reduce : (a * a -> a) * a * c a -> a+  inferPrim PrimReduce = do+    c <- freshAtom+    a <- freshTy+    return $ (a :*: a :->: a) :*: a :*: TyContainer c a :->: a++  -- filter : (a -> Bool) × c a -> c a+  inferPrim PrimFilter = do+    c <- freshAtom+    a <- freshTy+    return $ (a :->: TyBool) :*: TyContainer c a :->: TyContainer c a++  -- join : c (c a) -> c a+  inferPrim PrimJoin = do+    c <- freshAtom+    a <- freshTy+    return $ TyContainer c (TyContainer c a) :->: TyContainer c a++  -- merge : (N × N -> N) × c a × c a -> c a   (c = bag or set)+  inferPrim PrimMerge = do+    c <- freshAtom+    a <- freshTy+    constraint $+      COr+        [ CEq (TyAtom (ABase CtrBag)) (TyAtom c)+        , CEq (TyAtom (ABase CtrSet)) (TyAtom c)+        ]+    let ca = TyContainer c a+    return $ (TyN :*: TyN :->: TyN) :*: ca :*: ca :->: ca+  inferPrim (PrimBOp CartProd) = do+    a <- freshTy+    b <- freshTy+    c <- freshAtom+    return $ TyContainer c a :*: TyContainer c b :->: TyContainer c (a :*: b)+  inferPrim (PrimBOp setOp) | setOp `elem` [Union, Inter, Diff, Subset] = do+    a <- freshTy+    c <- freshAtom+    constraint $+      COr+        [ CEq (TyAtom (ABase CtrBag)) (TyAtom c)+        , CEq (TyAtom (ABase CtrSet)) (TyAtom c)+        ]+    let ca = TyContainer c a+    let resTy = case setOp of Subset -> TyBool; _ -> ca+    return $ ca :*: ca :->: resTy++  -- See Note [Pattern coverage] -----------------------------+  inferPrim (PrimBOp Union) = error "inferPrim Union should be unreachable"+  inferPrim (PrimBOp Inter) = error "inferPrim Inter should be unreachable"+  inferPrim (PrimBOp Diff) = error "inferPrim Diff should be unreachable"+  inferPrim (PrimBOp Subset) = error "inferPrim Subset should be unreachable"+  ------------------------------------------------------------++  inferPrim (PrimBOp Elem) = do+    a <- freshTy+    c <- freshAtom++    constraint $ CQual QCmp a++    return $ a :*: TyContainer c a :->: TyBool++  ----------------------------------------+  -- Arithmetic++  inferPrim (PrimBOp IDiv) = do+    a <- freshTy+    resTy <- cInt a+    return $ a :*: a :->: resTy+  inferPrim (PrimBOp Mod) = do+    a <- freshTy+    constraint $ CSub a TyZ+    return $ a :*: a :->: a+  inferPrim (PrimBOp op) | op `elem` [Add, Mul, Sub, Div, SSub] = do+    a <- freshTy+    constraint $ CQual (bopQual op) a+    return $ a :*: a :->: a++  -- See Note [Pattern coverage] -----------------------------+  inferPrim (PrimBOp Add) = error "inferPrim Add should be unreachable"+  inferPrim (PrimBOp Mul) = error "inferPrim Mul should be unreachable"+  inferPrim (PrimBOp Sub) = error "inferPrim Sub should be unreachable"+  inferPrim (PrimBOp Div) = error "inferPrim Div should be unreachable"+  inferPrim (PrimBOp SSub) = error "inferPrim SSub should be unreachable"+  ------------------------------------------------------------++  inferPrim (PrimUOp Neg) = do+    a <- freshTy+    constraint $ CQual QSub a+    return $ a :->: a+  inferPrim (PrimBOp Exp) = do+    a <- freshTy+    b <- freshTy+    resTy <- cExp a b+    return $ a :*: b :->: resTy++  ----------------------------------------+  -- Number theory++  inferPrim PrimIsPrime = return $ TyN :->: TyBool+  inferPrim PrimFactor = return $ TyN :->: TyBag TyN+  inferPrim PrimFrac = return $ TyQ :->: (TyZ :*: TyN)+  inferPrim (PrimBOp Divides) = do+    a <- freshTy+    constraint $ CQual QNum a+    return $ a :*: a :->: TyBool++  ----------------------------------------+  -- Choose++  -- For now, a simple typing rule for multinomial coefficients that+  -- requires everything to be Nat.  However, they can be extended to+  -- handle negative or fractional arguments.+  inferPrim (PrimBOp Choose) = do+    b <- freshTy++    -- b can be either Nat (a binomial coefficient)+    -- or a list of Nat (a multinomial coefficient).+    constraint $ COr [CEq b TyN, CEq b (TyList TyN)]+    return $ TyN :*: b :->: TyN++  ----------------------------------------+  -- Ellipses++  -- Actually 'until' supports more types than this, e.g. Q instead+  -- of N, but this is good enough.  This case is here just for+  -- completeness---in case someone enables primitives and uses it+  -- directly---but typically 'until' is generated only during+  -- desugaring of a container with ellipsis, after typechecking, in+  -- which case it can be assigned a more appropriate type directly.++  inferPrim PrimUntil = return $ TyN :*: TyList TyN :->: TyList TyN+  ----------------------------------------+  -- Crash++  inferPrim PrimCrash = do+    a <- freshTy+    return $ TyString :->: a++  ----------------------------------------+  -- Propositions++  -- 'holds' converts a Prop into a Bool (but might not terminate).+  inferPrim PrimHolds = return $ TyProp :->: TyBool+  -- An equality assertion =!= is just like a comparison ==, except+  -- the result is a Prop.+  inferPrim (PrimBOp ShouldEq) = do+    ty <- freshTy+    constraint $ CQual QCmp ty+    return $ ty :*: ty :->: TyProp+  inferPrim (PrimBOp ShouldLt) = do+    ty <- freshTy+    constraint $ CQual QCmp ty+    return $ ty :*: ty :->: TyProp++  ----------------------------------------+  -- Comparisons++  -- Infer the type of a comparison. A comparison always has type+  -- Bool, but we have to make sure the subterms have compatible+  -- types.  We also generate a QCmp qualifier, for two reasons:+  -- one, we need to know whether e.g. a comparison was done at a+  -- certain type, so we can decide whether the type is allowed to+  -- be completely polymorphic or not.  Also, comparison of Props is+  -- not allowed.+  inferPrim (PrimBOp op) | op `elem` [Eq, Neq, Lt, Gt, Leq, Geq] = do+    ty <- freshTy+    constraint $ CQual QCmp ty+    return $ ty :*: ty :->: TyBool++  -- See Note [Pattern coverage] -----------------------------+  inferPrim (PrimBOp Eq) = error "inferPrim Eq should be unreachable"+  inferPrim (PrimBOp Neq) = error "inferPrim Neq should be unreachable"+  inferPrim (PrimBOp Lt) = error "inferPrim Lt should be unreachable"+  inferPrim (PrimBOp Gt) = error "inferPrim Gt should be unreachable"+  inferPrim (PrimBOp Leq) = error "inferPrim Leq should be unreachable"+  inferPrim (PrimBOp Geq) = error "inferPrim Geq should be unreachable"+  ------------------------------------------------------------++  inferPrim (PrimBOp op) | op `elem` [Min, Max] = do+    ty <- freshTy+    constraint $ CQual QCmp ty+    return $ ty :*: ty :->: ty++  -- See Note [Pattern coverage] -----------------------------+  inferPrim (PrimBOp Min) = error "inferPrim Min should be unreachable"+  inferPrim (PrimBOp Max) = error "inferPrim Max should be unreachable"+  ------------------------------------------------------------++  ----------------------------------------+  -- Special arithmetic functions: fact, sqrt, floor, ceil, abs++  inferPrim (PrimUOp Fact) = return $ TyN :->: TyN+  inferPrim PrimSqrt = return $ TyN :->: TyN+  inferPrim p | p `elem` [PrimFloor, PrimCeil] = do+    argTy <- freshTy+    resTy <- cInt argTy+    return $ argTy :->: resTy++  -- See Note [Pattern coverage] -----------------------------+  inferPrim PrimFloor = error "inferPrim Floor should be unreachable"+  inferPrim PrimCeil = error "inferPrim Ceil should be unreachable"+  ------------------------------------------------------------++  inferPrim PrimAbs = do+    argTy <- freshTy+    resTy <- freshTy+    cAbs argTy resTy `cOr` cSize argTy resTy+    return $ argTy :->: resTy++  ----------------------------------------+  -- power set/bag++  inferPrim PrimPower = do+    a <- freshTy+    c <- freshAtom++    constraint $ CQual QCmp a+    constraint $+      COr+        [ CEq (TyAtom (ABase CtrSet)) (TyAtom c)+        , CEq (TyAtom (ABase CtrBag)) (TyAtom c)+        ]++    return $ TyContainer c a :->: TyContainer c (TyContainer c a)+  inferPrim PrimLookupSeq = return $ TyList TyN :->: (TyUnit :+: TyString)+  inferPrim PrimExtendSeq = return $ TyList TyN :->: TyList TyN++--------------------------------------------------+-- Base types++-- A few trivial cases for base types.+typecheck Infer TUnit = return ATUnit+typecheck Infer (TBool b) = return $ ATBool TyBool b+typecheck Infer (TChar c) = return $ ATChar c+typecheck Infer (TString cs) = return $ ATString cs+-- typecheck (Check (TyFin n)) (TNat x)     = return $ ATNat (TyFin n) x+typecheck Infer (TNat n) = return $ ATNat TyN n+typecheck Infer (TRat r) = return $ ATRat r+typecheck _ TWild = throw NoTWild+--------------------------------------------------+-- Abstractions (lambdas and quantifiers)++-- Lambdas and quantifiers are similar enough that we can share a+-- bunch of the code, but their typing rules are a bit different.  In+-- particular a lambda+--+--   \(x1:ty1), (x2:ty2) ... . body+--+-- is going to have a type like ty1 -> ty2 -> ... -> resTy, whereas a+-- quantifier like+--+--   ∃(x1:ty1), (x2:ty2) ... . body+--+-- is just going to have the type Prop.  The similarity is that in+-- both cases we have to generate unification variables for any+-- binders with omitted type annotations, and typecheck the body under+-- an extended context.++-- It's only helpful to do lambdas in checking mode, since the+-- provided function type can provide information about the types of+-- the arguments.  For other quantifiers we can just fall back to+-- inference mode.+typecheck (Check checkTy) tm@(TAbs Lam body) = do+  (args, t) <- unbind body++  -- First check that the given type is of the form ty1 -> ty2 ->+  -- ... -> resTy, where the types ty1, ty2 ... match up with any+  -- types declared for the arguments to the lambda (e.g.  (x:tyA)+  -- (y:tyB) -> ...).+  (ctx, typedArgs, resTy) <- checkArgs args checkTy tm++  -- Then check the type of the body under a context extended with+  -- types for all the arguments.+  extends ctx $+    ATAbs Lam checkTy <$> (bind (coerce typedArgs) <$> check t resTy)+ where+  -- Given the patterns and their optional type annotations in the+  -- head of a lambda (e.g.  @x (y:Z) (f : N -> N) -> ...@), and the+  -- type at which we are checking the lambda, ensure that:+  --+  --   - The type is of the form @ty1 -> ty2 -> ... -> resTy@ and+  --     there are enough @ty1@, @ty2@, ... to match all the arguments.+  --   - Each pattern successfully checks at its corresponding type.+  --+  -- If it succeeds, return a context binding variables to their+  -- types (as determined by the patterns and the input types) which+  -- we can use to extend when checking the body, a list of the typed+  -- patterns, and the result type of the function.+  checkArgs ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    [Pattern] ->+    Type ->+    Term ->+    Sem r (TyCtx, [APattern], Type)++  -- If we're all out of arguments, the remaining checking type is the+  -- result, and there are no variables to bind in the context.+  checkArgs [] ty _ = return (emptyCtx, [], ty)+  -- Take the next pattern and its annotation; the checking type must+  -- be a function type ty1 -> ty2.+  checkArgs (p : args) ty term = do+    -- Ensure that ty is a function type+    (ty1, ty2) <- ensureConstr2 CArr ty (Left term)++    -- Check the argument pattern against the function domain.+    (pCtx, pTyped) <- checkPattern p ty1++    -- Check the rest of the arguments under the type ty2, returning a+    -- context with the rest of the arguments and the final result type.+    (ctx, typedArgs, resTy) <- checkArgs args ty2 term++    -- Pass the result type through, and put the pattern-bound variables+    -- in the returned context.+    return (pCtx <> ctx, pTyped : typedArgs, resTy)++-- In inference mode, we handle lambdas as well as quantifiers (∀, ∃).+typecheck Infer (TAbs q lam) = do+  -- Open it and get the argument patterns with any type annotations.+  (args, t) <- unbind lam++  -- Replace any missing type annotations with fresh type variables,+  -- and check each pattern at that variable to refine them, collecting+  -- the types of each pattern's bound variables in a context.+  tys <- mapM getAscrOrFresh args+  (pCtxs, typedPats) <- unzip <$> zipWithM checkPattern args tys++  -- In the case of ∀, ∃, have to ensure that the argument types are+  -- searchable.+  when (q `elem` [All, Ex]) $+    -- What's the difference between this and `tys`? Nothing, after+    -- the solver runs, but right now the patterns might have a+    -- concrete type from annotations inside tuples.+    forM_ (map getType typedPats) $ \ty ->+      unless (isSearchable ty) $+        throw $+          NoSearch ty++  -- Extend the context with the given arguments, and then do+  -- something appropriate depending on the quantifier.+  extends (mconcat pCtxs) $ do+    case q of+      -- For lambdas, infer the type of the body, and return an appropriate+      -- function type.+      Lam -> do+        at <- infer t+        return $ ATAbs Lam (mkFunTy tys (getType at)) (bind typedPats at)++      -- For other quantifiers, check that the body has type Prop,+      -- and return Prop.+      _ -> do+        -- ∀, ∃+        at <- check t TyProp+        return $ ATAbs q TyProp (bind typedPats at)+ where+  getAscrOrFresh ::+    Members '[Reader TyDefCtx, Error TCError, Fresh] r =>+    Pattern ->+    Sem r Type+  getAscrOrFresh (PAscr _ ty) = checkTypeValid ty >> pure ty+  getAscrOrFresh _ = freshTy++  -- mkFunTy [ty1, ..., tyn] out = ty1 -> (ty2 -> ... (tyn -> out))+  mkFunTy :: [Type] -> Type -> Type+  mkFunTy tys out = foldr (:->:) out tys++--------------------------------------------------+-- Application++-- Infer the type of a function application by inferring the function+-- type and then checking the argument type.  We don't need a checking+-- case because checking mode doesn't help.+typecheck Infer (TApp t t') = do+  at <- infer t+  let ty = getType at+  (ty1, ty2) <- ensureConstr2 CArr ty (Left t)+  ATApp ty2 at <$> check t' ty1++--------------------------------------------------+-- Tuples++-- Check/infer the type of a tuple.+typecheck mode1 (TTup tup) = uncurry ATTup <$> typecheckTuple mode1 tup+ where+  typecheckTuple ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Mode ->+    [Term] ->+    Sem r (Type, [ATerm])+  typecheckTuple _ [] = error "Impossible! typecheckTuple []"+  typecheckTuple mode [t] = (getType &&& (: [])) <$> typecheck mode t+  typecheckTuple mode (t : ts) = do+    (m, ms) <- ensureConstrMode2 CProd mode (Left $ TTup (t : ts))+    at <- typecheck m t+    (ty, ats) <- typecheckTuple ms ts+    return (getType at :*: ty, at : ats)++----------------------------------------+-- Comparison chain++typecheck Infer (TChain t ls) =+  ATChain TyBool <$> infer t <*> inferChain t ls+ where+  inferChain ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Term ->+    [Link] ->+    Sem r [ALink]+  inferChain _ [] = return []+  inferChain t1 (TLink op t2 : links) = do+    at2 <- infer t2+    _ <- check (TBin op t1 t2) TyBool+    atl <- inferChain t2 links+    return $ ATLink op at2 : atl++----------------------------------------+-- Type operations++typecheck Infer (TTyOp Enumerate t) = do+  checkTypeValid t+  return $ ATTyOp (TyList t) Enumerate t+typecheck Infer (TTyOp Count t) = do+  checkTypeValid t+  return $ ATTyOp (TyUnit :+: TyN) Count t++--------------------------------------------------+-- Containers++-- Literal containers, including ellipses+typecheck mode t@(TContainer c xs ell) = do+  eltMode <- ensureConstrMode1 (containerToCon c) mode (Left t)+  axns <- mapM (\(x, n) -> (,) <$> typecheck eltMode x <*> traverse (`check` TyN) n) xs+  aell <- typecheckEllipsis eltMode ell+  resTy <- case mode of+    Infer -> do+      let tys = [getType at | Just (Until at) <- [aell]] ++ map (getType . fst) axns+      tyv <- freshTy+      constraints $ map (`CSub` tyv) tys+      return $ containerTy c tyv+    Check ty -> return ty+  eltTy <- getEltTy c resTy++  -- See Note [Container literal constraints]+  when (c /= ListContainer && not (P.null xs)) $ constraint $ CQual QCmp eltTy+  when (isJust ell) $ constraint $ CQual QEnum eltTy+  return $ ATContainer resTy c axns aell+ where+  typecheckEllipsis ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Mode ->+    Maybe (Ellipsis Term) ->+    Sem r (Maybe (Ellipsis ATerm))+  typecheckEllipsis _ Nothing = return Nothing+  typecheckEllipsis m (Just (Until tm)) = Just . Until <$> typecheck m tm++-- ~~~~ Note [Container literal constraints]+--+-- It should only be possible to construct something of type Set(a) or+-- Bag(a) when a is comparable, so we can normalize the set or bag+-- value.  For example, Set(N) is OK, but Set(N -> N) is not.  On the+-- other hand, List(a) is fine for any type a.  We want to maintain+-- the invariant that we can only actually obtain a value of type+-- Set(a) or Bag(a) if a is comparable.  This means we will be able to+-- write polymorphic functions that take bags or sets as input without+-- having to specify any constraints --- the only way to call such+-- functions is with element types that actually support comparison.+-- For example, 'unions' can simply have the type Set(Set(a)) ->+-- Set(a).+--+-- Hence, container literals (along with the 'set' and 'bag'+-- conversion functions) serve as "gatekeepers" to make sure we can+-- only construct containers with appropriate element types.  So when+-- we see a container literal, if it is a bag or set literal, we have+-- to introduce an additional QCmp constraint for the element type.+--+-- But not so fast --- with that rule, 'unions' does not type check!+-- To see why, look at the definition:+--+--   unions(ss) = foldr(~∪~, {}, list(ss))+--+-- The empty set literal in the definition means we end up generating+-- a QCmp constraint on the element type anyway.  But there is a+-- solution: we refine our invariant to say that we can only+-- actually obtain a *non-empty* value of type Set(a) or Bag(a) if a+-- is comparable.  Empty bags and sets are allowed to have any element+-- type.  This is safe because there is no way to generate a non-empty+-- set from an empty one, without also making use of something like a+-- non-empty set literal or conversion function.  So we add a special+-- case to the rule that says we only add a QCmp constraint in the+-- case of a *non-empty* set or bag literal.  Now the definition of+-- 'unions' type checks perfectly well.++-- Container comprehensions+typecheck mode tcc@(TContainerComp c bqt) = do+  eltMode <- ensureConstrMode1 (containerToCon c) mode (Left tcc)+  (qs, t) <- unbind bqt+  (aqs, cx) <- inferTelescope inferQual qs+  extends cx $ do+    at <- typecheck eltMode t+    let resTy = case mode of+          Infer -> containerTy c (getType at)+          Check ty -> ty+    return $ ATContainerComp resTy c (bind aqs at)+ where+  inferQual ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Qual ->+    Sem r (AQual, TyCtx)+  inferQual (QBind x (unembed -> t)) = do+    at <- infer t+    ty <- ensureConstr1 (containerToCon c) (getType at) (Left t)+    return (AQBind (coerce x) (embed at), singleCtx (localName x) (toPolyType ty))+  inferQual (QGuard (unembed -> t)) = do+    at <- check t TyBool+    return (AQGuard (embed at), emptyCtx)++--------------------------------------------------+-- Let++-- To check/infer a let expression.  Note let is non-recursive.+typecheck mode (TLet l) = do+  (bs, t2) <- unbind l++  -- Infer the types of all the variables bound by the let...+  (as, ctx) <- inferTelescope inferBinding bs++  -- ...then check/infer the body under an extended context.+  extends ctx $ do+    at2 <- typecheck mode t2+    return $ ATLet (getType at2) (bind as at2)+ where+  -- Infer the type of a binding (@x [: ty] = t@), returning a+  -- type-annotated binding along with a (singleton) context for the+  -- bound variable.  The optional type annotation on the variable+  -- determines whether we use inference or checking mode for the+  -- body.+  inferBinding ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Binding ->+    Sem r (ABinding, TyCtx)+  inferBinding (Binding mty x (unembed -> t)) = do+    at <- case mty of+      Just (unembed -> ty) -> checkPolyTy t ty+      Nothing -> infer t+    return (ABinding mty (coerce x) (embed at), singleCtx (localName x) (toPolyType $ getType at))++--------------------------------------------------+-- Case++-- Check/infer a case expression.+typecheck _ (TCase []) = throw EmptyCase+typecheck mode (TCase bs) = do+  bs' <- mapM typecheckBranch bs+  resTy <- case mode of+    Check ty -> return ty+    Infer -> do+      x <- freshTy+      constraints $ map ((`CSub` x) . getType) bs'+      return x+  return $ ATCase resTy bs'+ where+  typecheckBranch ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Branch ->+    Sem r ABranch+  typecheckBranch b = do+    (gs, t) <- unbind b+    (ags, ctx) <- inferTelescope inferGuard gs+    extends ctx $+      bind ags <$> typecheck mode t++  -- Infer the type of a guard, returning the type-annotated guard+  -- along with a context of types for any variables bound by the+  -- guard.+  inferGuard ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    Guard ->+    Sem r (AGuard, TyCtx)+  inferGuard (GBool (unembed -> t)) = do+    at <- check t TyBool+    return (AGBool (embed at), emptyCtx)+  inferGuard (GPat (unembed -> t) p) = do+    at <- infer t+    (ctx, apt) <- checkPattern p (getType at)+    return (AGPat (embed at) apt, ctx)+  inferGuard (GLet (Binding mty x (unembed -> t))) = do+    at <- case mty of+      Just (unembed -> ty) -> checkPolyTy t ty+      Nothing -> infer t+    return+      ( AGLet (ABinding mty (coerce x) (embed at))+      , singleCtx (localName x) (toPolyType (getType at))+      )++--------------------------------------------------+-- Type ascription++-- Ascriptions are what let us flip from inference mode into+-- checking mode.+typecheck Infer (TAscr t ty) = checkPolyTyValid ty >> checkPolyTy t ty+--------------------------------------------------+-- Inference fallback++-- Finally, to check anything else, we can fall back to inferring its+-- type and then check that the inferred type is a *subtype* of the+-- given type.  We have to be careful to call 'setType' to change the+-- type at the root of the term to the requested type.+typecheck (Check ty) t = do+  at <- infer t+  constraint $ CSub (getType at) ty+  return $ setType ty at++------------------------------------------------------------+-- Patterns+------------------------------------------------------------++-- | Check that a pattern has the given type, and return a context of+--   pattern variables bound in the pattern along with their types.+checkPattern ::+  Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Pattern ->+  Type ->+  Sem r (TyCtx, APattern)+checkPattern (PNonlinear p x) _ = throw $ NonlinearPattern p x+checkPattern p (TyUser name args) = lookupTyDefn name args >>= checkPattern p+checkPattern (PVar x) ty = return (singleCtx (localName x) (toPolyType ty), APVar ty (coerce x))+checkPattern PWild ty = return (emptyCtx, APWild ty)+checkPattern (PAscr p ty1) ty2 = do+  -- We have a pattern that promises to match ty1 and someone is asking+  -- us if it can also match ty2. So we just have to ensure what we're+  -- being asked for is a subtype of what we can promise to cover...+  constraint $ CSub ty2 ty1+  -- ... and then make sure the pattern can actually match what it promised to.+  checkPattern p ty1+checkPattern PUnit ty = do+  ensureEq ty TyUnit+  return (emptyCtx, APUnit)+checkPattern (PBool b) ty = do+  ensureEq ty TyBool+  return (emptyCtx, APBool b)+checkPattern (PChar c) ty = do+  ensureEq ty TyC+  return (emptyCtx, APChar c)+checkPattern (PString s) ty = do+  ensureEq ty TyString+  return (emptyCtx, APString s)+checkPattern (PTup tup) tupTy = do+  listCtxtAps <- checkTuplePat tup tupTy+  let (ctxs, aps) = unzip listCtxtAps+  return (mconcat ctxs, APTup (foldr1 (:*:) (map getType aps)) aps)+ where+  checkTuplePat ::+    Members '[Reader TyCtx, Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+    [Pattern] ->+    Type ->+    Sem r [(TyCtx, APattern)]+  checkTuplePat [] _ = error "Impossible! checkTuplePat []"+  checkTuplePat [p] ty = do+    -- (:[]) <$> check t ty+    (ctx, apt) <- checkPattern p ty+    return [(ctx, apt)]+  checkTuplePat (p : ps) ty = do+    (ty1, ty2) <- ensureConstr2 CProd ty (Right $ PTup (p : ps))+    (ctx, apt) <- checkPattern p ty1+    rest <- checkTuplePat ps ty2+    return ((ctx, apt) : rest)+checkPattern p@(PInj L pat) ty = do+  (ty1, ty2) <- ensureConstr2 CSum ty (Right p)+  (ctx, apt) <- checkPattern pat ty1+  return (ctx, APInj (ty1 :+: ty2) L apt)+checkPattern p@(PInj R pat) ty = do+  (ty1, ty2) <- ensureConstr2 CSum ty (Right p)+  (ctx, apt) <- checkPattern pat ty2+  return (ctx, APInj (ty1 :+: ty2) R apt)++-- we can match any supertype of TyN against a Nat pattern, OR+-- any TyFin.++-- XXX this isn't quite right, what if we're checking at a type+-- variable but we need to solve it to be a TyFin?  Can this ever+-- happen?  We would need a COr, except we can't express the+-- constraint "exists m. ty = TyFin m"+--+-- Yes, this can happen, and here's an example:+--+--   > (\x. {? true when x is 3, false otherwise ?}) (2 : Z5)+--   Unsolvable NoUnify+--   > (\(x : Z5). {? true when x is 3, false otherwise ?}) (2 : Z5)+--   false++-- checkPattern (PNat n) (TyFin m) = return (emptyCtx, APNat (TyFin m) n)+checkPattern (PNat n) ty = do+  constraint $ CSub TyN ty+  return (emptyCtx, APNat ty n)+checkPattern p@(PCons p1 p2) ty = do+  tyl <- ensureConstr1 CList ty (Right p)+  (ctx1, ap1) <- checkPattern p1 tyl+  (ctx2, ap2) <- checkPattern p2 (TyList tyl)+  return (ctx1 <> ctx2, APCons (TyList tyl) ap1 ap2)+checkPattern p@(PList ps) ty = do+  tyl <- ensureConstr1 CList ty (Right p)+  listCtxtAps <- mapM (`checkPattern` tyl) ps+  let (ctxs, aps) = unzip listCtxtAps+  return (mconcat ctxs, APList (TyList tyl) aps)+checkPattern (PAdd s p t) ty = do+  constraint $ CQual QNum ty+  (ctx, apt) <- checkPattern p ty+  at <- check t ty+  return (ctx, APAdd ty s apt at)+checkPattern (PMul s p t) ty = do+  constraint $ CQual QNum ty+  (ctx, apt) <- checkPattern p ty+  at <- check t ty+  return (ctx, APMul ty s apt at)+checkPattern (PSub p t) ty = do+  constraint $ CQual QNum ty+  (ctx, apt) <- checkPattern p ty+  at <- check t ty+  return (ctx, APSub ty apt at)+checkPattern (PNeg p) ty = do+  constraint $ CQual QSub ty+  tyInner <- cPos ty+  (ctx, apt) <- checkPattern p tyInner+  return (ctx, APNeg ty apt)+checkPattern (PFrac p q) ty = do+  constraint $ CQual QDiv ty+  tyP <- cInt ty+  tyQ <- cPos tyP+  (ctx1, ap1) <- checkPattern p tyP+  (ctx2, ap2) <- checkPattern q tyQ+  return (ctx1 <> ctx2, APFrac ty ap1 ap2)++------------------------------------------------------------+-- Constraints for abs, floor/ceiling/idiv, and exp+------------------------------------------------------------++-- | Constraints needed on a function type for it to be the type of+--   the absolute value function.+cAbs :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r ()+cAbs argTy resTy = do+  resTy' <- cPos argTy+  constraint $ CEq resTy resTy'++-- | Constraints needed on a function type for it to be the type of+--   the container size operation.+cSize :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r ()+cSize argTy resTy = do+  a <- freshTy+  c <- freshAtom+  constraint $ CEq (TyContainer c a) argTy+  constraint $ CEq TyN resTy++-- | Given an input type @ty@, return a type which represents the+--   output type of the absolute value function, and generate+--   appropriate constraints.+cPos :: Members '[Writer Constraint, Fresh] r => Type -> Sem r Type+cPos ty = do+  constraint $ CQual QNum ty -- The input type has to be numeric.+  case ty of+    -- If the input type is a concrete base type, we can just+    -- compute the correct output type.+    TyAtom (ABase b) -> return $ TyAtom (ABase (pos b))+    -- Otherwise, generate a fresh type variable for the output type+    -- along with some constraints.+    _ -> do+      res <- freshTy++      -- Valid types for absolute value are Z -> N, Q -> F, or T -> T+      -- (e.g. Z5 -> Z5).+      constraint $+        COr+          [ cAnd [CSub ty TyZ, CSub TyN res]+          , cAnd [CSub ty TyQ, CSub TyF res]+          , CEq ty res+          ]+      return res+ where+  pos Z = N+  pos Q = F+  pos t = t++-- | Given an input type @ty@, return a type which represents the+--   output type of the floor or ceiling functions, and generate+--   appropriate constraints.+cInt :: Members '[Writer Constraint, Fresh] r => Type -> Sem r Type+cInt ty = do+  constraint $ CQual QNum ty+  case ty of+    -- If the input type is a concrete base type, we can just+    -- compute the correct output type.+    TyAtom (ABase b) -> return $ TyAtom (ABase (int b))+    -- Otherwise, generate a fresh type variable for the output type+    -- along with some constraints.+    _ -> do+      res <- freshTy++      -- Valid types for absolute value are F -> N, Q -> Z, or T -> T+      -- (e.g. Z5 -> Z5).+      constraint $+        COr+          [ cAnd [CSub ty TyF, CSub TyN res]+          , cAnd [CSub ty TyQ, CSub TyZ res]+          , CEq ty res+          ]+      return res+ where+  int F = N+  int Q = Z+  int t = t++-- | Given input types to the exponentiation operator, return a type+--   which represents the output type, and generate appropriate+--   constraints.+cExp :: Members '[Writer Constraint, Fresh] r => Type -> Type -> Sem r Type+cExp ty1 TyN = do+  constraint $ CQual QNum ty1+  return ty1++-- We could include a special case for TyZ, but for that we would need+-- a function to find a supertype of a given type that satisfies QDiv.++cExp ty1 ty2 = do+  -- Create a fresh type variable to represent the result type.  The+  -- base type has to be a subtype.+  resTy <- freshTy+  constraint $ CSub ty1 resTy++  -- Either the exponent type is N, in which case the result type has+  -- to support multiplication, or else the exponent is Z, in which+  -- case the result type also has to support division.+  constraint $+    COr+      [ cAnd [CQual QNum resTy, CEq ty2 TyN]+      , cAnd [CQual QDiv resTy, CEq ty2 TyZ]+      ]+  return resTy++------------------------------------------------------------+-- Decomposing type constructors+------------------------------------------------------------++-- | Get the argument (element) type of a (known) container type.  Returns a+--   fresh variable with a suitable constraint if the given type is+--   not literally a container type.+getEltTy :: Members '[Writer Constraint, Fresh] r => Container -> Type -> Sem r Type+getEltTy _ (TyContainer _ e) = return e+getEltTy c ty = do+  eltTy <- freshTy+  constraint $ CEq (containerTy c eltTy) ty+  return eltTy++-- | Ensure that a type's outermost constructor matches the provided+--   constructor, returning the types within the matched constructor+--   or throwing a type error.  If the type provided is a type+--   variable, appropriate constraints are generated to guarantee the+--   type variable's outermost constructor matches the provided+--   constructor, and a list of fresh type variables is returned whose+--   count matches the arity of the provided constructor.+ensureConstr ::+  forall r.+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Type ->+  Either Term Pattern ->+  Sem r [Type]+ensureConstr c ty targ = matchConTy c ty+ where+  matchConTy :: Con -> Type -> Sem r [Type]++  -- expand type definitions lazily+  matchConTy c1 (TyUser name args) = lookupTyDefn name args >>= matchConTy c1+  matchConTy c1 (TyCon c2 tys) = do+    matchCon c1 c2+    return tys+  matchConTy c1 tyv@(TyAtom (AVar (U _))) = do+    tyvs <- mapM (const freshTy) (arity c1)+    constraint $ CEq tyv (TyCon c1 tyvs)+    return tyvs+  matchConTy _ _ = matchError++  -- \| Check whether two constructors match, which could include+  --   unifying container variables if we are matching two container+  --   types; otherwise, simply ensure that the constructors are+  --   equal.  Throw a 'matchError' if they do not match.+  matchCon :: Con -> Con -> Sem r ()+  matchCon c1 c2 | c1 == c2 = return ()+  matchCon (CContainer v@(AVar (U _))) (CContainer ctr2) =+    constraint $ CEq (TyAtom v) (TyAtom ctr2)+  matchCon (CContainer ctr1) (CContainer v@(AVar (U _))) =+    constraint $ CEq (TyAtom ctr1) (TyAtom v)+  matchCon _ _ = matchError++  matchError :: Sem r a+  matchError = case targ of+    Left term -> throw (NotCon c term ty)+    Right pat -> throw (PatternType c pat ty)++-- | A variant of ensureConstr that expects to get exactly one+--   argument type out, and throws an error if we get any other+--   number.+ensureConstr1 ::+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Type ->+  Either Term Pattern ->+  Sem r Type+ensureConstr1 c ty targ = do+  tys <- ensureConstr c ty targ+  case tys of+    [ty1] -> return ty1+    _ ->+      error $+        "Impossible! Wrong number of arg types in ensureConstr1 "+          ++ show c+          ++ " "+          ++ show ty+          ++ ": "+          ++ show tys++-- | A variant of ensureConstr that expects to get exactly two+--   argument types out, and throws an error if we get any other+--   number.+ensureConstr2 ::+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Type ->+  Either Term Pattern ->+  Sem r (Type, Type)+ensureConstr2 c ty targ = do+  tys <- ensureConstr c ty targ+  case tys of+    [ty1, ty2] -> return (ty1, ty2)+    _ ->+      error $+        "Impossible! Wrong number of arg types in ensureConstr2 "+          ++ show c+          ++ " "+          ++ show ty+          ++ ": "+          ++ show tys++-- | A variant of 'ensureConstr' that works on 'Mode's instead of+--   'Type's.  Behaves similarly to 'ensureConstr' if the 'Mode' is+--   'Check'; otherwise it generates an appropriate number of copies+--   of 'Infer'.+ensureConstrMode ::+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Mode ->+  Either Term Pattern ->+  Sem r [Mode]+ensureConstrMode c Infer _ = return $ map (const Infer) (arity c)+ensureConstrMode c (Check ty) tp = map Check <$> ensureConstr c ty tp++-- | A variant of 'ensureConstrMode' that expects to get a single+--   'Mode' and throws an error if it encounters any other number.+ensureConstrMode1 ::+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Mode ->+  Either Term Pattern ->+  Sem r Mode+ensureConstrMode1 c m targ = do+  ms <- ensureConstrMode c m targ+  case ms of+    [m1] -> return m1+    _ ->+      error $+        "Impossible! Wrong number of arg types in ensureConstrMode1 "+          ++ show c+          ++ " "+          ++ show m+          ++ ": "+          ++ show ms++-- | A variant of 'ensureConstrMode' that expects to get two 'Mode's+--   and throws an error if it encounters any other number.+ensureConstrMode2 ::+  Members '[Reader TyDefCtx, Writer Constraint, Error TCError, Fresh] r =>+  Con ->+  Mode ->+  Either Term Pattern ->+  Sem r (Mode, Mode)+ensureConstrMode2 c m targ = do+  ms <- ensureConstrMode c m targ+  case ms of+    [m1, m2] -> return (m1, m2)+    _ ->+      error $+        "Impossible! Wrong number of arg types in ensureConstrMode2 "+          ++ show c+          ++ " "+          ++ show m+          ++ ": "+          ++ show ms++-- | Ensure that two types are equal:+--     1. Do nothing if they are literally equal+--     2. Generate an equality constraint otherwise+ensureEq :: Member (Writer Constraint) r => Type -> Type -> Sem r ()+ensureEq ty1 ty2+  | ty1 == ty2 = return ()+  | otherwise = constraint $ CEq ty1 ty2
src/Disco/Typecheck/Constraints.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE OverloadedStrings #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Typecheck.Constraints -- Copyright   :  disco team and contributors@@ -10,73 +13,69 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Constraints generated by type inference & checking.-----------------------------------------------------------------------------------module Disco.Typecheck.Constraints-  ( Constraint(..)-  , cAnd-  )-  where+module Disco.Typecheck.Constraints (+  Constraint (..),+  cAnd,+)+where -import qualified Data.List.NonEmpty               as NE-import           Data.Semigroup-import           GHC.Generics                     (Generic)-import           Unbound.Generics.LocallyNameless hiding (lunbind)+import qualified Data.List.NonEmpty as NE+import Data.Semigroup+import GHC.Generics (Generic)+import Unbound.Generics.LocallyNameless hiding (lunbind) -import           Disco.Effects.LFresh+import Disco.Effects.LFresh -import           Disco.Pretty                     hiding ((<>))-import           Disco.Syntax.Operators           (BFixity (In, InL, InR))-import           Disco.Types-import           Disco.Types.Rules+import Disco.Pretty hiding ((<>))+import Disco.Syntax.Operators (BFixity (In, InL, InR))+import Disco.Types+import Disco.Types.Rules  -- | Constraints are generated as a result of type inference and checking. --   These constraints are accumulated during the inference and checking phase --   and are subsequently solved by the constraint solver. data Constraint where-  CSub   :: Type -> Type -> Constraint-  CEq    :: Type -> Type -> Constraint-  CQual  :: Qualifier -> Type -> Constraint-  CAnd   :: [Constraint] -> Constraint-  CTrue  :: Constraint-  COr    :: [Constraint] -> Constraint-  CAll  :: Bind [Name Type] Constraint -> Constraint-+  CSub :: Type -> Type -> Constraint+  CEq :: Type -> Type -> Constraint+  CQual :: Qualifier -> Type -> Constraint+  CAnd :: [Constraint] -> Constraint+  CTrue :: Constraint+  COr :: [Constraint] -> Constraint+  CAll :: Bind [Name Type] Constraint -> Constraint   deriving (Show, Generic, Alpha, Subst Type)  instance Pretty Constraint where   pretty = \case-    CSub ty1 ty2  -> withPA (PA 4 In) $ lt (pretty ty1) <+> "<:" <+> rt (pretty ty2)-    CEq ty1 ty2   -> withPA (PA 4 In) $ lt (pretty ty1) <+> "=" <+> rt (pretty ty2)-    CQual q ty    -> withPA (PA 10 InL) $ lt (pretty q) <+> rt (pretty ty)-    CAnd [c]      -> pretty c-      -- Use rt for both, since we don't need to print parens for /\ at all-    CAnd (c:cs)   -> withPA (PA 3 InR) $ rt (pretty c) <+> "/\\" <+> rt (pretty (CAnd cs))-    CAnd []       -> "True"-    CTrue         -> "True"-    COr [c]       -> pretty c-    COr (c:cs)    -> withPA (PA 2 InR) $ lt (pretty c) <+> "\\/" <+> rt (pretty (COr cs))-    COr []        -> "False"-    CAll b        -> lunbind b $ \(xs, c) ->+    CSub ty1 ty2 -> withPA (PA 4 In) $ lt (pretty ty1) <+> "<:" <+> rt (pretty ty2)+    CEq ty1 ty2 -> withPA (PA 4 In) $ lt (pretty ty1) <+> "=" <+> rt (pretty ty2)+    CQual q ty -> withPA (PA 10 InL) $ lt (pretty q) <+> rt (pretty ty)+    CAnd [c] -> pretty c+    -- Use rt for both, since we don't need to print parens for /\ at all+    CAnd (c : cs) -> withPA (PA 3 InR) $ rt (pretty c) <+> "/\\" <+> rt (pretty (CAnd cs))+    CAnd [] -> "True"+    CTrue -> "True"+    COr [c] -> pretty c+    COr (c : cs) -> withPA (PA 2 InR) $ lt (pretty c) <+> "\\/" <+> rt (pretty (COr cs))+    COr [] -> "False"+    CAll b -> lunbind b $ \(xs, c) ->       "∀" <+> intercalate "," (map pretty xs) <> "." <+> pretty c  -- A helper function for creating a single constraint from a list of constraints. cAnd :: [Constraint] -> Constraint cAnd cs = case filter nontrivial cs of-  []  -> CTrue+  [] -> CTrue   [c] -> c   cs' -> CAnd cs'-  where-    nontrivial CTrue = False-    nontrivial _     = True+ where+  nontrivial CTrue = False+  nontrivial _ = True  instance Semigroup Constraint where-  c1 <> c2 = cAnd [c1,c2]-  sconcat  = cAnd . NE.toList-  stimes   = stimesIdempotent+  c1 <> c2 = cAnd [c1, c2]+  sconcat = cAnd . NE.toList+  stimes = stimesIdempotent  instance Monoid Constraint where-  mempty  = CTrue+  mempty = CTrue   mappend = (<>)   mconcat = cAnd
src/Disco/Typecheck/Erase.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Typecheck.Erase -- Copyright   :  (c) 2016 disco team (see LICENSE)@@ -7,82 +10,83 @@ -- -- Typecheck the Disco surface language and transform it into a -- type-annotated AST.---------------------------------------------------------------------------------- module Disco.Typecheck.Erase where -import           Unbound.Generics.LocallyNameless-import           Unbound.Generics.LocallyNameless.Unsafe+import Unbound.Generics.LocallyNameless+import Unbound.Generics.LocallyNameless.Unsafe -import           Control.Arrow                           ((***))-import           Data.Coerce+import Control.Arrow ((***))+import Data.Coerce -import           Disco.AST.Desugared-import           Disco.AST.Surface-import           Disco.AST.Typed-import           Disco.Names                             (QName (..))+import Disco.AST.Desugared+import Disco.AST.Surface+import Disco.AST.Typed+import Disco.Names (QName (..))  -- | Erase all the type annotations from a term. erase :: ATerm -> Term erase (ATVar _ (QName _ x)) = TVar (coerce x)-erase (ATPrim _ x)          = TPrim x-erase (ATLet _ bs)          = TLet $ bind (mapTelescope eraseBinding tel) (erase at)-  where (tel,at) = unsafeUnbind bs-erase ATUnit                = TUnit-erase (ATBool _ b)          = TBool b-erase (ATChar c)            = TChar c-erase (ATString s)          = TString s-erase (ATNat _ i)           = TNat i-erase (ATRat r)             = TRat r-erase (ATAbs q _ b)         = TAbs q $ bind (map erasePattern x) (erase at)-  where (x,at) = unsafeUnbind b-erase (ATApp _ t1 t2)       = TApp (erase t1) (erase t2)-erase (ATTup _ ats)         = TTup (map erase ats)-erase (ATCase _ brs)        = TCase (map eraseBranch brs)-erase (ATChain _ at lnks)   = TChain (erase at) (map eraseLink lnks)-erase (ATTyOp _ op ty)      = TTyOp op ty-erase (ATContainer _ c ats aell)   = TContainer c (map (erase *** fmap erase) ats) ((fmap . fmap) erase aell)-erase (ATContainerComp _ c b)      = TContainerComp c $ bind (mapTelescope eraseQual tel) (erase at)-  where (tel,at) = unsafeUnbind b-erase (ATTest _ x)          = erase x+erase (ATPrim _ x) = TPrim x+erase (ATLet _ bs) = TLet $ bind (mapTelescope eraseBinding tel) (erase at)+ where+  (tel, at) = unsafeUnbind bs+erase ATUnit = TUnit+erase (ATBool _ b) = TBool b+erase (ATChar c) = TChar c+erase (ATString s) = TString s+erase (ATNat _ i) = TNat i+erase (ATRat r) = TRat r+erase (ATAbs q _ b) = TAbs q $ bind (map erasePattern x) (erase at)+ where+  (x, at) = unsafeUnbind b+erase (ATApp _ t1 t2) = TApp (erase t1) (erase t2)+erase (ATTup _ ats) = TTup (map erase ats)+erase (ATCase _ brs) = TCase (map eraseBranch brs)+erase (ATChain _ at lnks) = TChain (erase at) (map eraseLink lnks)+erase (ATTyOp _ op ty) = TTyOp op ty+erase (ATContainer _ c ats aell) = TContainer c (map (erase *** fmap erase) ats) ((fmap . fmap) erase aell)+erase (ATContainerComp _ c b) = TContainerComp c $ bind (mapTelescope eraseQual tel) (erase at)+ where+  (tel, at) = unsafeUnbind b+erase (ATTest _ x) = erase x  eraseBinding :: ABinding -> Binding eraseBinding (ABinding mty x (unembed -> at)) = Binding mty (coerce x) (embed (erase at))  erasePattern :: APattern -> Pattern-erasePattern (APVar _ n)        = PVar (coerce n)-erasePattern (APWild _)         = PWild-erasePattern APUnit             = PUnit-erasePattern (APBool b)         = PBool b-erasePattern (APChar c)         = PChar c-erasePattern (APString s)       = PString s-erasePattern (APTup _ alp)      = PTup $ map erasePattern alp-erasePattern (APInj _ s apt)    = PInj s (erasePattern apt)-erasePattern (APNat _ n)        = PNat n+erasePattern (APVar _ n) = PVar (coerce n)+erasePattern (APWild _) = PWild+erasePattern APUnit = PUnit+erasePattern (APBool b) = PBool b+erasePattern (APChar c) = PChar c+erasePattern (APString s) = PString s+erasePattern (APTup _ alp) = PTup $ map erasePattern alp+erasePattern (APInj _ s apt) = PInj s (erasePattern apt)+erasePattern (APNat _ n) = PNat n erasePattern (APCons _ ap1 ap2) = PCons (erasePattern ap1) (erasePattern ap2)-erasePattern (APList _ alp)     = PList $ map erasePattern alp-erasePattern (APAdd _ s p t)    = PAdd s (erasePattern p) (erase t)-erasePattern (APMul _ s p t)    = PMul s (erasePattern p) (erase t)-erasePattern (APSub _ p t)      = PSub (erasePattern p) (erase t)-erasePattern (APNeg _ p)        = PNeg (erasePattern p)-erasePattern (APFrac _ p1 p2)   = PFrac (erasePattern p1) (erasePattern p2)+erasePattern (APList _ alp) = PList $ map erasePattern alp+erasePattern (APAdd _ s p t) = PAdd s (erasePattern p) (erase t)+erasePattern (APMul _ s p t) = PMul s (erasePattern p) (erase t)+erasePattern (APSub _ p t) = PSub (erasePattern p) (erase t)+erasePattern (APNeg _ p) = PNeg (erasePattern p)+erasePattern (APFrac _ p1 p2) = PFrac (erasePattern p1) (erasePattern p2)  eraseBranch :: ABranch -> Branch eraseBranch b = bind (mapTelescope eraseGuard tel) (erase at)-  where (tel,at) = unsafeUnbind b+ where+  (tel, at) = unsafeUnbind b  eraseGuard :: AGuard -> Guard-eraseGuard (AGBool (unembed -> at))  = GBool (embed (erase at))+eraseGuard (AGBool (unembed -> at)) = GBool (embed (erase at)) eraseGuard (AGPat (unembed -> at) p) = GPat (embed (erase at)) (erasePattern p)-eraseGuard (AGLet b)                 = GLet (eraseBinding b)+eraseGuard (AGLet b) = GLet (eraseBinding b)  eraseLink :: ALink -> Link eraseLink (ATLink bop at) = TLink bop (erase at)  eraseQual :: AQual -> Qual eraseQual (AQBind x (unembed -> at)) = QBind (coerce x) (embed (erase at))-eraseQual (AQGuard (unembed -> at))  = QGuard (embed (erase at))+eraseQual (AQGuard (unembed -> at)) = QGuard (embed (erase at))  eraseProperty :: AProperty -> Property eraseProperty = erase@@ -92,32 +96,33 @@  eraseDTerm :: DTerm -> Term eraseDTerm (DTVar _ (QName _ x)) = TVar (coerce x)-eraseDTerm (DTPrim _ x)     = TPrim x-eraseDTerm DTUnit           = TUnit-eraseDTerm (DTBool _ b)     = TBool b-eraseDTerm (DTChar c)       = TChar c-eraseDTerm (DTNat _ n)      = TNat n-eraseDTerm (DTRat r)        = TRat r-eraseDTerm (DTAbs q _ b)    = TAbs q $ bind [PVar . coerce $ x] (eraseDTerm dt)-  where (x, dt) = unsafeUnbind b-eraseDTerm (DTApp _ d1 d2)  = TApp (eraseDTerm d1) (eraseDTerm d2)+eraseDTerm (DTPrim _ x) = TPrim x+eraseDTerm DTUnit = TUnit+eraseDTerm (DTBool _ b) = TBool b+eraseDTerm (DTChar c) = TChar c+eraseDTerm (DTNat _ n) = TNat n+eraseDTerm (DTRat r) = TRat r+eraseDTerm (DTAbs q _ b) = TAbs q $ bind [PVar . coerce $ x] (eraseDTerm dt)+ where+  (x, dt) = unsafeUnbind b+eraseDTerm (DTApp _ d1 d2) = TApp (eraseDTerm d1) (eraseDTerm d2) eraseDTerm (DTPair _ d1 d2) = TTup [eraseDTerm d1, eraseDTerm d2]-eraseDTerm (DTCase _ bs)    = TCase (map eraseDBranch bs)+eraseDTerm (DTCase _ bs) = TCase (map eraseDBranch bs) eraseDTerm (DTTyOp _ op ty) = TTyOp op ty-eraseDTerm (DTNil _)        = TList [] Nothing-eraseDTerm (DTTest _ x)     = eraseDTerm x+eraseDTerm (DTNil _) = TList [] Nothing+eraseDTerm (DTTest _ x) = eraseDTerm x  eraseDBranch :: DBranch -> Branch eraseDBranch b = bind (mapTelescope eraseDGuard tel) (eraseDTerm d)-  where-    (tel, d) = unsafeUnbind b+ where+  (tel, d) = unsafeUnbind b  eraseDGuard :: DGuard -> Guard eraseDGuard (DGPat (unembed -> d) p) = GPat (embed (eraseDTerm d)) (eraseDPattern p)  eraseDPattern :: DPattern -> Pattern-eraseDPattern (DPVar _ x)      = PVar (coerce x)-eraseDPattern (DPWild _)       = PWild-eraseDPattern DPUnit           = PUnit-eraseDPattern (DPPair _ x1 x2) = PTup (map (PVar . coerce) [x1,x2])-eraseDPattern (DPInj _ s x)    = PInj s (PVar (coerce x))+eraseDPattern (DPVar _ x) = PVar (coerce x)+eraseDPattern (DPWild _) = PWild+eraseDPattern DPUnit = PUnit+eraseDPattern (DPPair _ x1 x2) = PTup (map (PVar . coerce) [x1, x2])+eraseDPattern (DPInj _ s x) = PInj s (PVar (coerce x))
src/Disco/Typecheck/Graph.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE OverloadedStrings #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Typecheck.Graph -- Copyright   :  disco team and contributors@@ -11,65 +14,67 @@ -- A thin layer on top of graphs from the @fgl@ package, which --   allows dealing with vertices by label instead of by integer --   @Node@ values.------------------------------------------------------------------------------- module Disco.Typecheck.Graph where -import           Prelude                           hiding (map, (<>))-import qualified Prelude                           as P+import Prelude hiding (map, (<>))+import qualified Prelude as P -import           Control.Arrow                     ((&&&))-import           Data.Map                          (Map)-import qualified Data.Map                          as M-import           Data.Maybe                        (fromJust, isJust, mapMaybe)-import           Data.Set                          (Set)-import qualified Data.Set                          as S-import           Data.Tuple                        (swap)+import Control.Arrow ((&&&))+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe (fromJust, isJust, mapMaybe)+import Data.Set (Set)+import qualified Data.Set as S+import Data.Tuple (swap) -import qualified Data.Graph.Inductive.Graph        as G-import           Data.Graph.Inductive.PatriciaTree (Gr)-import qualified Data.Graph.Inductive.Query.DFS    as G (components,-                                                         condensation, topsort')+import qualified Data.Graph.Inductive.Graph as G+import Data.Graph.Inductive.PatriciaTree (Gr)+import qualified Data.Graph.Inductive.Query.DFS as G (+  components,+  condensation,+  topsort',+ ) -import           Disco.Pretty-import           Disco.Util                        ((!))+import Disco.Pretty+import Disco.Util ((!))  -- | Directed graphs, with vertices labelled by @a@ and unlabelled --   edges. data Graph a = G (Gr a ()) (Map a G.Node) (Map G.Node a)-  deriving Show+  deriving (Show)  instance Pretty a => Pretty (Graph a) where   pretty (G g _ _) = parens (prettyVertices <> ", " <> prettyEdges)+   where     -- (V = {(0, x), (1, N)}, E = {0 -> 1, 2 -> 3})-    where-      vs = G.labNodes g-      es = G.labEdges g -      prettyVertex (n,a) = parens (text (show n) <> ", " <> pretty a)-      prettyVertices = "V = " <> braces (intercalate "," (P.map prettyVertex vs))-      prettyEdge (v1,v2,_) = text (show v1) <+> "->" <+> text (show v2)-      prettyEdges = "E = " <> braces (intercalate "," (P.map prettyEdge es))+    vs = G.labNodes g+    es = G.labEdges g +    prettyVertex (n, a) = parens (text (show n) <> ", " <> pretty a)+    prettyVertices = "V = " <> braces (intercalate "," (P.map prettyVertex vs))+    prettyEdge (v1, v2, _) = text (show v1) <+> "->" <+> text (show v2)+    prettyEdges = "E = " <> braces (intercalate "," (P.map prettyEdge es))+ -- | Create a graph with the given set of vertices and directed edges. --   If any edges refer to vertices that are not in the given vertex --   set, they will simply be dropped.-mkGraph :: (Show a, Ord a) => Set a -> Set (a,a) -> Graph a+mkGraph :: (Show a, Ord a) => Set a -> Set (a, a) -> Graph a mkGraph vs es = G (G.mkGraph vs' es') a2n n2a-  where-    vs' = zip [0..] (S.toList vs)-    n2a = M.fromList vs'-    a2n = M.fromList . P.map swap $ vs'-    es' = mapMaybe mkEdge (S.toList es)-    mkEdge (a1,a2) = (,,) <$> M.lookup a1 a2n <*> M.lookup a2 a2n <*> pure ()+ where+  vs' = zip [0 ..] (S.toList vs)+  n2a = M.fromList vs'+  a2n = M.fromList . P.map swap $ vs'+  es' = mapMaybe mkEdge (S.toList es)+  mkEdge (a1, a2) = (,,) <$> M.lookup a1 a2n <*> M.lookup a2 a2n <*> pure ()  -- | Return the set of vertices (nodes) of a graph. nodes :: Graph a -> Set a nodes (G _ m _) = M.keysSet m  -- | Return the set of directed edges of a graph.-edges :: Ord a => Graph a -> Set (a,a)-edges (G g _ m) = S.fromList $ P.map (\(n1,n2,()) -> (m ! n1, m ! n2)) (G.labEdges g)+edges :: Ord a => Graph a -> Set (a, a)+edges (G g _ m) = S.fromList $ P.map (\(n1, n2, ()) -> (m ! n1, m ! n2)) (G.labEdges g)  -- | Map a function over all the vertices of a graph.  @Graph@ is not --   a @Functor@ instance because of the @Ord@ constraint on @b@.@@ -79,8 +84,8 @@ -- | Delete a vertex. delete :: (Show a, Ord a) => a -> Graph a -> Graph a delete a (G g a2n n2a) = G (G.delNode n g) (M.delete a a2n) (M.delete n n2a)-  where-    n = a2n ! a+ where+  n = a2n ! a  -- | The @condensation@ of a graph is the graph of its strongly --   connected components, /i.e./ each strongly connected component is@@ -90,11 +95,11 @@ --   component A to any vertex in component B in the original graph. condensation :: Ord a => Graph a -> Graph (Set a) condensation (G g _ n2a) = G g' as2n n2as-  where-    g' = G.nmap (S.fromList . P.map (n2a !)) (G.condensation g)-    vs' = G.labNodes g'-    n2as = M.fromList vs'-    as2n = M.fromList . P.map swap $ vs'+ where+  g' = G.nmap (S.fromList . P.map (n2a !)) (G.condensation g)+  vs' = G.labNodes g'+  n2as = M.fromList vs'+  as2n = M.fromList . P.map swap $ vs'  -- | Get a list of the weakly connected components of a graph, --   providing the set of vertices in each.  Equivalently, return the@@ -116,7 +121,7 @@ sequenceGraph :: Ord a => Graph (Maybe a) -> Maybe (Graph a) sequenceGraph g = case all isJust (nodes g) of   False -> Nothing-  True  -> Just $ map fromJust g+  True -> Just $ map fromJust g  -- | Get a list of all the /successors/ of a given node in the graph, --   /i.e./ all the nodes reachable from the given node by a directed@@ -140,16 +145,16 @@ --   but much more efficient. cessors :: (Show a, Ord a) => Graph a -> (Map a (Set a), Map a (Set a)) cessors g@(G gg _ _) = (succs, preds)-  where-    as = G.topsort' gg-    succs = foldr collectSuccs M.empty as  -- build successors map-    collectSuccs a m = M.insert a succsSet m-      where-        ss       = suc g a-        succsSet = S.fromList ss `S.union` S.unions (P.map (m !) ss)+ where+  as = G.topsort' gg+  succs = foldr collectSuccs M.empty as -- build successors map+  collectSuccs a m = M.insert a succsSet m+   where+    ss = suc g a+    succsSet = S.fromList ss `S.union` S.unions (P.map (m !) ss) -    preds = foldr collectPreds M.empty (reverse as)  -- build predecessors map-    collectPreds a m = M.insert a predsSet m-      where-        ss       = pre g a-        predsSet = S.fromList ss `S.union` S.unions (P.map (m !) ss)+  preds = foldr collectPreds M.empty (reverse as) -- build predecessors map+  collectPreds a m = M.insert a predsSet m+   where+    ss = pre g a+    predsSet = S.fromList ss `S.union` S.unions (P.map (m !) ss)
src/Disco/Typecheck/Solve.hs view
@@ -1,1083 +1,1182 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell   #-}---------------------------------------------------------------------------------- |--- Module      :  Disco.Typecheck.Solve--- Copyright   :  disco team and contributors--- Maintainer  :  byorgey@gmail.com------ SPDX-License-Identifier: BSD-3-Clause------ Constraint solver for the constraints generated during type--- checking/inference.--------------------------------------------------------------------------------module Disco.Typecheck.Solve where--import           Unbound.Generics.LocallyNameless (Alpha, Name, Subst, fv,-                                                   name2Integer, string2Name,-                                                   substs)--import           Data.Coerce-import           GHC.Generics                     (Generic)--import           Control.Arrow                    ((&&&), (***))-import           Control.Lens                     hiding (use, (%=), (.=))-import           Control.Monad                    (unless, zipWithM)-import           Data.Bifunctor                   (first, second)-import           Data.Either                      (partitionEithers)-import           Data.List                        (find, foldl', intersect,-                                                   partition)-import           Data.Map                         (Map, (!))-import qualified Data.Map                         as M-import           Data.Maybe                       (fromJust, fromMaybe,-                                                   mapMaybe)-import           Data.Monoid                      (First (..))-import           Data.Set                         (Set)-import qualified Data.Set                         as S-import           Data.Tuple--import           Disco.Effects.Fresh-import           Disco.Effects.State-import           Polysemy-import           Polysemy.Error-import           Polysemy.Input-import           Polysemy.Output--import           Disco.Messages-import           Disco.Pretty                     hiding ((<>))-import           Disco.Subst-import qualified Disco.Subst                      as Subst-import           Disco.Typecheck.Constraints-import           Disco.Typecheck.Graph            (Graph)-import qualified Disco.Typecheck.Graph            as G-import           Disco.Typecheck.Unify-import           Disco.Types-import           Disco.Types.Qualifiers-import           Disco.Types.Rules------------------------------------------------------- Solver errors---- | Type of errors which can be generated by the constraint solving---   process.-data SolveError where-  NoWeakUnifier :: SolveError-  NoUnify       :: SolveError-  UnqualBase    :: Qualifier -> BaseTy    -> SolveError-  Unqual        :: Qualifier -> Type      -> SolveError-  QualSkolem    :: Qualifier -> Name Type -> SolveError-  deriving Show--instance Semigroup SolveError where-  e <> _ = e------------------------------------------------------- Error utilities--runSolve :: Sem (Fresh ': Error SolveError ': r) a -> Sem r (Either SolveError a)-runSolve = runError . runFresh---- | Run a list of actions, and return the results from those which do---   not throw an error.  If all of them throw an error, rethrow the---   first one.-filterErrors :: Member (Error e) r => [Sem r a] -> Sem r [a]-filterErrors ms = do-  es <- mapM try ms-  case partitionEithers es of-    (e:_, []) -> throw e-    (_, as)   -> return as---- | A variant of 'asum' which picks the first action that succeeds,---   or re-throws the error of the last one if none of them---   do. Precondition: the list must not be empty.-asum' :: Member (Error e) r => [Sem r a] -> Sem r a-asum' []     = error "Impossible: asum' []"-asum' [m]    = m-asum' (m:ms) = m `catch` (\_ -> asum' ms)------------------------------------------------------- Simple constraints--data SimpleConstraint where-  (:<:) :: Type -> Type -> SimpleConstraint-  (:=:) :: Type -> Type -> SimpleConstraint-  deriving (Show, Eq, Ord, Generic, Alpha, Subst Type)--instance Pretty SimpleConstraint where-  pretty = \case-    ty1 :<: ty2 -> pretty ty1 <+> "<:" <+> pretty ty2-    ty1 :=: ty2 -> pretty ty1 <+> "=" <+> pretty ty2------------------------------------------------------- Simplifier types---- Uses TH to generate lenses so it has to go here before other stuff.-------------------------------------- Variable maps---- | Information about a particular type variable.  More information---   may be added in the future (e.g. polarity).-data TyVarInfo = TVI-  { _tyVarIlk  :: First Ilk   -- ^ The ilk (unification or skolem) of the variable, if known-  , _tyVarSort :: Sort        -- ^ The sort (set of qualifiers) of the type variable.-  }-  deriving (Show)--makeLenses ''TyVarInfo--instance Pretty TyVarInfo where-  pretty (TVI (First ilk) s) = maybe (pure "?") pretty ilk <> "%" <> pretty s---- | Create a 'TyVarInfo' given an 'Ilk' and a 'Sort'.-mkTVI :: Ilk -> Sort -> TyVarInfo-mkTVI = TVI . First . Just---- | We can learn different things about a type variable from---   different places; the 'Semigroup' instance allows combining---   information about a type variable into a single record.-instance Semigroup TyVarInfo where-  TVI i1 s1 <> TVI i2 s2 = TVI (i1 <> i2) (s1 <> s2)---- | A 'TyVarInfoMap' records what we know about each type variable;---   it is a mapping from type variable names to 'TyVarInfo' records.-newtype TyVarInfoMap = VM { unVM :: Map (Name Type) TyVarInfo }-  deriving (Show)--instance Pretty TyVarInfoMap where-  pretty (VM m) = pretty m---- | Utility function for acting on a 'TyVarInfoMap' by acting on the---   underlying 'Map'.-onVM ::-  (Map (Name Type) TyVarInfo -> Map (Name Type) TyVarInfo) ->-  TyVarInfoMap -> TyVarInfoMap-onVM f (VM m) = VM (f m)---- | Look up a given variable name in a 'TyVarInfoMap'.-lookupVM :: Name Type -> TyVarInfoMap -> Maybe TyVarInfo-lookupVM v = M.lookup v . unVM---- | Remove the mapping for a particular variable name (if it exists)---   from a 'TyVarInfoMap'.-deleteVM :: Name Type -> TyVarInfoMap -> TyVarInfoMap-deleteVM = onVM . M.delete---- | Given a list of type variable names, add them all to the---   'TyVarInfoMap' as 'Skolem' variables (with a trivial sort).-addSkolems :: [Name Type] -> TyVarInfoMap -> TyVarInfoMap-addSkolems vs = onVM $ \vm -> foldl' (flip (\v -> M.insert v (mkTVI Skolem mempty))) vm vs---- | The @Semigroup@ instance for 'TyVarInfoMap' unions the two maps,---   combining the info records for any variables occurring in both---   maps.-instance Semigroup TyVarInfoMap where-  VM sm1 <> VM sm2 = VM (M.unionWith (<>) sm1 sm2)--instance Monoid TyVarInfoMap where-  mempty  = VM M.empty-  mappend = (<>)---- | Get the sort of a particular variable recorded in a---   'TyVarInfoMap'.  Returns the trivial (empty) sort for a variable---   not in the map.-getSort :: TyVarInfoMap -> Name Type -> Sort-getSort (VM m) v = maybe topSort (view tyVarSort) (M.lookup v m)---- | Get the 'Ilk' of a variable recorded in a 'TyVarInfoMap'.---   Returns @Nothing@ if the variable is not in the map, or if its---   ilk is not known.-getIlk :: TyVarInfoMap -> Name Type -> Maybe Ilk-getIlk (VM m) v = (m ^? ix v . tyVarIlk) >>= getFirst---- | Extend the sort of a type variable by combining its existing sort---   with the given one.  Has no effect if the variable is not already---   in the map.-extendSort :: Name Type -> Sort -> TyVarInfoMap -> TyVarInfoMap-extendSort x s = onVM (at x . _Just . tyVarSort %~ (`S.union` s))-------------------------------------- Simplifier state---- The simplification stage maintains a mutable state consisting of--- the current qualifier map (containing wanted qualifiers for type--- variables), the list of remaining SimpleConstraints, and the--- current substitution.  It also keeps track of seen constraints, so--- expansion of recursive types can stop when encountering a--- previously seen constraint.-data SimplifyState = SS-  { _ssVarMap      :: TyVarInfoMap-  , _ssConstraints :: [SimpleConstraint]-  , _ssSubst       :: S-  , _ssSeen        :: Set SimpleConstraint-  }--makeLenses ''SimplifyState--lkup :: (Ord k, Show k, Show (Map k a)) => String -> Map k a -> k -> a-lkup messg m k = fromMaybe (error errMsg) (M.lookup k m)-  where-    errMsg = unlines-      [ "Key lookup error:"-      , "  Key = " ++ show k-      , "  Map = " ++ show m-      , "  Location: " ++ messg-      ]------------------------------------------------------- Top-level solver algorithm--solveConstraint-  :: Members '[Fresh, Error SolveError, Output Message, Input TyDefCtx] r-  => Constraint -> Sem r S-solveConstraint c = do--  -- Step 1. Open foralls (instantiating with skolem variables) and-  -- collect wanted qualifiers; also expand disjunctions.  Result in a-  -- list of possible constraint sets; each one consists of equational-  -- and subtyping constraints in addition to qualifiers.--  debug "Solving:"-  debugPretty c--  debug "------------------------------"-  debug "Decomposing constraints..."--  qcList <- decomposeConstraint c--  -- Now try continuing with each set and pick the first one that has-  -- a solution.-  asum' (map (uncurry solveConstraintChoice) qcList)--solveConstraintChoice-  :: Members '[Fresh, Error SolveError, Output Message, Input TyDefCtx] r-  => TyVarInfoMap -> [SimpleConstraint] -> Sem r S-solveConstraintChoice quals cs = do--  debugPretty quals-  debug $ vcat (map pretty' cs)--  tyDefns <- input @TyDefCtx--  -- Step 2. Check for weak unification to ensure termination. (a la-  -- Traytel et al).--  let toEqn (t1 :<: t2) = (t1,t2)-      toEqn (t1 :=: t2) = (t1,t2)-  _ <- note NoWeakUnifier $ weakUnify tyDefns (map toEqn cs)--  -- Step 3. Simplify constraints, resulting in a set of atomic-  -- subtyping constraints.  Also simplify/update qualifier set-  -- accordingly.--  debug "------------------------------"-  debug "Running simplifier..."--  (vm, atoms, theta_simp) <- simplify quals cs-  debug "Done running simplifier. Results:"--  debugPretty vm-  debug $ vcat $ map (pretty' . (\(x,y) -> TyAtom x :<: TyAtom y)) atoms-  debugPretty theta_simp--  -- Step 4. Turn the atomic constraints into a directed constraint-  -- graph.--  debug "------------------------------"-  debug "Generating constraint graph..."--  -- Some variables might have qualifiers but not participate in any-  -- equality or subtyping relations (see issue #153); make sure to-  -- extract them and include them in the constraint graph as isolated-  -- vertices-  let mkAVar (v, First (Just Skolem)) = AVar (S v)-      mkAVar (v, _                  ) = AVar (U v)-      vars = S.fromList . map (mkAVar . second (view tyVarIlk)) . M.assocs . unVM $ vm-      g = mkConstraintGraph vars atoms--  debugPretty g--  -- Step 5.-  -- Check for any weakly connected components containing more-  -- than one skolem, or a skolem and a base type; such components are-  -- not allowed.  Other WCCs with a single skolem simply unify to-  -- that skolem.--  debug "------------------------------"-  debug "Checking WCCs for skolems..."--  (g', theta_skolem) <- checkSkolems vm g-  debugPretty theta_skolem--  -- We don't need to ensure that theta_skolem respects sorts since-  -- checkSkolems will only unify skolem vars with unsorted variables.---  -- Step 6. Eliminate cycles from the graph, turning each strongly-  -- connected component into a single node, unifying all the atoms in-  -- each component.--  debug "------------------------------"-  debug "Collapsing SCCs..."--  (g'', theta_cyc) <- elimCycles tyDefns g'--  debugPretty g''-  debugPretty theta_cyc--  -- Check that the resulting substitution respects sorts...-  let sortOK (x, TyAtom (ABase ty))   = hasSort ty (getSort vm x)-      sortOK (_, TyAtom (AVar (U _))) = True-      sortOK p                        = error $ "Impossible! sortOK " ++ show p-  unless (all sortOK (Subst.toList theta_cyc))-    $ throw NoUnify--  -- ... and update the sort map if we unified any type variables.-  -- Just make sure that if theta_cyc maps x |-> y, then y picks up-  -- the sort of x.--  debug "Old sort map:"-  debugPretty vm--  let vm' = foldr updateVarMap vm (Subst.toList theta_cyc)-        where-          updateVarMap :: (Name Type, Type) -> TyVarInfoMap -> TyVarInfoMap-          updateVarMap (x, TyAtom (AVar (U y))) vmm = extendSort y (getSort vmm x) vmm-          updateVarMap _                        vmm = vmm--  debug "Updated sort map:"-  debugPretty vm--  -- Steps 7 & 8: solve the graph, iteratively finding satisfying-  -- assignments for each type variable based on its successor and-  -- predecessor base types in the graph; then unify all the type-  -- variables in any remaining weakly connected components.--  debug "------------------------------"-  debug "Solving for type variables..."--  theta_sol       <- solveGraph vm' g''-  debugPretty theta_sol--  debug "------------------------------"-  debug "Composing final substitution..."--  let theta_final = theta_sol @@ theta_cyc @@ theta_skolem @@ theta_simp-  debugPretty theta_final--  return theta_final-------------------------------------------------------- Step 1. Constraint decomposition.--decomposeConstraint-  :: Members '[Fresh, Error SolveError, Input TyDefCtx] r-  => Constraint -> Sem r [(TyVarInfoMap, [SimpleConstraint])]-decomposeConstraint (CSub t1 t2) = return [(mempty, [t1 :<: t2])]-decomposeConstraint (CEq  t1 t2) = return [(mempty, [t1 :=: t2])]-decomposeConstraint (CQual q ty) = (:[]) . (, []) <$> decomposeQual ty q-decomposeConstraint (CAnd cs)    = map mconcat . sequence <$> mapM decomposeConstraint cs-decomposeConstraint CTrue        = return [mempty]-decomposeConstraint (CAll ty)    = do-  (vars, c) <- unbind ty-  let c' = substs (mkSkolems vars) c-  (map . first . addSkolems) vars <$> decomposeConstraint c'--  where-    mkSkolems :: [Name Type] -> [(Name Type, Type)]-    mkSkolems = map (id &&& TySkolem)--decomposeConstraint (COr cs)     = concat <$> filterErrors (map decomposeConstraint cs)--decomposeQual-  :: Members '[Fresh, Error SolveError, Input TyDefCtx] r-  => Type -> Qualifier -> Sem r TyVarInfoMap-decomposeQual = go S.empty-  where-    go :: Members '[Fresh, Error SolveError, Input TyDefCtx] r-       => Set (String, [Type], Qualifier) -> Type -> Qualifier -> Sem r TyVarInfoMap--    -- For a type atom, call out to checkQual.-    go _ (TyAtom a) q = checkQual q a--    -- Coinductively check user-defined types for a qualifier.  Keep-    -- track of a set of user-defined types and qualifiers we have-    -- seen.  Every time we encounter a new one, add it to the set and-    -- recurse on its unfolding.  If we ever encounter one we have-    -- already seen, we can assume by coinduction that the qualifier-    -- is satisfied.-    go seen (TyCon (CUser t) tys) q = do-      case (t, tys, q) `S.member` seen of-        True -> return mempty-        False -> do-          tyDefns <- input @TyDefCtx-          case M.lookup t tyDefns of-            Nothing  -> error $ show t ++ " not in ty defn map!!"-            Just (TyDefBody _ body) -> do-              let ty' = body tys-              go (S.insert (t, tys, q) seen) ty' q--    -- If we have a container type where the container is still a variable,-    -- just replace it with List for the purposes of generating constraints----    -- all containers (lists, bags, sets) have the same qualifier rules.-    go seen (TyCon (CContainer (AVar _)) tys) q = go seen (TyCon CList tys) q--    -- Otherwise, decompose a type constructor according to the qualRules.-    go seen ty@(TyCon c tys) q = case qualRules c q of-      Nothing -> throw $ Unqual q ty-      Just qs -> mconcat <$> zipWithM (maybe (return mempty) . go seen) tys qs--checkQual-  :: Members '[Fresh, Error SolveError] r-  => Qualifier -> Atom -> Sem r TyVarInfoMap-checkQual q (AVar (U v)) = return . VM . M.singleton v $ mkTVI Unification (S.singleton q)-checkQual q (AVar (S v)) = throw $ QualSkolem q v-checkQual q (ABase bty)  =-  case hasQual bty q of-    True  -> return mempty-    False -> throw $ UnqualBase q bty------------------------------------------------------- Step 3. Constraint simplification.---- | This step does unification of equality constraints, as well as---   structural decomposition of subtyping constraints.  For example,---   if we have a constraint (x -> y) <: (z -> Int), then we can---   decompose it into two constraints, (z <: x) and (y <: Int); if we---   have a constraint v <: (a,b), then we substitute v ↦ (x,y) (where---   x and y are fresh type variables) and continue; and so on.------   After this step, the remaining constraints will all be atomic---   constraints, that is, only of the form (v1 <: v2), (v <: b), or---   (b <: v), where v is a type variable and b is a base type.--simplify-  :: Members '[Error SolveError, Output Message, Input TyDefCtx] r-  => TyVarInfoMap -> [SimpleConstraint] -> Sem r (TyVarInfoMap, [(Atom, Atom)], S)-simplify origVM cs-  = (\(SS vm' cs' s' _) -> (vm', map extractAtoms cs', s'))-  -- contFreshMT :: Monad m => FreshMT m a -> Integer -> m a-  -- "Run a FreshMT computation given a starting index for fresh name generation."-  <$> runFresh' n (execState (SS origVM cs idS S.empty) simplify')-  where--    fvNums :: Alpha a => [a] -> [Integer]-    fvNums = map (name2Integer :: Name Type -> Integer) . toListOf fv--    -- Find first unused integer in constraint free vars and sort map-    -- domain, and use it to start the fresh var generation, so we don't-    -- generate any "fresh" names that interfere with existing names-    n1 = maximum0 . fvNums $ cs-    n = succ . maximum . (n1:) . fvNums . M.keys . unVM $ origVM--    maximum0 [] = 0-    maximum0 xs = maximum xs--    -- Extract the type atoms from an atomic constraint.-    extractAtoms :: SimpleConstraint -> (Atom, Atom)-    extractAtoms (TyAtom a1 :<: TyAtom a2) = (a1, a2)-    extractAtoms c = error $ "Impossible: simplify left non-atomic or non-subtype constraint " ++ show c--    -- Iterate picking one simplifiable constraint and simplifying it-    -- until none are left.-    simplify'-      :: Members '[State SimplifyState, Fresh, Error SolveError, Output Message, Input TyDefCtx] r-      => Sem r ()-    simplify' = do-      -- q <- gets fst-      -- debug (pretty q)-      -- debug ""--      mc <- pickSimplifiable-      case mc of-        Nothing -> return ()-        Just s  -> do--          debug $ "Simplifying:" <+> pretty' s--          simplifyOne s-          simplify'--    -- Pick out one simplifiable constraint, removing it from the list-    -- of constraints in the state.  Return Nothing if no more-    -- constraints can be simplified.-    pickSimplifiable-      :: Members '[State SimplifyState, Fresh, Error SolveError] r-      => Sem r (Maybe SimpleConstraint)-    pickSimplifiable = do-      curCs <- use ssConstraints-      case pick simplifiable curCs of-        Nothing     -> return Nothing-        Just (a,as) -> do-          ssConstraints .= as-          return (Just a)--    -- Pick the first element from a list satisfying the given-    -- predicate, returning the element and the list with the element-    -- removed.-    pick :: (a -> Bool) -> [a] -> Maybe (a,[a])-    pick _ [] = Nothing-    pick p (a:as)-      | p a       = Just (a,as)-      | otherwise = second (a:) <$> pick p as--    -- Check if a constraint can be simplified.  An equality-    -- constraint can always be "simplified" via unification.  A-    -- subtyping constraint can be simplified if either it involves a-    -- type constructor (in which case we can decompose it), or if it-    -- involves two base types (in which case it can be removed if the-    -- relationship holds).-    simplifiable :: SimpleConstraint -> Bool-    simplifiable (_ :=: _)                               = True-    simplifiable (TyCon {} :<: TyCon {})                 = True-    simplifiable (TyVar {} :<: TyCon {})                 = True-    simplifiable (TyCon {} :<: TyVar {})                 = True-    simplifiable (TyCon (CUser _) _ :<: _)               = True-    simplifiable (_ :<: TyCon (CUser _) _)               = True-    simplifiable (TyAtom (ABase _) :<: TyAtom (ABase _)) = True--    simplifiable _                                       = False--    -- Simplify the given simplifiable constraint.  If the constraint-    -- has already been seen before (due to expansion of a recursive-    -- type), just throw it away and stop.-    simplifyOne-      :: Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r-      => SimpleConstraint -> Sem r ()-    simplifyOne c = do-      seen <- use ssSeen-      case c `S.member` seen of-        True  -> return ()-        False -> do-          ssSeen %= S.insert c-          simplifyOne' c--    simplifyOne'-      :: Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r-      => SimpleConstraint -> Sem r ()--    -- If we have an equality constraint, run unification on it.  The-    -- resulting substitution is applied to the remaining constraints-    -- as well as prepended to the current substitution.--    simplifyOne' (ty1 :=: ty2) = do-      tyDefns <- input @TyDefCtx-      case unify tyDefns [(ty1, ty2)] of-        Nothing -> throw NoUnify-        Just s' -> extendSubst s'--    -- If we see a constraint of the form (T <: a), where T is a-    -- user-defined type and a is a type variable, then just turn it-    -- into an equality (T = a).  This is sound but probably not-    -- complete.  The alternative seems quite complicated, possibly-    -- even undecidable.  See https://github.com/disco-lang/disco/issues/207 .-    simplifyOne' (ty1@(TyCon (CUser _) _) :<: ty2@TyVar{})-      = simplifyOne' (ty1 :=: ty2)--    -- Otherwise, expand the user-defined type and continue.-    simplifyOne' (TyCon (CUser t) ts :<: ty2) = do-      tyDefns <- input @TyDefCtx-      case M.lookup t tyDefns of-        Nothing  -> error $ show t ++ " not in ty defn map!"-        Just (TyDefBody _ body) ->-          ssConstraints %= ((body ts :<: ty2) :)--    -- Turn  a <: T  into  a = T.  See comment above.-    simplifyOne' (ty1@TyVar{} :<: ty2@(TyCon (CUser _) _))-      = simplifyOne' (ty1 :=: ty2)--    simplifyOne' (ty1 :<: TyCon (CUser t) ts) = do-      tyDefns <- input @TyDefCtx-      case M.lookup t tyDefns of-        Nothing  -> error $ show t ++ " not in ty defn map!"-        Just (TyDefBody _ body) ->-          ssConstraints %= ((ty1 :<: body ts) :)--    -- Given a subtyping constraint between two type constructors,-    -- decompose it if the constructors are the same (or fail if they-    -- aren't), taking into account the variance of each argument to-    -- the constructor.  Container types are a special case;-    -- recursively generate a subtyping constraint for their-    -- constructors as well.-    simplifyOne' (TyCon c1@(CContainer ctr1) tys1 :<: TyCon (CContainer ctr2) tys2) =-      ssConstraints %=-        (( (TyAtom ctr1 :<: TyAtom ctr2)-         : zipWith3 variance (arity c1) tys1 tys2-         )-         ++)--    simplifyOne' (TyCon c1 tys1 :<: TyCon c2 tys2)-      | c1 /= c2  = throw NoUnify-      | otherwise =-          ssConstraints %= (zipWith3 variance (arity c1) tys1 tys2 ++)--    -- Given a subtyping constraint between a variable and a type-    -- constructor, expand the variable into the same constructor-    -- applied to fresh type variables.-    simplifyOne' con@(TyVar a   :<: TyCon c _) = expandStruct a c con-    simplifyOne' con@(TyCon c _ :<: TyVar a  ) = expandStruct a c con--    -- Given a subtyping constraint between two base types, just check-    -- whether the first is indeed a subtype of the second.  (Note-    -- that we only pattern match here on type atoms, which could-    -- include variables, but this will only ever get called if-    -- 'simplifiable' was true, which checks that both are base-    -- types.)-    simplifyOne' (TyAtom (ABase b1) :<: TyAtom (ABase b2)) = do-      case isSubB b1 b2 of-        True  -> return ()-        False -> throw NoUnify--    simplifyOne' (s :<: t) =-      error $ "Impossible! simplifyOne' " ++ show s ++ " :<: " ++ show t--    expandStruct-      :: Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r-      => Name Type -> Con -> SimpleConstraint -> Sem r ()-    expandStruct a c con = do-      as <- mapM (const (TyVar <$> fresh (string2Name "a"))) (arity c)-      let s' = a |-> TyCon c as-      ssConstraints %= (con:)-      extendSubst s'--    -- 1. compose s' with current subst-    -- 2. apply s' to constraints-    -- 3. apply s' to qualifier map and decompose-    extendSubst-      :: Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r-      => S -> Sem r ()-    extendSubst s' = do-      ssSubst %= (s'@@)-      ssConstraints %= applySubst s'-      substVarMap s'--    substVarMap-      :: Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r-      => S -> Sem r ()-    substVarMap s' = do-      vm <- use ssVarMap--      -- 1. Get quals for each var in domain of s' and match them with-      -- the types being substituted for those vars.--      let tySorts :: [(Type, Sort)]-          tySorts = map (second (view tyVarSort)) . mapMaybe (traverse (`lookupVM` vm) . swap) $ Subst.toList s'--          tyQualList :: [(Type, Qualifier)]-          tyQualList = concatMap (sequenceA . second S.toList) tySorts--      -- 2. Decompose the resulting qualifier constraints--      vm' <- mconcat <$> mapM (uncurry decomposeQual) tyQualList--      -- 3. delete domain of s' from vm and merge in decomposed quals.--      ssVarMap .= vm' <> foldl' (flip deleteVM) vm (dom s')--      -- The above works even when unifying two variables.  Say we have-      -- the TyVarInfoMap-      ---      --   a |-> {add}-      --   b |-> {sub}-      ---      -- and we get back theta = [a |-> b].  The domain of theta-      -- consists solely of a, so we look up a in the TyVarInfoMap and get-      -- {add}.  We therefore generate the constraint 'add (theta a)'-      -- = 'add b' which can't be decomposed at all, and hence yields-      -- the TyVarInfoMap {b |-> {add}}.  We then delete a from the-      -- original TyVarInfoMap and merge the result with the new TyVarInfoMap,-      -- yielding {b |-> {sub,add}}.---    -- Create a subtyping constraint based on the variance of a type-    -- constructor argument position: in the usual order for-    -- covariant, and reversed for contravariant.-    variance Co     ty1 ty2 = ty1 :<: ty2-    variance Contra ty1 ty2 = ty2 :<: ty1------------------------------------------------------- Step 4: Build constraint graph---- | Given a list of atoms and atomic subtype constraints (each pair---   @(a1,a2)@ corresponds to the constraint @a1 <: a2@) build the---   corresponding constraint graph.-mkConstraintGraph :: (Show a, Ord a) => Set a -> [(a, a)] -> Graph a-mkConstraintGraph as cs = G.mkGraph nodes (S.fromList cs)-  where-    nodes = as `S.union` S.fromList (cs ^.. traverse . each)------------------------------------------------------- Step 5: Check skolems---- | Check for any weakly connected components containing more than---   one skolem, or a skolem and a base type, or a skolem and any---   variables with nontrivial sorts; such components are not allowed.---   If there are any WCCs with a single skolem, no base types, and---   only unsorted variables, just unify them all with the skolem and---   remove those components.-checkSkolems-  :: Members '[Error SolveError, Output Message, Input TyDefCtx] r-  => TyVarInfoMap -> Graph Atom -> Sem r (Graph UAtom, S)-checkSkolems vm graph = do-  let skolemWCCs :: [Set Atom]-      skolemWCCs = filter (any isSkolem) $ G.wcc graph--      ok wcc =  S.size (S.filter isSkolem wcc) <= 1-             && all (\case { ABase _    -> False-                           ; AVar (S _) -> True-                           ; AVar (U v) -> maybe True (S.null . view tyVarSort) (lookupVM v vm) })-                wcc--      (good, bad) = partition ok skolemWCCs--  unless (null bad) $ throw NoUnify--  -- take all good sets and-  --   (1) delete them from the graph-  --   (2) unify them all with the skolem-  unifyWCCs graph idS good--  where-    noSkolems :: Atom -> UAtom-    noSkolems (ABase b)    = UB b-    noSkolems (AVar (U v)) = UV v-    noSkolems (AVar (S v)) = error $ "Skolem " ++ show v ++ " remaining in noSkolems"--    unifyWCCs-      :: Members '[Error SolveError, Output Message, Input TyDefCtx] r-      => Graph Atom -> S -> [Set Atom] -> Sem r (Graph UAtom, S)-    unifyWCCs g s []     = return (G.map noSkolems g, s)-    unifyWCCs g s (u:us) = do-      debug $ "Unifying" <+> pretty' (u:us) <> "..."--      tyDefns <- input @TyDefCtx--      let g' = foldl' (flip G.delete) g u--          ms' = unifyAtoms tyDefns (S.toList u)-      case ms' of-        Nothing -> throw NoUnify-        Just s' -> unifyWCCs g' (atomToTypeSubst s' @@ s) us------------------------------------------------------- Step 6: Eliminate cycles---- | Eliminate cycles in the constraint set by collapsing each---   strongly connected component to a single node, (unifying all the---   types in the SCC). A strongly connected component is a maximal---   set of nodes where every node is reachable from every other by a---   directed path; since we are using directed edges to indicate a---   subtyping constraint, this means every node must be a subtype of---   every other, and the only way this can happen is if all are in---   fact equal.------   Of course, this step can fail if the types in a SCC are not---   unifiable.  If it succeeds, it returns the collapsed graph (which---   is now guaranteed to be acyclic, i.e. a DAG) and a substitution.-elimCycles-  :: Members '[Error SolveError] r-  => TyDefCtx -> Graph UAtom -> Sem r (Graph UAtom, S)-elimCycles tyDefns = elimCyclesGen uatomToTypeSubst (unifyUAtoms tyDefns)--elimCyclesGen-  :: forall a b r. (Subst a a, Ord a, Members '[Error SolveError] r)-  => (Substitution a -> Substitution b) -> ([a] -> Maybe (Substitution a))-  -> Graph a -> Sem r (Graph a, Substitution b)-elimCyclesGen genSubst genUnify g-  = note NoUnify-  $ (G.map fst &&& (genSubst . compose . S.map snd . G.nodes)) <$> g'-  where--    g' :: Maybe (Graph (a, Substitution a))-    g' = G.sequenceGraph $ G.map unifySCC (G.condensation g)--    unifySCC :: Set a -> Maybe (a, Substitution a)-    unifySCC uatoms = case S.toList uatoms of-      []       -> error "Impossible! unifySCC on the empty set"-      as@(a:_) -> (flip applySubst a &&& id) <$> genUnify as----------------------------------------------------------------- Steps 7 and 8: Constraint resolution----------------------------------------------------------------- | Rels stores the set of base types and variables related to a---   given variable in the constraint graph (either predecessors or---   successors, but not both).-data Rels = Rels-  { baseRels :: Set BaseTy-  , varRels  :: Set (Name Type)-  }-  deriving (Show, Eq)---- | A RelMap associates each variable to its sets of base type and---   variable predecessors and successors in the constraint graph.-newtype RelMap = RelMap { unRelMap :: Map (Name Type, Dir) Rels}--instance Pretty RelMap where-  pretty (RelMap rm) = vcat (map prettyVar byVar)-    where-      vars = S.map fst (M.keysSet rm)-      byVar = map (\x -> (rm!(x,SubTy), x, rm!(x,SuperTy))) (S.toList vars)--      prettyVar (subs, x, sups) = hsep [prettyRel subs, "<:", pretty x, "<:", prettyRel sups]-      prettyRel rs = pretty (baseRels rs) <> ", " <> pretty (varRels rs)---- | Modify a @RelMap@ to record the fact that we have solved for a---   type variable.  In particular, delete the variable from the---   @RelMap@ as a key, and also update the relative sets of every---   other variable to remove this variable and add the base type we---   chose for it.-substRel :: Name Type -> BaseTy -> RelMap -> RelMap-substRel x ty-  = RelMap-  . M.delete (x,SuperTy)-  . M.delete (x,SubTy)-  . M.map (\r@(Rels b v) -> if x `S.member` v then Rels (S.insert ty b) (S.delete x v) else r)-  . unRelMap---- | Essentially dirtypesBySort vm rm dir t s x finds all the---   dir-types (sub- or super-) of t which have sort s, relative to---   the variables in x.  This is \overbar{T}_S^X (resp. \underbar...)---   from Traytel et al.-dirtypesBySort :: TyVarInfoMap -> RelMap -> Dir -> BaseTy -> Sort -> Set (Name Type) -> [BaseTy]-dirtypesBySort vm (RelMap relMap) dir t s x--    -- Keep only those supertypes t' of t-  = keep (dirtypes dir t) $ \t' ->-      -- which have the right sort, and such that-      hasSort t' s &&--      -- for all variables beta \in x,-      forAll x (\beta ->--        -- there is at least one type t'' which is a subtype of t'-        -- which would be a valid solution for beta, that is,-        exists (dirtypes (other dir) t') $ \t'' ->--          -- t'' has the sort beta is supposed to have, and-          hasSort t'' (getSort vm beta) &&--          -- t'' is a supertype of every base type predecessor of beta.-          forAll (baseRels (lkup "dirtypesBySort, beta rel" relMap (beta, other dir)))-            (isDirB dir t''))--    -- The above comments are written assuming dir = Super; of course,-    -- if dir = Sub then just swap "super" and "sub" everywhere.--  where-    forAll, exists :: Foldable t => t a -> (a -> Bool) -> Bool-    forAll = flip all-    exists = flip any-    keep   = flip filter---- | Sort-aware infimum or supremum.-limBySort :: TyVarInfoMap -> RelMap -> Dir -> [BaseTy] -> Sort -> Set (Name Type) -> Maybe BaseTy-limBySort vm rm dir ts s x-  = (\is -> find (\lim -> all (\u -> isDirB dir u lim) is) is)-  . isects-  . map (\t -> dirtypesBySort vm rm dir t s x)-  $ ts-  where-    isects = foldr1 intersect--lubBySort, glbBySort :: TyVarInfoMap -> RelMap -> [BaseTy] -> Sort -> Set (Name Type) -> Maybe BaseTy-lubBySort vm rm = limBySort vm rm SuperTy-glbBySort vm rm = limBySort vm rm SubTy---- | From the constraint graph, build the sets of sub- and super- base---   types of each type variable, as well as the sets of sub- and---   supertype variables.  For each type variable x in turn, try to---   find a common supertype of its base subtypes which is consistent---   with the sort of x and with the sorts of all its sub-variables,---   as well as symmetrically a common subtype of its supertypes, etc.---   Assign x one of the two: if it has only successors, assign it---   their inf; otherwise, assign it the sup of its predecessors.  If---   it has both, we have a choice of whether to assign it the sup of---   predecessors or inf of successors; both lead to a sound &---   complete algorithm.  We choose to assign it the sup of its---   predecessors in this case, since it seems nice to default to---   "simpler" types lower down in the subtyping chain.-solveGraph-  :: Members '[Fresh, Error SolveError, Output Message] r-  => TyVarInfoMap -> Graph UAtom -> Sem r S-solveGraph vm g = atomToTypeSubst . unifyWCC <$> go topRelMap-  where-    unifyWCC :: Substitution BaseTy -> Substitution Atom-    unifyWCC s = compose (map mkEquateSubst wccVarGroups) @@ fmap ABase s-      where-        wccVarGroups :: [Set (Name Type)]-        wccVarGroups  = map (S.map getVar) . filter (all uisVar) . applySubst s $ G.wcc g-        getVar (UV v) = v-        getVar (UB b) = error-          $ "Impossible! Base type " ++ show b ++ " in solveGraph.getVar"--        mkEquateSubst :: Set (Name Type) -> Substitution Atom-        mkEquateSubst = mkEquations . S.toList--        mkEquations (a:as) = Subst.fromList . map (\v -> (coerce v, AVar (U a))) $ as-        mkEquations []     = error "Impossible! Empty set of names in mkEquateSubst"--            -- After picking concrete base types for all the type-            -- variables we can, the only thing possibly remaining in-            -- the graph are components containing only type variables-            -- and no base types.  It is sound, and simplifies the-            -- generated types considerably, to simply unify any type-            -- variables which are related by subtyping constraints.-            -- That is, we collect all the type variables in each-            -- weakly connected component and unify them.-            ---            -- As an example where this final step makes a difference,-            -- consider a term like @\x. (\y.y) x@.  A fresh type-            -- variable is generated for the type of @x@, and another-            -- for the type of @y@; the application of @(\y.y)@ to @x@-            -- induces a subtyping constraint between the two type-            -- variables.  The most general type would be something-            -- like @forall a b. (a <: b) => a -> b@, but we want to-            -- avoid generating unnecessary subtyping constraints (the-            -- type system might not even support subtyping qualifiers-            -- like this).  Instead, we unify the two type variables-            -- and the resulting type is @forall a. a -> a@.--    -- Get the successor and predecessor sets for all the type variables.-    topRelMap :: RelMap-    topRelMap-      = RelMap-      . M.map (uncurry Rels . (S.fromAscList *** S.fromAscList)-               . partitionEithers . map uatomToEither . S.toList)-      $ M.mapKeys (,SuperTy) subMap `M.union` M.mapKeys (,SubTy) superMap--    subMap, superMap :: Map (Name Type) (Set UAtom)-    (subMap, superMap) = (onlyVars *** onlyVars) $ G.cessors g--    onlyVars :: Map UAtom (Set UAtom) -> Map (Name Type) (Set UAtom)-    onlyVars = M.mapKeys fromVar . M.filterWithKey (\a _ -> uisVar a)-      where-        fromVar (UV x) = x-        fromVar _      = error "Impossible! UB but uisVar."--    go-      :: Members '[Fresh, Error SolveError, Output Message] r-      => RelMap -> Sem r (Substitution BaseTy)-    go relMap@(RelMap rm) = debugPretty relMap >> case as of--      -- No variables left that have base type constraints.-      []    -> return idS--      -- Solve one variable at a time.  See below.-      (a:_) -> do-        debug $ "Solving for" <+> pretty' a-        case solveVar a of-          Nothing       -> do-            debug $ "Couldn't solve for" <+> pretty' a-            throw NoUnify--          -- If we solved for a, delete it from the maps, apply the-          -- resulting substitution to the remainder (updating the-          -- relMap appropriately), and recurse.  The substitution we-          -- want will be the composition of the substitution for a-          -- with the substitution generated by the recursive call.-          ---          -- Note we don't need to delete a from the TyVarInfoMap; we-          -- never use the set of keys from the TyVarInfoMap for-          -- anything (indeed, some variables might not be keys if-          -- they have an empty sort), so it doesn't matter if old-          -- variables hang around in it.-          Just s -> do-            debugPretty s-            (@@ s) <$> go (substRel a (fromJust $ Subst.lookup (coerce a) s) relMap)--      where-        -- NOTE we can't solve a bunch in parallel!  Might end up-        -- assigning them conflicting solutions if some depend on-        -- others.  For example, consider the situation-        ---        --            Z-        --            |-        --            a3-        --           /  \-        --          a1   N-        ---        -- If we try to solve in parallel we will end up assigning a1-        -- -> Z (since it only has base types as an upper bound) and-        -- a3 -> N (since it has both upper and lower bounds, and by-        -- default we pick the lower bound), but this is wrong since-        -- we should have a1 < a3.-        ---        -- If instead we solve them one at a time, we could e.g. first-        -- solve a1 -> Z, and then we would find a3 -> Z as well.-        -- Alternately, if we first solve a3 -> N then we will have a1-        -- -> N as well.  Both are acceptable.-        ---        -- In fact, this exact graph comes from (^x.x+1) which was-        -- erroneously being inferred to have type Z -> N when I first-        -- wrote the code.--        -- Get only the variables we can solve on this pass, which-        -- have base types in their predecessor or successor set.  If-        -- there are no such variables, then start picking any-        -- remaining variables with a sort and pick types for them-        -- (disco doesn't have qualified polymorphism so we can't just-        -- leave them).-        asBase-          = map fst-          . filter (not . S.null . baseRels . lkup "solveGraph.go.as" rm)-          $ M.keys rm-        as = case asBase of-          [] -> filter ((/= topSort) . getSort vm) . map fst $ M.keys rm-          _  -> asBase--        -- Solve for a variable, failing if it has no solution, otherwise returning-        -- a substitution for it.-        solveVar :: Name Type -> Maybe (Substitution BaseTy)-        solveVar v =-          case ((v,SuperTy), (v,SubTy)) & over both (S.toList . baseRels . lkup "solveGraph.solveVar" rm) of-            -- No sub- or supertypes; the only way this can happen is-            -- if it has a nontrivial sort.-            ---            -- Traytel et al. don't seem to have a rule saying what to-            -- do in this case (see Fig. 16 on p. 16 of their long-            -- version).  We used to just pick a type that inhabits-            -- the sort, but this is wrong; see-            -- https://github.com/disco-lang/disco/issues/192.-            ---            -- For now, let's assume that any situation in which we-            -- have no base sub- or supertypes but we do have-            -- nontrivial sorts means that we are dealing with numeric-            -- types; so we can just call N a base subtype and go from there.--            ([], []) ->-              -- Debug.trace (show v ++ " has no sub- or supertypes.  Assuming N as a subtype.")-              (coerce v |->) <$> lubBySort vm relMap [N] (getSort vm v)-                (varRels (lkup "solveVar none, rels" rm (v,SubTy)))--            -- Only supertypes.  Just assign a to their inf, if one exists.-            (bsupers, []) ->-              -- Debug.trace (show v ++ " has only supertypes (" ++ show bsupers ++ ")") $-              (coerce v |->) <$> glbBySort vm relMap bsupers (getSort vm v)-                (varRels (lkup "solveVar bsupers, rels" rm (v,SuperTy)))--            -- Only subtypes.  Just assign a to their sup.-            ([], bsubs)   ->-              -- Debug.trace (show v ++ " has only subtypes (" ++ show bsubs ++ ")") $-              -- Debug.trace ("sortmap: " ++ show vm) $-              -- Debug.trace ("relmap: " ++ show relMap) $-              -- Debug.trace ("sort for " ++ show v ++ ": " ++ show (getSort vm v)) $-              -- Debug.trace ("relvars: " ++ show (varRels (relMap ! (v,SubTy)))) $-              (coerce v |->) <$> lubBySort vm relMap bsubs (getSort vm v)-                (varRels (lkup "solveVar bsubs, rels" rm (v,SubTy)))--            -- Both successors and predecessors.  Both must have a-            -- valid bound, and the bounds must not overlap.  Assign a-            -- to the sup of its predecessors.-            (bsupers, bsubs) -> do-              ub <- glbBySort vm relMap bsupers (getSort vm v)-                      (varRels (rm ! (v,SuperTy)))-              lb <- lubBySort vm relMap bsubs   (getSort vm v)-                      (varRels (rm ! (v,SubTy)))-              case isSubB lb ub of-                True  -> Just (coerce v |-> lb)-                False -> Nothing+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++-----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- |+-- Module      :  Disco.Typecheck.Solve+-- Copyright   :  disco team and contributors+-- Maintainer  :  byorgey@gmail.com+--+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Constraint solver for the constraints generated during type+-- checking/inference.+module Disco.Typecheck.Solve where++import Unbound.Generics.LocallyNameless (+  Alpha,+  Name,+  Subst,+  fv,+  name2Integer,+  string2Name,+  substs,+ )++import Data.Coerce+import GHC.Generics (Generic)++import Control.Arrow ((&&&), (***))+import Control.Lens hiding (use, (%=), (.=))+import Control.Monad (unless, zipWithM)+import Data.Bifunctor (first, second)+import Data.Either (partitionEithers)+import Data.List (+  find,+  foldl',+  intersect,+  partition,+ )+import Data.Map (Map, (!))+import qualified Data.Map as M+import Data.Maybe (+  fromJust,+  fromMaybe,+  mapMaybe,+ )+import Data.Monoid (First (..))+import Data.Set (Set)+import qualified Data.Set as S+import Data.Tuple++import Disco.Effects.Fresh+import Disco.Effects.State+import Polysemy+import Polysemy.Error+import Polysemy.Input+import Polysemy.Output++import Disco.Messages+import Disco.Pretty hiding ((<>))+import Disco.Subst+import qualified Disco.Subst as Subst+import Disco.Typecheck.Constraints+import Disco.Typecheck.Graph (Graph)+import qualified Disco.Typecheck.Graph as G+import Disco.Typecheck.Unify+import Disco.Types+import Disco.Types.Qualifiers+import Disco.Types.Rules++--------------------------------------------------+-- Solver errors++-- | Type of errors which can be generated by the constraint solving+--   process.+data SolveError where+  NoWeakUnifier :: SolveError+  NoUnify :: SolveError+  UnqualBase :: Qualifier -> BaseTy -> SolveError+  Unqual :: Qualifier -> Type -> SolveError+  QualSkolem :: Qualifier -> Name Type -> SolveError+  deriving (Show)++instance Semigroup SolveError where+  e <> _ = e++--------------------------------------------------+-- Error utilities++runSolve :: Sem (Fresh ': Error SolveError ': r) a -> Sem r (Either SolveError a)+runSolve = runError . runFresh++-- | Run a list of actions, and return the results from those which do+--   not throw an error.  If all of them throw an error, rethrow the+--   first one.+filterErrors :: Member (Error e) r => [Sem r a] -> Sem r [a]+filterErrors ms = do+  es <- mapM try ms+  case partitionEithers es of+    (e : _, []) -> throw e+    (_, as) -> return as++-- | A variant of 'asum' which picks the first action that succeeds,+--   or re-throws the error of the last one if none of them+--   do. Precondition: the list must not be empty.+asum' :: Member (Error e) r => [Sem r a] -> Sem r a+asum' [] = error "Impossible: asum' []"+asum' [m] = m+asum' (m : ms) = m `catch` (\_ -> asum' ms)++--------------------------------------------------+-- Simple constraints++data SimpleConstraint where+  (:<:) :: Type -> Type -> SimpleConstraint+  (:=:) :: Type -> Type -> SimpleConstraint+  deriving (Show, Eq, Ord, Generic, Alpha, Subst Type)++instance Pretty SimpleConstraint where+  pretty = \case+    ty1 :<: ty2 -> pretty ty1 <+> "<:" <+> pretty ty2+    ty1 :=: ty2 -> pretty ty1 <+> "=" <+> pretty ty2++--------------------------------------------------+-- Simplifier types++-- Uses TH to generate lenses so it has to go here before other stuff.++---------------------------------+-- Variable maps++-- | Information about a particular type variable.  More information+--   may be added in the future (e.g. polarity).+data TyVarInfo = TVI+  { _tyVarIlk :: First Ilk+  -- ^ The ilk (unification or skolem) of the variable, if known+  , _tyVarSort :: Sort+  -- ^ The sort (set of qualifiers) of the type variable.+  }+  deriving (Show)++makeLenses ''TyVarInfo++instance Pretty TyVarInfo where+  pretty (TVI (First ilk) s) = maybe (pure "?") pretty ilk <> "%" <> pretty s++-- | Create a 'TyVarInfo' given an 'Ilk' and a 'Sort'.+mkTVI :: Ilk -> Sort -> TyVarInfo+mkTVI = TVI . First . Just++-- | We can learn different things about a type variable from+--   different places; the 'Semigroup' instance allows combining+--   information about a type variable into a single record.+instance Semigroup TyVarInfo where+  TVI i1 s1 <> TVI i2 s2 = TVI (i1 <> i2) (s1 <> s2)++-- | A 'TyVarInfoMap' records what we know about each type variable;+--   it is a mapping from type variable names to 'TyVarInfo' records.+newtype TyVarInfoMap = VM {unVM :: Map (Name Type) TyVarInfo}+  deriving (Show)++instance Pretty TyVarInfoMap where+  pretty (VM m) = pretty m++-- | Utility function for acting on a 'TyVarInfoMap' by acting on the+--   underlying 'Map'.+onVM ::+  (Map (Name Type) TyVarInfo -> Map (Name Type) TyVarInfo) ->+  TyVarInfoMap ->+  TyVarInfoMap+onVM f (VM m) = VM (f m)++-- | Look up a given variable name in a 'TyVarInfoMap'.+lookupVM :: Name Type -> TyVarInfoMap -> Maybe TyVarInfo+lookupVM v = M.lookup v . unVM++-- | Remove the mapping for a particular variable name (if it exists)+--   from a 'TyVarInfoMap'.+deleteVM :: Name Type -> TyVarInfoMap -> TyVarInfoMap+deleteVM = onVM . M.delete++-- | Given a list of type variable names, add them all to the+--   'TyVarInfoMap' as 'Skolem' variables (with a trivial sort).+addSkolems :: [Name Type] -> TyVarInfoMap -> TyVarInfoMap+addSkolems vs = onVM $ \vm -> foldl' (flip (\v -> M.insert v (mkTVI Skolem mempty))) vm vs++-- | The @Semigroup@ instance for 'TyVarInfoMap' unions the two maps,+--   combining the info records for any variables occurring in both+--   maps.+instance Semigroup TyVarInfoMap where+  VM sm1 <> VM sm2 = VM (M.unionWith (<>) sm1 sm2)++instance Monoid TyVarInfoMap where+  mempty = VM M.empty+  mappend = (<>)++-- | Get the sort of a particular variable recorded in a+--   'TyVarInfoMap'.  Returns the trivial (empty) sort for a variable+--   not in the map.+getSort :: TyVarInfoMap -> Name Type -> Sort+getSort (VM m) v = maybe topSort (view tyVarSort) (M.lookup v m)++-- | Get the 'Ilk' of a variable recorded in a 'TyVarInfoMap'.+--   Returns @Nothing@ if the variable is not in the map, or if its+--   ilk is not known.+getIlk :: TyVarInfoMap -> Name Type -> Maybe Ilk+getIlk (VM m) v = (m ^? ix v . tyVarIlk) >>= getFirst++-- | Extend the sort of a type variable by combining its existing sort+--   with the given one.  Has no effect if the variable is not already+--   in the map.+extendSort :: Name Type -> Sort -> TyVarInfoMap -> TyVarInfoMap+extendSort x s = onVM (at x . _Just . tyVarSort %~ (`S.union` s))++---------------------------------+-- Simplifier state++-- The simplification stage maintains a mutable state consisting of+-- the current qualifier map (containing wanted qualifiers for type+-- variables), the list of remaining SimpleConstraints, and the+-- current substitution.  It also keeps track of seen constraints, so+-- expansion of recursive types can stop when encountering a+-- previously seen constraint.+data SimplifyState = SS+  { _ssVarMap :: TyVarInfoMap+  , _ssConstraints :: [SimpleConstraint]+  , _ssSubst :: S+  , _ssSeen :: Set SimpleConstraint+  }++makeLenses ''SimplifyState++lkup :: (Ord k, Show k, Show (Map k a)) => String -> Map k a -> k -> a+lkup messg m k = fromMaybe (error errMsg) (M.lookup k m)+ where+  errMsg =+    unlines+      [ "Key lookup error:"+      , "  Key = " ++ show k+      , "  Map = " ++ show m+      , "  Location: " ++ messg+      ]++--------------------------------------------------+-- Top-level solver algorithm++solveConstraint ::+  Members '[Fresh, Error SolveError, Output (Message ann), Input TyDefCtx] r =>+  Constraint ->+  Sem r S+solveConstraint c = do+  -- Step 1. Open foralls (instantiating with skolem variables) and+  -- collect wanted qualifiers; also expand disjunctions.  Result in a+  -- list of possible constraint sets; each one consists of equational+  -- and subtyping constraints in addition to qualifiers.++  debug "Solving:"+  debugPretty c++  debug "------------------------------"+  debug "Decomposing constraints..."++  qcList <- decomposeConstraint c++  -- Now try continuing with each set and pick the first one that has+  -- a solution.+  asum' (map (uncurry solveConstraintChoice) qcList)++solveConstraintChoice ::+  Members '[Fresh, Error SolveError, Output (Message ann), Input TyDefCtx] r =>+  TyVarInfoMap ->+  [SimpleConstraint] ->+  Sem r S+solveConstraintChoice quals cs = do+  debugPretty quals+  debug $ vcat (map pretty' cs)++  tyDefns <- input @TyDefCtx++  -- Step 2. Check for weak unification to ensure termination. (a la+  -- Traytel et al).++  let toEqn (t1 :<: t2) = (t1, t2)+      toEqn (t1 :=: t2) = (t1, t2)+  _ <- note NoWeakUnifier $ weakUnify tyDefns (map toEqn cs)++  -- Step 3. Simplify constraints, resulting in a set of atomic+  -- subtyping constraints.  Also simplify/update qualifier set+  -- accordingly.++  debug "------------------------------"+  debug "Running simplifier..."++  (vm, atoms, theta_simp) <- simplify quals cs+  debug "Done running simplifier. Results:"++  debugPretty vm+  debug $ vcat $ map (pretty' . (\(x, y) -> TyAtom x :<: TyAtom y)) atoms+  debugPretty theta_simp++  -- Step 4. Turn the atomic constraints into a directed constraint+  -- graph.++  debug "------------------------------"+  debug "Generating constraint graph..."++  -- Some variables might have qualifiers but not participate in any+  -- equality or subtyping relations (see issue #153); make sure to+  -- extract them and include them in the constraint graph as isolated+  -- vertices+  let mkAVar (v, First (Just Skolem)) = AVar (S v)+      mkAVar (v, _) = AVar (U v)+      vars = S.fromList . map (mkAVar . second (view tyVarIlk)) . M.assocs . unVM $ vm+      g = mkConstraintGraph vars atoms++  debugPretty g++  -- Step 5.+  -- Check for any weakly connected components containing more+  -- than one skolem, or a skolem and a base type; such components are+  -- not allowed.  Other WCCs with a single skolem simply unify to+  -- that skolem.++  debug "------------------------------"+  debug "Checking WCCs for skolems..."++  (g', theta_skolem) <- checkSkolems vm g+  debugPretty theta_skolem++  -- We don't need to ensure that theta_skolem respects sorts since+  -- checkSkolems will only unify skolem vars with unsorted variables.++  -- Step 6. Eliminate cycles from the graph, turning each strongly+  -- connected component into a single node, unifying all the atoms in+  -- each component.++  debug "------------------------------"+  debug "Collapsing SCCs..."++  (g'', theta_cyc) <- elimCycles tyDefns g'++  debugPretty g''+  debugPretty theta_cyc++  -- Check that the resulting substitution respects sorts...+  let sortOK (x, TyAtom (ABase ty)) = hasSort ty (getSort vm x)+      sortOK (_, TyAtom (AVar (U _))) = True+      sortOK p = error $ "Impossible! sortOK " ++ show p+  unless (all sortOK (Subst.toList theta_cyc)) $+    throw NoUnify++  -- ... and update the sort map if we unified any type variables.+  -- Just make sure that if theta_cyc maps x |-> y, then y picks up+  -- the sort of x.++  debug "Old sort map:"+  debugPretty vm++  let vm' = foldr updateVarMap vm (Subst.toList theta_cyc)+       where+        updateVarMap :: (Name Type, Type) -> TyVarInfoMap -> TyVarInfoMap+        updateVarMap (x, TyAtom (AVar (U y))) vmm = extendSort y (getSort vmm x) vmm+        updateVarMap _ vmm = vmm++  debug "Updated sort map:"+  debugPretty vm++  debug "------------------------------"+  debug "Checking edges between base types..."++  -- Step 6b. Collapsing SCCs can create some edges between base+  -- types.  Check that any such edges are consistent, then remove+  -- them, since they no longer give us any information about type+  -- variables.  See https://github.com/disco-lang/disco/issues/357.++  g''' <- checkBaseEdges g''++  -- Steps 7 & 8: solve the graph, iteratively finding satisfying+  -- assignments for each type variable based on its successor and+  -- predecessor base types in the graph; then unify all the type+  -- variables in any remaining weakly connected components.++  debug "------------------------------"+  debug "Solving for type variables..."++  theta_sol <- solveGraph vm' g'''+  debugPretty theta_sol++  debug "------------------------------"+  debug "Composing final substitution..."++  let theta_final = theta_sol @@ theta_cyc @@ theta_skolem @@ theta_simp+  debugPretty theta_final++  return theta_final++--------------------------------------------------+-- Step 1. Constraint decomposition.++decomposeConstraint ::+  Members '[Fresh, Error SolveError, Input TyDefCtx] r =>+  Constraint ->+  Sem r [(TyVarInfoMap, [SimpleConstraint])]+decomposeConstraint (CSub t1 t2) = return [(mempty, [t1 :<: t2])]+decomposeConstraint (CEq t1 t2) = return [(mempty, [t1 :=: t2])]+decomposeConstraint (CQual q ty) = (: []) . (,[]) <$> decomposeQual ty q+decomposeConstraint (CAnd cs) = map mconcat . sequence <$> mapM decomposeConstraint cs+decomposeConstraint CTrue = return [mempty]+decomposeConstraint (CAll ty) = do+  (vars, c) <- unbind ty+  let c' = substs (mkSkolems vars) c+  (map . first . addSkolems) vars <$> decomposeConstraint c'+ where+  mkSkolems :: [Name Type] -> [(Name Type, Type)]+  mkSkolems = map (id &&& TySkolem)+decomposeConstraint (COr cs) = concat <$> filterErrors (map decomposeConstraint cs)++decomposeQual ::+  Members '[Fresh, Error SolveError, Input TyDefCtx] r =>+  Type ->+  Qualifier ->+  Sem r TyVarInfoMap+decomposeQual = go S.empty+ where+  go ::+    Members '[Fresh, Error SolveError, Input TyDefCtx] r =>+    Set (String, [Type], Qualifier) ->+    Type ->+    Qualifier ->+    Sem r TyVarInfoMap++  -- For a type atom, call out to checkQual.+  go _ (TyAtom a) q = checkQual q a+  -- Coinductively check user-defined types for a qualifier.  Keep+  -- track of a set of user-defined types and qualifiers we have+  -- seen.  Every time we encounter a new one, add it to the set and+  -- recurse on its unfolding.  If we ever encounter one we have+  -- already seen, we can assume by coinduction that the qualifier+  -- is satisfied.+  go seen (TyCon (CUser t) tys) q = do+    case (t, tys, q) `S.member` seen of+      True -> return mempty+      False -> do+        tyDefns <- input @TyDefCtx+        case M.lookup t tyDefns of+          Nothing -> error $ show t ++ " not in ty defn map!!"+          Just (TyDefBody _ body) -> do+            let ty' = body tys+            go (S.insert (t, tys, q) seen) ty' q++  -- If we have a container type where the container is still a variable,+  -- just replace it with List for the purposes of generating constraints---+  -- all containers (lists, bags, sets) have the same qualifier rules.+  go seen (TyCon (CContainer (AVar _)) tys) q = go seen (TyCon CList tys) q+  -- Otherwise, decompose a type constructor according to the qualRules.+  go seen ty@(TyCon c tys) q = case qualRules c q of+    Nothing -> throw $ Unqual q ty+    Just qs -> mconcat <$> zipWithM (maybe (return mempty) . go seen) tys qs++checkQual ::+  Members '[Fresh, Error SolveError] r =>+  Qualifier ->+  Atom ->+  Sem r TyVarInfoMap+checkQual q (AVar (U v)) = return . VM . M.singleton v $ mkTVI Unification (S.singleton q)+checkQual q (AVar (S v)) = throw $ QualSkolem q v+checkQual q (ABase bty) =+  case hasQual bty q of+    True -> return mempty+    False -> throw $ UnqualBase q bty++--------------------------------------------------+-- Step 3. Constraint simplification.++-- | This step does unification of equality constraints, as well as+--   structural decomposition of subtyping constraints.  For example,+--   if we have a constraint (x -> y) <: (z -> Int), then we can+--   decompose it into two constraints, (z <: x) and (y <: Int); if we+--   have a constraint v <: (a,b), then we substitute v ↦ (x,y) (where+--   x and y are fresh type variables) and continue; and so on.+--+--   After this step, the remaining constraints will all be atomic+--   constraints, that is, only of the form (v1 <: v2), (v <: b), or+--   (b <: v), where v is a type variable and b is a base type.+simplify ::+  Members '[Error SolveError, Output (Message ann), Input TyDefCtx] r =>+  TyVarInfoMap ->+  [SimpleConstraint] ->+  Sem r (TyVarInfoMap, [(Atom, Atom)], S)+simplify origVM cs =+  (\(SS vm' cs' s' _) -> (vm', map extractAtoms cs', s'))+    -- contFreshMT :: Monad m => FreshMT m a -> Integer -> m a+    -- "Run a FreshMT computation given a starting index for fresh name generation."+    <$> runFresh' n (execState (SS origVM cs idS S.empty) simplify')+ where+  fvNums :: Alpha a => [a] -> [Integer]+  fvNums = map (name2Integer :: Name Type -> Integer) . toListOf fv++  -- Find first unused integer in constraint free vars and sort map+  -- domain, and use it to start the fresh var generation, so we don't+  -- generate any "fresh" names that interfere with existing names+  n1 = maximum0 . fvNums $ cs+  n = succ . maximum . (n1 :) . fvNums . M.keys . unVM $ origVM++  maximum0 [] = 0+  maximum0 xs = maximum xs++  -- Extract the type atoms from an atomic constraint.+  extractAtoms :: SimpleConstraint -> (Atom, Atom)+  extractAtoms (TyAtom a1 :<: TyAtom a2) = (a1, a2)+  extractAtoms c = error $ "Impossible: simplify left non-atomic or non-subtype constraint " ++ show c++  -- Iterate picking one simplifiable constraint and simplifying it+  -- until none are left.+  simplify' ::+    Members '[State SimplifyState, Fresh, Error SolveError, Output (Message ann), Input TyDefCtx] r =>+    Sem r ()+  simplify' = do+    -- q <- gets fst+    -- debug (pretty q)+    -- debug ""++    mc <- pickSimplifiable+    case mc of+      Nothing -> return ()+      Just s -> do+        debug $ "Simplifying:" <+> pretty' s++        simplifyOne s+        simplify'++  -- Pick out one simplifiable constraint, removing it from the list+  -- of constraints in the state.  Return Nothing if no more+  -- constraints can be simplified.+  pickSimplifiable ::+    Members '[State SimplifyState, Fresh, Error SolveError] r =>+    Sem r (Maybe SimpleConstraint)+  pickSimplifiable = do+    curCs <- use ssConstraints+    case pick simplifiable curCs of+      Nothing -> return Nothing+      Just (a, as) -> do+        ssConstraints .= as+        return (Just a)++  -- Pick the first element from a list satisfying the given+  -- predicate, returning the element and the list with the element+  -- removed.+  pick :: (a -> Bool) -> [a] -> Maybe (a, [a])+  pick _ [] = Nothing+  pick p (a : as)+    | p a = Just (a, as)+    | otherwise = second (a :) <$> pick p as++  -- Check if a constraint can be simplified.  An equality+  -- constraint can always be "simplified" via unification.  A+  -- subtyping constraint can be simplified if either it involves a+  -- type constructor (in which case we can decompose it), or if it+  -- involves two base types (in which case it can be removed if the+  -- relationship holds).+  simplifiable :: SimpleConstraint -> Bool+  simplifiable (_ :=: _) = True+  simplifiable (TyCon {} :<: TyCon {}) = True+  simplifiable (TyVar {} :<: TyCon {}) = True+  simplifiable (TyCon {} :<: TyVar {}) = True+  simplifiable (TyCon (CUser _) _ :<: _) = True+  simplifiable (_ :<: TyCon (CUser _) _) = True+  simplifiable (TyAtom (ABase _) :<: TyAtom (ABase _)) = True+  simplifiable _ = False++  -- Simplify the given simplifiable constraint.  If the constraint+  -- has already been seen before (due to expansion of a recursive+  -- type), just throw it away and stop.+  simplifyOne ::+    Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r =>+    SimpleConstraint ->+    Sem r ()+  simplifyOne c = do+    seen <- use ssSeen+    case c `S.member` seen of+      True -> return ()+      False -> do+        ssSeen %= S.insert c+        simplifyOne' c++  simplifyOne' ::+    Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r =>+    SimpleConstraint ->+    Sem r ()++  -- If we have an equality constraint, run unification on it.  The+  -- resulting substitution is applied to the remaining constraints+  -- as well as prepended to the current substitution.++  simplifyOne' (ty1 :=: ty2) = do+    tyDefns <- input @TyDefCtx+    case unify tyDefns [(ty1, ty2)] of+      Nothing -> throw NoUnify+      Just s' -> extendSubst s'++  -- If we see a constraint of the form (T <: a), where T is a+  -- user-defined type and a is a type variable, then just turn it+  -- into an equality (T = a).  This is sound but probably not+  -- complete.  The alternative seems quite complicated, possibly+  -- even undecidable.  See https://github.com/disco-lang/disco/issues/207 .+  simplifyOne' (ty1@(TyCon (CUser _) _) :<: ty2@TyVar {}) =+    simplifyOne' (ty1 :=: ty2)+  -- Otherwise, expand the user-defined type and continue.+  simplifyOne' (TyCon (CUser t) ts :<: ty2) = do+    tyDefns <- input @TyDefCtx+    case M.lookup t tyDefns of+      Nothing -> error $ show t ++ " not in ty defn map!"+      Just (TyDefBody _ body) ->+        ssConstraints %= ((body ts :<: ty2) :)++  -- Turn  a <: T  into  a = T.  See comment above.+  simplifyOne' (ty1@TyVar {} :<: ty2@(TyCon (CUser _) _)) =+    simplifyOne' (ty1 :=: ty2)+  simplifyOne' (ty1 :<: TyCon (CUser t) ts) = do+    tyDefns <- input @TyDefCtx+    case M.lookup t tyDefns of+      Nothing -> error $ show t ++ " not in ty defn map!"+      Just (TyDefBody _ body) ->+        ssConstraints %= ((ty1 :<: body ts) :)++  -- Given a subtyping constraint between two type constructors,+  -- decompose it if the constructors are the same (or fail if they+  -- aren't), taking into account the variance of each argument to+  -- the constructor.  Container types are a special case;+  -- recursively generate a subtyping constraint for their+  -- constructors as well.+  simplifyOne' (TyCon c1@(CContainer ctr1) tys1 :<: TyCon (CContainer ctr2) tys2) =+    ssConstraints+      %= ( ( (TyAtom ctr1 :<: TyAtom ctr2)+              : zipWith3 variance (arity c1) tys1 tys2+           )+            +++         )+  simplifyOne' (TyCon c1 tys1 :<: TyCon c2 tys2)+    | c1 /= c2 = throw NoUnify+    | otherwise =+        ssConstraints %= (zipWith3 variance (arity c1) tys1 tys2 ++)+  -- Given a subtyping constraint between a variable and a type+  -- constructor, expand the variable into the same constructor+  -- applied to fresh type variables.+  simplifyOne' con@(TyVar a :<: TyCon c _) = expandStruct a c con+  simplifyOne' con@(TyCon c _ :<: TyVar a) = expandStruct a c con+  -- Given a subtyping constraint between two base types, just check+  -- whether the first is indeed a subtype of the second.  (Note+  -- that we only pattern match here on type atoms, which could+  -- include variables, but this will only ever get called if+  -- 'simplifiable' was true, which checks that both are base+  -- types.)+  simplifyOne' (TyAtom (ABase b1) :<: TyAtom (ABase b2)) = do+    case isSubB b1 b2 of+      True -> return ()+      False -> throw NoUnify+  simplifyOne' (s :<: t) =+    error $ "Impossible! simplifyOne' " ++ show s ++ " :<: " ++ show t++  expandStruct ::+    Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r =>+    Name Type ->+    Con ->+    SimpleConstraint ->+    Sem r ()+  expandStruct a c con = do+    as <- mapM (const (TyVar <$> fresh (string2Name "a"))) (arity c)+    let s' = a |-> TyCon c as+    ssConstraints %= (con :)+    extendSubst s'++  -- 1. compose s' with current subst+  -- 2. apply s' to constraints+  -- 3. apply s' to qualifier map and decompose+  extendSubst ::+    Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r =>+    S ->+    Sem r ()+  extendSubst s' = do+    ssSubst %= (s' @@)+    ssConstraints %= applySubst s'+    substVarMap s'++  substVarMap ::+    Members '[State SimplifyState, Fresh, Error SolveError, Input TyDefCtx] r =>+    S ->+    Sem r ()+  substVarMap s' = do+    vm <- use ssVarMap++    -- 1. Get quals for each var in domain of s' and match them with+    -- the types being substituted for those vars.++    let tySorts :: [(Type, Sort)]+        tySorts = map (second (view tyVarSort)) . mapMaybe (traverse (`lookupVM` vm) . swap) $ Subst.toList s'++        tyQualList :: [(Type, Qualifier)]+        tyQualList = concatMap (sequenceA . second S.toList) tySorts++    -- 2. Decompose the resulting qualifier constraints++    vm' <- mconcat <$> mapM (uncurry decomposeQual) tyQualList++    -- 3. delete domain of s' from vm and merge in decomposed quals.++    ssVarMap .= vm' <> foldl' (flip deleteVM) vm (dom s')++  -- The above works even when unifying two variables.  Say we have+  -- the TyVarInfoMap+  --+  --   a |-> {add}+  --   b |-> {sub}+  --+  -- and we get back theta = [a |-> b].  The domain of theta+  -- consists solely of a, so we look up a in the TyVarInfoMap and get+  -- {add}.  We therefore generate the constraint 'add (theta a)'+  -- = 'add b' which can't be decomposed at all, and hence yields+  -- the TyVarInfoMap {b |-> {add}}.  We then delete a from the+  -- original TyVarInfoMap and merge the result with the new TyVarInfoMap,+  -- yielding {b |-> {sub,add}}.++  -- Create a subtyping constraint based on the variance of a type+  -- constructor argument position: in the usual order for+  -- covariant, and reversed for contravariant.+  variance Co ty1 ty2 = ty1 :<: ty2+  variance Contra ty1 ty2 = ty2 :<: ty1++--------------------------------------------------+-- Step 4: Build constraint graph++-- | Given a list of atoms and atomic subtype constraints (each pair+--   @(a1,a2)@ corresponds to the constraint @a1 <: a2@) build the+--   corresponding constraint graph.+mkConstraintGraph :: (Show a, Ord a) => Set a -> [(a, a)] -> Graph a+mkConstraintGraph as cs = G.mkGraph nodes (S.fromList cs)+ where+  nodes = as `S.union` S.fromList (cs ^.. traverse . each)++--------------------------------------------------+-- Step 5: Check skolems++-- | Check for any weakly connected components containing more than+--   one skolem, or a skolem and a base type, or a skolem and any+--   variables with nontrivial sorts; such components are not allowed.+--   If there are any WCCs with a single skolem, no base types, and+--   only unsorted variables, just unify them all with the skolem and+--   remove those components.+checkSkolems ::+  Members '[Error SolveError, Output (Message ann), Input TyDefCtx] r =>+  TyVarInfoMap ->+  Graph Atom ->+  Sem r (Graph UAtom, S)+checkSkolems vm graph = do+  let skolemWCCs :: [Set Atom]+      skolemWCCs = filter (any isSkolem) $ G.wcc graph++      ok wcc =+        S.size (S.filter isSkolem wcc) <= 1+          && all+            ( \case+                ABase _ -> False+                AVar (S _) -> True+                AVar (U v) -> maybe True (S.null . view tyVarSort) (lookupVM v vm)+            )+            wcc++      (good, bad) = partition ok skolemWCCs++  unless (null bad) $ throw NoUnify++  -- take all good sets and+  --   (1) delete them from the graph+  --   (2) unify them all with the skolem+  unifyWCCs graph idS good+ where+  noSkolems :: Atom -> UAtom+  noSkolems (ABase b) = UB b+  noSkolems (AVar (U v)) = UV v+  noSkolems (AVar (S v)) = error $ "Skolem " ++ show v ++ " remaining in noSkolems"++  unifyWCCs ::+    Members '[Error SolveError, Output (Message ann), Input TyDefCtx] r =>+    Graph Atom ->+    S ->+    [Set Atom] ->+    Sem r (Graph UAtom, S)+  unifyWCCs g s [] = return (G.map noSkolems g, s)+  unifyWCCs g s (u : us) = do+    debug $ "Unifying" <+> pretty' (u : us) <> "..."++    tyDefns <- input @TyDefCtx++    let g' = foldl' (flip G.delete) g u++        ms' = unifyAtoms tyDefns (S.toList u)+    case ms' of+      Nothing -> throw NoUnify+      Just s' -> unifyWCCs g' (atomToTypeSubst s' @@ s) us++--------------------------------------------------+-- Step 6: Eliminate cycles++-- | Eliminate cycles in the constraint set by collapsing each+--   strongly connected component to a single node, (unifying all the+--   types in the SCC). A strongly connected component is a maximal+--   set of nodes where every node is reachable from every other by a+--   directed path; since we are using directed edges to indicate a+--   subtyping constraint, this means every node must be a subtype of+--   every other, and the only way this can happen is if all are in+--   fact equal.+--+--   Of course, this step can fail if the types in a SCC are not+--   unifiable.  If it succeeds, it returns the collapsed graph (which+--   is now guaranteed to be acyclic, i.e. a DAG) and a substitution.+elimCycles ::+  Members '[Error SolveError] r =>+  TyDefCtx ->+  Graph UAtom ->+  Sem r (Graph UAtom, S)+elimCycles tyDefns = elimCyclesGen uatomToTypeSubst (unifyUAtoms tyDefns)++elimCyclesGen ::+  forall a b r.+  (Subst a a, Ord a, Members '[Error SolveError] r) =>+  (Substitution a -> Substitution b) ->+  ([a] -> Maybe (Substitution a)) ->+  Graph a ->+  Sem r (Graph a, Substitution b)+elimCyclesGen genSubst genUnify g =+  note NoUnify $+    (G.map fst &&& (genSubst . compose . S.map snd . G.nodes)) <$> g'+ where+  g' :: Maybe (Graph (a, Substitution a))+  g' = G.sequenceGraph $ G.map unifySCC (G.condensation g)++  unifySCC :: Set a -> Maybe (a, Substitution a)+  unifySCC uatoms = case S.toList uatoms of+    [] -> error "Impossible! unifySCC on the empty set"+    as@(a : _) -> (flip applySubst a &&& id) <$> genUnify as++------------------------------------------------------------+-- Step 6a: check base type edges+------------------------------------------------------------++isBaseEdge :: (UAtom, UAtom) -> Either (BaseTy, BaseTy) (UAtom, UAtom)+isBaseEdge (UB b1, UB b2) = Left (b1, b2)+isBaseEdge e = Right e++checkBaseEdge :: Members '[Error SolveError] r => (BaseTy, BaseTy) -> Sem r ()+checkBaseEdge (b1, b2)+  | isSubB b1 b2 = return ()+  | otherwise = throw NoUnify++checkBaseEdges :: Members '[Error SolveError] r => Graph UAtom -> Sem r (Graph UAtom)+checkBaseEdges g = do+  let (baseEdges, varEdges) = partitionEithers . map isBaseEdge . S.toList . G.edges $ g+  mapM_ checkBaseEdge baseEdges+  return $ G.mkGraph (G.nodes g) (S.fromList varEdges)++------------------------------------------------------------+-- Steps 7 and 8: Constraint resolution+------------------------------------------------------------++-- | Rels stores the set of base types and variables related to a+--   given variable in the constraint graph (either predecessors or+--   successors, but not both).+data Rels = Rels+  { baseRels :: Set BaseTy+  , varRels :: Set (Name Type)+  }+  deriving (Show, Eq)++-- | A RelMap associates each variable to its sets of base type and+--   variable predecessors and successors in the constraint graph.+newtype RelMap = RelMap {unRelMap :: Map (Name Type, Dir) Rels}++instance Pretty RelMap where+  pretty (RelMap rm) = vcat (map prettyVar byVar)+   where+    vars = S.map fst (M.keysSet rm)+    byVar = map (\x -> (rm ! (x, SubTy), x, rm ! (x, SuperTy))) (S.toList vars)++    prettyVar (subs, x, sups) = hsep [prettyRel subs, "<:", pretty x, "<:", prettyRel sups]+    prettyRel rs = pretty (baseRels rs) <> ", " <> pretty (varRels rs)++-- | Modify a @RelMap@ to record the fact that we have solved for a+--   type variable.  In particular, delete the variable from the+--   @RelMap@ as a key, and also update the relative sets of every+--   other variable to remove this variable and add the base type we+--   chose for it.+substRel :: Name Type -> BaseTy -> RelMap -> RelMap+substRel x ty =+  RelMap+    . M.delete (x, SuperTy)+    . M.delete (x, SubTy)+    . M.map (\r@(Rels b v) -> if x `S.member` v then Rels (S.insert ty b) (S.delete x v) else r)+    . unRelMap++-- | Essentially dirtypesBySort vm rm dir t s x finds all the+--   dir-types (sub- or super-) of t which have sort s, relative to+--   the variables in x.  This is \overbar{T}_S^X (resp. \underbar...)+--   from Traytel et al.+dirtypesBySort :: TyVarInfoMap -> RelMap -> Dir -> BaseTy -> Sort -> Set (Name Type) -> [BaseTy]+dirtypesBySort vm (RelMap relMap) dir t s x =+  -- Keep only those supertypes t' of t+  keep (dirtypes dir t) $ \t' ->+    -- which have the right sort, and such that+    hasSort t' s+      &&+      -- for all variables beta \in x,+      forAll+        x+        ( \beta ->+            -- there is at least one type t'' which is a subtype of t'+            -- which would be a valid solution for beta, that is,+            exists (dirtypes (other dir) t') $ \t'' ->+              -- t'' has the sort beta is supposed to have, and+              hasSort t'' (getSort vm beta)+                &&+                -- t'' is a supertype of every base type predecessor of beta.+                forAll+                  (baseRels (lkup "dirtypesBySort, beta rel" relMap (beta, other dir)))+                  (isDirB dir t'')+        )+ where+  -- The above comments are written assuming dir = Super; of course,+  -- if dir = Sub then just swap "super" and "sub" everywhere.++  forAll, exists :: Foldable t => t a -> (a -> Bool) -> Bool+  forAll = flip all+  exists = flip any+  keep = flip filter++-- | Sort-aware infimum or supremum.+limBySort :: TyVarInfoMap -> RelMap -> Dir -> [BaseTy] -> Sort -> Set (Name Type) -> Maybe BaseTy+limBySort vm rm dir ts s x =+  (\is -> find (\lim -> all (\u -> isDirB dir u lim) is) is)+    . isects+    . map (\t -> dirtypesBySort vm rm dir t s x)+    $ ts+ where+  isects = foldr1 intersect++lubBySort, glbBySort :: TyVarInfoMap -> RelMap -> [BaseTy] -> Sort -> Set (Name Type) -> Maybe BaseTy+lubBySort vm rm = limBySort vm rm SuperTy+glbBySort vm rm = limBySort vm rm SubTy++-- | From the constraint graph, build the sets of sub- and super- base+--   types of each type variable, as well as the sets of sub- and+--   supertype variables.  For each type variable x in turn, try to+--   find a common supertype of its base subtypes which is consistent+--   with the sort of x and with the sorts of all its sub-variables,+--   as well as symmetrically a common subtype of its supertypes, etc.+--   Assign x one of the two: if it has only successors, assign it+--   their inf; otherwise, assign it the sup of its predecessors.  If+--   it has both, we have a choice of whether to assign it the sup of+--   predecessors or inf of successors; both lead to a sound &+--   complete algorithm.  We choose to assign it the sup of its+--   predecessors in this case, since it seems nice to default to+--   "simpler" types lower down in the subtyping chain.+solveGraph ::+  Members '[Fresh, Error SolveError, Output (Message ann)] r =>+  TyVarInfoMap ->+  Graph UAtom ->+  Sem r S+solveGraph vm g = atomToTypeSubst . unifyWCC <$> go topRelMap+ where+  unifyWCC :: Substitution BaseTy -> Substitution Atom+  unifyWCC s = compose (map mkEquateSubst wccVarGroups) @@ fmap ABase s+   where+    wccVarGroups :: [Set (Name Type)]+    wccVarGroups = map (S.map getVar) . filter (all uisVar) . applySubst s $ G.wcc g+    getVar (UV v) = v+    getVar (UB b) =+      error $+        "Impossible! Base type " ++ show b ++ " in solveGraph.getVar"++    mkEquateSubst :: Set (Name Type) -> Substitution Atom+    mkEquateSubst = mkEquations . S.toList++    mkEquations (a : as) = Subst.fromList . map (\v -> (coerce v, AVar (U a))) $ as+    mkEquations [] = error "Impossible! Empty set of names in mkEquateSubst"++  -- After picking concrete base types for all the type+  -- variables we can, the only thing possibly remaining in+  -- the graph are components containing only type variables+  -- and no base types.  It is sound, and simplifies the+  -- generated types considerably, to simply unify any type+  -- variables which are related by subtyping constraints.+  -- That is, we collect all the type variables in each+  -- weakly connected component and unify them.+  --+  -- As an example where this final step makes a difference,+  -- consider a term like @\x. (\y.y) x@.  A fresh type+  -- variable is generated for the type of @x@, and another+  -- for the type of @y@; the application of @(\y.y)@ to @x@+  -- induces a subtyping constraint between the two type+  -- variables.  The most general type would be something+  -- like @forall a b. (a <: b) => a -> b@, but we want to+  -- avoid generating unnecessary subtyping constraints (the+  -- type system might not even support subtyping qualifiers+  -- like this).  Instead, we unify the two type variables+  -- and the resulting type is @forall a. a -> a@.++  -- Get the successor and predecessor sets for all the type variables.+  topRelMap :: RelMap+  topRelMap =+    RelMap+      . M.map+        ( uncurry Rels+            . (S.fromAscList *** S.fromAscList)+            . partitionEithers+            . map uatomToEither+            . S.toList+        )+      $ M.mapKeys (,SuperTy) subMap `M.union` M.mapKeys (,SubTy) superMap++  subMap, superMap :: Map (Name Type) (Set UAtom)+  (subMap, superMap) = (onlyVars *** onlyVars) $ G.cessors g++  onlyVars :: Map UAtom (Set UAtom) -> Map (Name Type) (Set UAtom)+  onlyVars = M.mapKeys fromVar . M.filterWithKey (\a _ -> uisVar a)+   where+    fromVar (UV x) = x+    fromVar _ = error "Impossible! UB but uisVar."++  go ::+    Members '[Fresh, Error SolveError, Output (Message ann)] r =>+    RelMap ->+    Sem r (Substitution BaseTy)+  go relMap@(RelMap rm) =+    debugPretty relMap >> case as of+      -- No variables left that have base type constraints.+      [] -> return idS+      -- Solve one variable at a time.  See below.+      (a : _) -> do+        debug $ "Solving for" <+> pretty' a+        case solveVar a of+          Nothing -> do+            debug $ "Couldn't solve for" <+> pretty' a+            throw NoUnify++          -- If we solved for a, delete it from the maps, apply the+          -- resulting substitution to the remainder (updating the+          -- relMap appropriately), and recurse.  The substitution we+          -- want will be the composition of the substitution for a+          -- with the substitution generated by the recursive call.+          --+          -- Note we don't need to delete a from the TyVarInfoMap; we+          -- never use the set of keys from the TyVarInfoMap for+          -- anything (indeed, some variables might not be keys if+          -- they have an empty sort), so it doesn't matter if old+          -- variables hang around in it.+          Just s -> do+            debugPretty s+            (@@ s) <$> go (substRel a (fromJust $ Subst.lookup (coerce a) s) relMap)+   where+    -- NOTE we can't solve a bunch in parallel!  Might end up+    -- assigning them conflicting solutions if some depend on+    -- others.  For example, consider the situation+    --+    --            Z+    --            |+    --            a3+    --           /  \+    --          a1   N+    --+    -- If we try to solve in parallel we will end up assigning a1+    -- -> Z (since it only has base types as an upper bound) and+    -- a3 -> N (since it has both upper and lower bounds, and by+    -- default we pick the lower bound), but this is wrong since+    -- we should have a1 < a3.+    --+    -- If instead we solve them one at a time, we could e.g. first+    -- solve a1 -> Z, and then we would find a3 -> Z as well.+    -- Alternately, if we first solve a3 -> N then we will have a1+    -- -> N as well.  Both are acceptable.+    --+    -- In fact, this exact graph comes from (^x.x+1) which was+    -- erroneously being inferred to have type Z -> N when I first+    -- wrote the code.++    -- Get only the variables we can solve on this pass, which+    -- have base types in their predecessor or successor set.  If+    -- there are no such variables, then start picking any+    -- remaining variables with a sort and pick types for them+    -- (disco doesn't have qualified polymorphism so we can't just+    -- leave them).+    asBase =+      map fst+        . filter (not . S.null . baseRels . lkup "solveGraph.go.as" rm)+        $ M.keys rm+    as = case asBase of+      [] -> filter ((/= topSort) . getSort vm) . map fst $ M.keys rm+      _ -> asBase++    -- Solve for a variable, failing if it has no solution, otherwise returning+    -- a substitution for it.+    solveVar :: Name Type -> Maybe (Substitution BaseTy)+    solveVar v =+      case ((v, SuperTy), (v, SubTy)) & over both (S.toList . baseRels . lkup "solveGraph.solveVar" rm) of+        -- No sub- or supertypes; the only way this can happen is+        -- if it has a nontrivial sort.+        --+        -- Traytel et al. don't seem to have a rule saying what to+        -- do in this case (see Fig. 16 on p. 16 of their long+        -- version).  We used to just pick a type that inhabits+        -- the sort, but this is wrong; see+        -- https://github.com/disco-lang/disco/issues/192.+        --+        -- If the sort is 'bool', we'll pick the Boolean base+        -- type, since there are no other sorts which could cause+        -- a conflict as in #192.+        --+        -- Otherwise, we assume that any situation in which we+        -- have no base sub- or supertypes but we do have+        -- nontrivial sorts means that we are dealing with numeric+        -- types; so we can just call N a base subtype and go from+        -- there.++        ([], []) ->+          if getSort vm v == S.fromList [QBool]+            then Just (coerce v |-> B)+            else -- Debug.trace (show v ++ " has no sub- or supertypes.  Assuming N as a subtype.")++              (coerce v |->)+                <$> lubBySort+                  vm+                  relMap+                  [N]+                  (getSort vm v)+                  (varRels (lkup "solveVar none, rels" rm (v, SubTy)))+        -- Only supertypes.  Just assign a to their inf, if one exists.+        (bsupers, []) ->+          -- Debug.trace (show v ++ " has only supertypes (" ++ show bsupers ++ ")") $+          (coerce v |->)+            <$> glbBySort+              vm+              relMap+              bsupers+              (getSort vm v)+              (varRels (lkup "solveVar bsupers, rels" rm (v, SuperTy)))+        -- Only subtypes.  Just assign a to their sup.+        ([], bsubs) ->+          -- Debug.trace (show v ++ " has only subtypes (" ++ show bsubs ++ ")") $+          -- Debug.trace ("sortmap: " ++ show vm) $+          -- Debug.trace ("relmap: " ++ show relMap) $+          -- Debug.trace ("sort for " ++ show v ++ ": " ++ show (getSort vm v)) $+          -- Debug.trace ("relvars: " ++ show (varRels (relMap ! (v,SubTy)))) $+          (coerce v |->)+            <$> lubBySort+              vm+              relMap+              bsubs+              (getSort vm v)+              (varRels (lkup "solveVar bsubs, rels" rm (v, SubTy)))+        -- Both successors and predecessors.  Both must have a+        -- valid bound, and the bounds must not overlap.  Assign a+        -- to the sup of its predecessors.+        (bsupers, bsubs) -> do+          ub <-+            glbBySort+              vm+              relMap+              bsupers+              (getSort vm v)+              (varRels (rm ! (v, SuperTy)))+          lb <-+            lubBySort+              vm+              relMap+              bsubs+              (getSort vm v)+              (varRels (rm ! (v, SubTy)))+          case isSubB lb ub of+            True -> Just (coerce v |-> lb)+            False -> Nothing
src/Disco/Typecheck/Unify.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Typecheck.Unify -- Copyright   :  disco team and contributors@@ -7,21 +10,18 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Unification.---------------------------------------------------------------------------------- module Disco.Typecheck.Unify where -import           Unbound.Generics.LocallyNameless (Name, fv)+import Unbound.Generics.LocallyNameless (Name, fv) -import           Control.Lens                     (anyOf)-import           Control.Monad.State-import qualified Data.Map                         as M-import           Data.Set                         (Set)-import qualified Data.Set                         as S+import Control.Lens (anyOf)+import Control.Monad.State+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S -import           Disco.Subst-import           Disco.Types+import Disco.Subst+import Disco.Types  -- XXX todo: might be better if unification took sorts into account -- directly.  As it is, however, I think it works properly;@@ -51,92 +51,89 @@ -- | Given a list of equations between types, return a substitution --   which makes all the equations satisfied (or fail if it is not --   possible), up to the given comparison on base types.-unify' :: (BaseTy -> BaseTy -> Bool) -> TyDefCtx-       -> [(Type, Type)] -> Maybe S+unify' ::+  (BaseTy -> BaseTy -> Bool) ->+  TyDefCtx ->+  [(Type, Type)] ->+  Maybe S unify' baseEq tyDefns eqs = evalStateT (go eqs) S.empty-  where-    go :: [(Type, Type)] -> StateT (Set (Type,Type)) Maybe S-    go [] = return idS-    go (e:es) = do-      u <- unifyOne e-      case u of-        Left sub    -> (@@ sub) <$> go (applySubst sub es)-        Right newEs -> go (newEs ++ es)--    unifyOne :: (Type, Type) -> StateT (Set (Type,Type)) Maybe (Either S [(Type, Type)])-    unifyOne pair = do-      seen <- get-      case pair `S.member` seen of-        True  -> return $ Left idS-        False -> unifyOne' pair--    unifyOne' :: (Type, Type) -> StateT (Set (Type,Type)) Maybe (Either S [(Type, Type)])--    unifyOne' (ty1, ty2)-      | ty1 == ty2 = return $ Left idS--    unifyOne' (TyVar x, ty2)-      | occurs x ty2 = mzero-      | otherwise    = return $ Left (x |-> ty2)-    unifyOne' (ty1, x@(TyVar _))-      = unifyOne (x, ty1)--    -- At this point we know ty2 isn't the same skolem nor a unification variable.-    -- Skolems don't unify with anything.-    unifyOne' (TySkolem _, _) = mzero-    unifyOne' (_, TySkolem _) = mzero+ where+  go :: [(Type, Type)] -> StateT (Set (Type, Type)) Maybe S+  go [] = return idS+  go (e : es) = do+    u <- unifyOne e+    case u of+      Left sub -> (@@ sub) <$> go (applySubst sub es)+      Right newEs -> go (newEs ++ es) -    -- Unify two container types: unify the container descriptors as-    -- well as the type arguments-    unifyOne' p@(TyCon (CContainer ctr1) tys1, TyCon (CContainer ctr2) tys2) = do-      modify (S.insert p)-      return $ Right ((TyAtom ctr1, TyAtom ctr2) : zip tys1 tys2)+  unifyOne :: (Type, Type) -> StateT (Set (Type, Type)) Maybe (Either S [(Type, Type)])+  unifyOne pair = do+    seen <- get+    case pair `S.member` seen of+      True -> return $ Left idS+      False -> unifyOne' pair -    -- If one of the types to be unified is a user-defined type,-    -- unfold its definition before continuing the matching-    unifyOne' p@(TyCon (CUser t) tys1, ty2) = do-      modify (S.insert p)-      case M.lookup t tyDefns of-        Nothing                 -> mzero-        Just (TyDefBody _ body) -> return $ Right [(body tys1, ty2)]+  unifyOne' :: (Type, Type) -> StateT (Set (Type, Type)) Maybe (Either S [(Type, Type)]) -    unifyOne' p@(ty1, TyCon (CUser t) tys2) = do-      modify (S.insert p)-      case M.lookup t tyDefns of-        Nothing                 -> mzero-        Just (TyDefBody _ body) -> return $ Right [(ty1, body tys2)]+  unifyOne' (ty1, ty2)+    | ty1 == ty2 = return $ Left idS+  unifyOne' (TyVar x, ty2)+    | occurs x ty2 = mzero+    | otherwise = return $ Left (x |-> ty2)+  unifyOne' (ty1, x@(TyVar _)) =+    unifyOne (x, ty1)+  -- At this point we know ty2 isn't the same skolem nor a unification variable.+  -- Skolems don't unify with anything.+  unifyOne' (TySkolem _, _) = mzero+  unifyOne' (_, TySkolem _) = mzero+  -- Unify two container types: unify the container descriptors as+  -- well as the type arguments+  unifyOne' p@(TyCon (CContainer ctr1) tys1, TyCon (CContainer ctr2) tys2) = do+    modify (S.insert p)+    return $ Right ((TyAtom ctr1, TyAtom ctr2) : zip tys1 tys2) -    -- Unify any other pair of type constructor applications: the type-    -- constructors must match exactly-    unifyOne' p@(TyCon c1 tys1, TyCon c2 tys2)-      | c1 == c2  = do-          modify (S.insert p)-          return $ Right (zip tys1 tys2)-      | otherwise = mzero-    unifyOne' (TyAtom (ABase b1), TyAtom (ABase b2))-      | baseEq b1 b2 = return $ Left idS-      | otherwise    = mzero-    unifyOne' _ = mzero  -- Atom = Cons+  -- If one of the types to be unified is a user-defined type,+  -- unfold its definition before continuing the matching+  unifyOne' p@(TyCon (CUser t) tys1, ty2) = do+    modify (S.insert p)+    case M.lookup t tyDefns of+      Nothing -> mzero+      Just (TyDefBody _ body) -> return $ Right [(body tys1, ty2)]+  unifyOne' p@(ty1, TyCon (CUser t) tys2) = do+    modify (S.insert p)+    case M.lookup t tyDefns of+      Nothing -> mzero+      Just (TyDefBody _ body) -> return $ Right [(ty1, body tys2)] +  -- Unify any other pair of type constructor applications: the type+  -- constructors must match exactly+  unifyOne' p@(TyCon c1 tys1, TyCon c2 tys2)+    | c1 == c2 = do+        modify (S.insert p)+        return $ Right (zip tys1 tys2)+    | otherwise = mzero+  unifyOne' (TyAtom (ABase b1), TyAtom (ABase b2))+    | baseEq b1 b2 = return $ Left idS+    | otherwise = mzero+  unifyOne' _ = mzero -- Atom = Cons  equate :: TyDefCtx -> [Type] -> Maybe S equate tyDefns tys = unify tyDefns eqns-  where-    eqns = zip tys (tail tys)+ where+  eqns = zip tys (tail tys)  occurs :: Name Type -> Type -> Bool-occurs x = anyOf fv (==x)-+occurs x = anyOf fv (== x)  unifyAtoms :: TyDefCtx -> [Atom] -> Maybe (Substitution Atom) unifyAtoms tyDefns = fmap (fmap fromTyAtom) . equate tyDefns . map TyAtom-  where-    fromTyAtom (TyAtom a) = a-    fromTyAtom _          = error "fromTyAtom on non-TyAtom!"+ where+  fromTyAtom (TyAtom a) = a+  fromTyAtom _ = error "fromTyAtom on non-TyAtom!"  unifyUAtoms :: TyDefCtx -> [UAtom] -> Maybe (Substitution UAtom) unifyUAtoms tyDefns = fmap (fmap fromTyAtom) . equate tyDefns . map (TyAtom . uatomToAtom)-  where-    fromTyAtom (TyAtom (ABase b))    = UB b-    fromTyAtom (TyAtom (AVar (U v))) = UV v-    fromTyAtom _                     = error "fromTyAtom on wrong thing!"+ where+  fromTyAtom (TyAtom (ABase b)) = UB b+  fromTyAtom (TyAtom (AVar (U v))) = UV v+  fromTyAtom _ = error "fromTyAtom on wrong thing!"
src/Disco/Typecheck/Util.hs view
@@ -1,5 +1,4 @@ ------------------------------------------------------------------------------ -- | -- Module      :  Disco.Typecheck.Util -- Copyright   :  (c) 2016 disco team (see LICENSE)@@ -8,30 +7,27 @@ -- -- Definition of type contexts, type errors, and various utilities -- used during type checking.---------------------------------------------------------------------------------- module Disco.Typecheck.Util where -import           Disco.Effects.Fresh-import           Polysemy-import           Polysemy.Error-import           Polysemy.Output-import           Polysemy.Reader-import           Polysemy.Writer-import           Unbound.Generics.LocallyNameless (Name, bind, string2Name)+import Disco.Effects.Fresh+import Polysemy+import Polysemy.Error+import Polysemy.Output+import Polysemy.Reader+import Polysemy.Writer+import Unbound.Generics.LocallyNameless (Name, bind, string2Name) -import qualified Data.Map                         as M-import           Data.Tuple                       (swap)-import           Prelude                          hiding (lookup)+import qualified Data.Map as M+import Data.Tuple (swap)+import Prelude hiding (lookup) -import           Disco.AST.Surface-import           Disco.Context-import           Disco.Messages-import           Disco.Names                      (ModuleName, QName)-import           Disco.Typecheck.Constraints-import           Disco.Typecheck.Solve-import           Disco.Types+import Disco.AST.Surface+import Disco.Context+import Disco.Messages+import Disco.Names (ModuleName, QName)+import Disco.Typecheck.Constraints+import Disco.Typecheck.Solve+import Disco.Types  ------------------------------------------------------------ -- Contexts@@ -47,7 +43,7 @@ -- | A typechecking error, wrapped up together with the name of the --   thing that was being checked when the error occurred. data LocTCError = LocTCError (Maybe (QName Term)) TCError-  deriving Show+  deriving (Show)  -- | Wrap a @TCError@ into a @LocTCError@ with no explicit provenance --   information.@@ -56,38 +52,60 @@  -- | Potential typechecking errors. data TCError-  = Unbound (Name Term)    -- ^ Encountered an unbound variable-  | Ambiguous (Name Term) [ModuleName] -- ^ Encountered an ambiguous name.-  | NoType  (Name Term)    -- ^ No type is specified for a definition-  | NotCon Con Term Type   -- ^ The type of the term should have an-                           --   outermost constructor matching Con, but-                           --   it has type 'Type' instead-  | EmptyCase              -- ^ Case analyses cannot be empty.-  | PatternType Con Pattern Type  -- ^ The given pattern should have the type, but it doesn't.-                                  -- instead it has a kind of type given by the Con.-  | DuplicateDecls (Name Term)  -- ^ Duplicate declarations.-  | DuplicateDefns (Name Term)  -- ^ Duplicate definitions.-  | DuplicateTyDefns String -- ^ Duplicate type definitions.-  | CyclicTyDef String     -- ^ Cyclic type definition.-  | NumPatterns            -- ^ # of patterns does not match type in definition-  | NoSearch Type          -- ^ Type can't be quantified over.-  | Unsolvable SolveError  -- ^ The constraint solver couldn't find a solution.-  | NotTyDef String        -- ^ An undefined type name was used.-  | NoTWild                -- ^ Wildcards are not allowed in terms.-  | NotEnoughArgs Con      -- ^ Not enough arguments provided to type constructor.-  | TooManyArgs Con        -- ^ Too many arguments provided to type constructor.-  | UnboundTyVar (Name Type) -- ^ Unbound type variable-  | NoPolyRec String [String] [Type] -- ^ Polymorphic recursion is not allowed-  | NoError                -- ^ Not an error.  The identity of the-                           --   @Monoid TCError@ instance.-  deriving Show+  = -- | Encountered an unbound variable+    Unbound (Name Term)+  | -- | Encountered an ambiguous name.+    Ambiguous (Name Term) [ModuleName]+  | -- | No type is specified for a definition+    NoType (Name Term)+  | -- | The type of the term should have an+    --   outermost constructor matching Con, but+    --   it has type 'Type' instead+    NotCon Con Term Type+  | -- | Case analyses cannot be empty.+    EmptyCase+  | -- | The given pattern should have the type, but it doesn't.+    -- instead it has a kind of type given by the Con.+    PatternType Con Pattern Type+  | -- | Duplicate declarations.+    DuplicateDecls (Name Term)+  | -- | Duplicate definitions.+    DuplicateDefns (Name Term)+  | -- | Duplicate type definitions.+    DuplicateTyDefns String+  | -- | Cyclic type definition.+    CyclicTyDef String+  | -- | # of patterns does not match type in definition+    NumPatterns+  | -- | Duplicate variable in a pattern+    NonlinearPattern Pattern (Name Term)+  | -- | Type can't be quantified over.+    NoSearch Type+  | -- | The constraint solver couldn't find a solution.+    Unsolvable SolveError+  | -- | An undefined type name was used.+    NotTyDef String+  | -- | Wildcards are not allowed in terms.+    NoTWild+  | -- | Not enough arguments provided to type constructor.+    NotEnoughArgs Con+  | -- | Too many arguments provided to type constructor.+    TooManyArgs Con+  | -- | Unbound type variable+    UnboundTyVar (Name Type)+  | -- | Polymorphic recursion is not allowed+    NoPolyRec String [String] [Type]+  | -- | Not an error.  The identity of the+    --   @Monoid TCError@ instance.+    NoError+  deriving (Show)  instance Semigroup TCError where   _ <> r = r  -- | 'TCError' is a monoid where we simply discard the first error. instance Monoid TCError where-  mempty  = NoError+  mempty = NoError   mappend = (<>)  ------------------------------------------------------------@@ -126,14 +144,15 @@ -- | Run a computation and solve its generated constraint, returning --   the resulting substitution (or failing with an error).  Note that --   this locally dispatches the constraint writer effect.-solve-  :: Members '[Reader TyDefCtx, Error TCError, Output Message] r-  => Sem (Writer Constraint ': r) a -> Sem r (a, S)+solve ::+  Members '[Reader TyDefCtx, Error TCError, Output (Message ann)] r =>+  Sem (Writer Constraint ': r) a ->+  Sem r (a, S) solve m = do   (a, c) <- withConstraint m   res <- runSolve . inputToReader . solveConstraint $ c   case res of-    Left e  -> throw (Unsolvable e)+    Left e -> throw (Unsolvable e)     Right s -> return (a, s)  ------------------------------------------------------------@@ -143,12 +162,14 @@ -- | Look up the definition of a named type.  Throw a 'NotTyDef' error --   if it is not found. lookupTyDefn ::-  Members '[Reader TyDefCtx, Error TCError] r-  => String -> [Type] -> Sem r Type+  Members '[Reader TyDefCtx, Error TCError] r =>+  String ->+  [Type] ->+  Sem r Type lookupTyDefn x args = do   d <- ask @TyDefCtx   case M.lookup x d of-    Nothing                 -> throw (NotTyDef x)+    Nothing -> throw (NotTyDef x)     Just (TyDefBody _ body) -> return $ body args  -- | Run a subcomputation with an extended type definition context.
src/Disco/Types.hs view
@@ -1,12 +1,16 @@-{-# LANGUAGE DeriveAnyClass       #-}-{-# LANGUAGE DeriveDataTypeable   #-}-{-# LANGUAGE OverloadedStrings    #-}-{-# LANGUAGE PatternSynonyms      #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE UndecidableInstances #-}- {-# OPTIONS_GHC -fno-warn-orphans #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Types -- Copyright   :  disco team and contributors@@ -14,104 +18,111 @@ -- -- The "Disco.Types" module defines the set of types used in the disco -- language type system, along with various utility functions.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Types-       (-       -- * Disco language types-       -- ** Atomic types--         BaseTy(..), isCtr, Var(..), Ilk(..), pattern U, pattern S-       , Atom(..)-       , isVar, isBase, isSkolem-       , UAtom(..), uisVar, uatomToAtom, uatomToEither--       -- ** Type constructors--       , Con(..)-       , pattern CList, pattern CBag, pattern CSet--       -- ** Types--       , Type(..)--       , pattern TyVar-       , pattern TySkolem-       , pattern TyVoid-       , pattern TyUnit-       , pattern TyBool-       , pattern TyProp-       , pattern TyN-       , pattern TyZ-       , pattern TyF-       , pattern TyQ-       , pattern TyC-       -- , pattern TyFin-       , pattern (:->:)-       , pattern (:*:)-       , pattern (:+:)-       , pattern TyList-       , pattern TyBag-       , pattern TySet-       , pattern TyGraph-       , pattern TyMap-       , pattern TyContainer-       , pattern TyUser-       , pattern TyString+module Disco.Types (+  -- * Disco language types -       -- ** Quantified types+  -- ** Atomic types+  BaseTy (..),+  isCtr,+  Var (..),+  Ilk (..),+  pattern U,+  pattern S,+  Atom (..),+  isVar,+  isBase,+  isSkolem,+  UAtom (..),+  uisVar,+  uatomToAtom,+  uatomToEither, -       , PolyType(..)-       , toPolyType, closeType+  -- ** Type constructors+  Con (..),+  pattern CList,+  pattern CBag,+  pattern CSet, -       -- * Type predicates+  -- ** Types+  Type (..),+  pattern TyVar,+  pattern TySkolem,+  pattern TyVoid,+  pattern TyUnit,+  pattern TyBool,+  pattern TyProp,+  pattern TyN,+  pattern TyZ,+  pattern TyF,+  pattern TyQ,+  pattern TyC,+  -- , pattern TyFin+  pattern (:->:),+  pattern (:*:),+  pattern (:+:),+  pattern TyList,+  pattern TyBag,+  pattern TySet,+  pattern TyGraph,+  pattern TyMap,+  pattern TyContainer,+  pattern TyUser,+  pattern TyString, -       , isNumTy, isEmptyTy, isFiniteTy, isSearchable+  -- ** Quantified types+  PolyType (..),+  toPolyType,+  closeType, -       -- * Type substitutions+  -- * Type predicates+  isNumTy,+  isEmptyTy,+  isFiniteTy,+  isSearchable, -       , Substitution, atomToTypeSubst, uatomToTypeSubst+  -- * Type substitutions+  Substitution,+  atomToTypeSubst,+  uatomToTypeSubst, -       -- * Strictness-       , Strictness(..), strictness+  -- * Strictness+  Strictness (..),+  strictness, -       -- * Utilities-       , isTyVar-       , containerVars-       , countType-       , unpair-       , S-       , TyDefBody(..)-       , TyDefCtx+  -- * Utilities+  isTyVar,+  containerVars,+  countType,+  unpair,+  S,+  TyDefBody (..),+  TyDefCtx, -       -- * HasType class-       , HasType(..)-       )-       where+  -- * HasType class+  HasType (..),+)+where -import           Data.Coerce-import           Data.Data                         (Data)-import           Disco.Data                        ()-import           GHC.Generics                      (Generic)-import           Unbound.Generics.LocallyNameless  hiding (lunbind)+import Data.Coerce+import Data.Data (Data)+import Disco.Data ()+import GHC.Generics (Generic)+import Unbound.Generics.LocallyNameless hiding (lunbind) -import           Control.Lens                      (toListOf)-import           Data.List                         (nub)-import           Data.Map                          (Map)-import qualified Data.Map                          as M-import           Data.Set                          (Set)-import qualified Data.Set                          as S-import           Data.Void-import           Math.Combinatorics.Exact.Binomial (choose)+import Control.Lens (toListOf)+import Data.List (nub)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.Set as S+import Data.Void+import Math.Combinatorics.Exact.Binomial (choose) -import           Disco.Effects.LFresh+import Disco.Effects.LFresh -import           Disco.Pretty                      hiding ((<>))-import           Disco.Subst                       (Substitution)-import           Disco.Types.Qualifiers+import Disco.Pretty hiding ((<>))+import Disco.Subst (Substitution)+import Disco.Types.Qualifiers  -------------------------------------------------- -- Disco types@@ -123,34 +134,24 @@ -- | Base types are the built-in types which form the basis of the --   disco type system, out of which more complex types can be built. data BaseTy where-   -- | The void type, with no inhabitants.   Void :: BaseTy-   -- | The unit type, with one inhabitant.   Unit :: BaseTy-   -- | Booleans.-  B    :: BaseTy-+  B :: BaseTy   -- | Propositions.-  P    :: BaseTy-+  P :: BaseTy   -- | Natural numbers.-  N    :: BaseTy-+  N :: BaseTy   -- | Integers.-  Z    :: BaseTy-+  Z :: BaseTy   -- | Fractionals (i.e. nonnegative rationals).-  F    :: BaseTy-+  F :: BaseTy   -- | Rationals.-  Q    :: BaseTy-+  Q :: BaseTy   -- | Unicode characters.-  C    :: BaseTy-+  C :: BaseTy   -- Finite types. The single argument is a natural number defining   -- the exact number of inhabitants.   -- Fin  :: Integer -> BaseTy@@ -161,29 +162,26 @@   --   particular this allows us to reuse all the existing constraint   --   solving machinery for container subtyping.   CtrSet :: BaseTy-   -- | Bag container type.   CtrBag :: BaseTy-   -- | List container type.   CtrList :: BaseTy-   deriving (Show, Eq, Ord, Generic, Data, Alpha, Subst BaseTy, Subst Atom, Subst UAtom, Subst Type)  instance Pretty BaseTy where   pretty = \case-    Void    -> text "Void"-    Unit    -> text "Unit"-    B       -> text "Bool"-    P       -> text "Prop"-    N       -> text "ℕ"-    Z       -> text "ℤ"-    Q       -> text "ℚ"-    F       -> text "𝔽"-    C       -> text "Char"+    Void -> text "Void"+    Unit -> text "Unit"+    B -> text "Bool"+    P -> text "Prop"+    N -> text "ℕ"+    Z -> text "ℤ"+    Q -> text "ℚ"+    F -> text "𝔽"+    C -> text "Char"     CtrList -> text "List"-    CtrBag  -> text "Bag"-    CtrSet  -> text "Set"+    CtrBag -> text "Bag"+    CtrSet -> text "Set"  -- | Test whether a 'BaseTy' is a container (set, bag, or list). isCtr :: BaseTy -> Bool@@ -218,7 +216,7 @@  instance Pretty Ilk where   pretty = \case-    Skolem      -> "S"+    Skolem -> "S"     Unification -> "U"  -- | 'Var' represents /type variables/, that is, variables which stand@@ -247,24 +245,24 @@ --   simplification step, we want to be able to work with collections --   of constraints that are guaranteed to contain only atomic types. data Atom where-  AVar  :: Var -> Atom+  AVar :: Var -> Atom   ABase :: BaseTy -> Atom   deriving (Show, Eq, Ord, Generic, Data, Alpha, Subst Type)  instance Subst Atom Atom where   isvar (AVar (U x)) = Just (SubstName (coerce x))-  isvar _            = Nothing+  isvar _ = Nothing  instance Pretty Atom where   pretty = \case     AVar (U v) -> pretty v     AVar (S v) -> text "$" <> pretty v-    ABase b    -> pretty b+    ABase b -> pretty b  -- | Is this atomic type a variable? isVar :: Atom -> Bool isVar (AVar _) = True-isVar _        = False+isVar _ = False  -- | Is this atomic type a base type? isBase :: Atom -> Bool@@ -273,7 +271,7 @@ -- | Is this atomic type a skolem variable? isSkolem :: Atom -> Bool isSkolem (AVar (S _)) = True-isSkolem _            = False+isSkolem _ = False  -- | /Unifiable/ atomic types are the same as atomic types but without --   skolem variables.  Hence, a unifiable atomic type is either a base@@ -287,13 +285,13 @@ --   these things, we can just focus on base types and unification --   variables. data UAtom where-  UB :: BaseTy    -> UAtom+  UB :: BaseTy -> UAtom   UV :: Name Type -> UAtom   deriving (Show, Eq, Ord, Generic, Alpha, Subst BaseTy)  instance Subst UAtom UAtom where   isvar (UV x) = Just (SubstName (coerce x))-  isvar _      = Nothing+  isvar _ = Nothing  instance Pretty UAtom where   pretty (UB b) = pretty b@@ -302,7 +300,7 @@ -- | Is this unifiable atomic type a (unification) variable? uisVar :: UAtom -> Bool uisVar (UV _) = True-uisVar _      = False+uisVar _ = False  -- | Convert a unifiable atomic type into a regular atomic type. uatomToAtom :: UAtom -> Atom@@ -322,12 +320,11 @@ --   argument types. data Con where   -- | Function type constructor, @T1 -> T2@.-  CArr  :: Con+  CArr :: Con   -- | Product type constructor, @T1 * T2@.   CProd :: Con   -- | Sum type constructor, @T1 + T2@.-  CSum  :: Con-+  CSum :: Con   -- | Container type (list, bag, or set) constructor.  Note this   --   looks like it could contain any 'Atom', but it will only ever   --   contain either a type variable or a 'CtrList', 'CtrBag', or@@ -335,29 +332,24 @@   --   --   See also 'CList', 'CBag', and 'CSet'.   CContainer :: Atom -> Con--   -- | Key value maps, Map k v   CMap :: Con-   -- | Graph constructor, Graph a   CGraph :: Con-   -- | The name of a user defined algebraic datatype.   CUser :: String -> Con-   deriving (Show, Eq, Ord, Generic, Data, Alpha)  instance Pretty Con where   pretty = \case-    CMap         -> text "Map"-    CGraph       -> text "Graph"-    CUser s      -> text s-    CList        -> text "List"-    CBag         -> text "Bag"-    CSet         -> text "Set"+    CMap -> text "Map"+    CGraph -> text "Graph"+    CUser s -> text s+    CList -> text "List"+    CBag -> text "Bag"+    CSet -> text "Set"     CContainer v -> pretty v-    c            -> error $ "Impossible: got Con " ++ show c ++ " in pretty @Con"+    c -> error $ "Impossible: got Con " ++ show c ++ " in pretty @Con"  -- | 'CList' is provided for convenience; it represents a list type --   constructor (/i.e./ @List a@).@@ -395,49 +387,49 @@ --   pattern-match on types when convenient.  For example, using these --   synonyms the foregoing example can be written @TyN :->: TyVar a@. data Type where-   -- | Atomic types (variables and base types), /e.g./ @N@, @Bool@, /etc./   TyAtom :: Atom -> Type-   -- | Application of a type constructor to type arguments, /e.g./ @N   --   -> Bool@ is the application of the arrow type constructor to the   --   arguments @N@ and @Bool@.-  TyCon  :: Con -> [Type] -> Type-+  TyCon :: Con -> [Type] -> Type   deriving (Show, Eq, Ord, Generic, Data, Alpha)  instance Pretty Type where-  pretty (TyAtom a)     = pretty a-  pretty (ty1 :->: ty2) = withPA tarrPA $-    lt (pretty ty1) <+> text "→" <+> rt (pretty ty2)-  pretty (ty1 :*: ty2)  = withPA tmulPA $-    lt (pretty ty1) <+> text "×" <+> rt (pretty ty2)-  pretty (ty1 :+: ty2)  = withPA taddPA $-    lt (pretty ty1) <+> text "+" <+> rt (pretty ty2)-  pretty (TyCon c [])   = pretty c-  pretty (TyCon c tys)  = do+  pretty (TyAtom a) = pretty a+  pretty (ty1 :->: ty2) =+    withPA tarrPA $+      lt (pretty ty1) <+> text "→" <+> rt (pretty ty2)+  pretty (ty1 :*: ty2) =+    withPA tmulPA $+      lt (pretty ty1) <+> text "×" <+> rt (pretty ty2)+  pretty (ty1 :+: ty2) =+    withPA taddPA $+      lt (pretty ty1) <+> text "+" <+> rt (pretty ty2)+  pretty (TyCon c []) = pretty c+  pretty (TyCon c tys) = do     ds <- setPA initPA $ punctuate (text ",") (map pretty tys)     pretty c <> parens (hsep ds)  instance Subst Type Qualifier instance Subst Type Rational where   subst _ _ = id-  substs _  = id+  substs _ = id instance Subst Type Void where   subst _ _ = id-  substs _  = id+  substs _ = id instance Subst Type Con where-  isCoerceVar (CContainer (AVar (U x)))-    = Just (SubstCoerce x substCtrTy)-    where-      substCtrTy (TyAtom a) = Just (CContainer a)-      substCtrTy _          = Nothing-  isCoerceVar _                         = Nothing+  isCoerceVar (CContainer (AVar (U x))) =+    Just (SubstCoerce x substCtrTy)+   where+    substCtrTy (TyAtom a) = Just (CContainer a)+    substCtrTy _ = Nothing+  isCoerceVar _ = Nothing instance Subst Type Type where   isvar (TyAtom (AVar (U x))) = Just (SubstName x)-  isvar _                     = Nothing+  isvar _ = Nothing -pattern TyVar  :: Name Type -> Type+pattern TyVar :: Name Type -> Type pattern TyVar v = TyAtom (AVar (U v))  pattern TySkolem :: Name Type -> Type@@ -470,7 +462,6 @@ pattern TyC :: Type pattern TyC = TyAtom (ABase C) - -- pattern TyFin :: Integer -> Type -- pattern TyFin n = TyAtom (ABase (Fin n)) @@ -492,7 +483,7 @@ pattern TyList :: Type -> Type pattern TyList elTy = TyCon CList [elTy] -pattern TyBag  :: Type -> Type+pattern TyBag :: Type -> Type pattern TyBag elTy = TyCon CBag [elTy]  pattern TySet :: Type -> Type@@ -515,21 +506,40 @@ pattern TyString = TyList TyC  {-# COMPLETE-      TyVar, TySkolem, TyVoid, TyUnit, TyBool, TyProp, TyN, TyZ, TyF, TyQ, TyC,-      (:->:), (:*:), (:+:), TyList, TyBag, TySet, TyGraph, TyMap, TyUser #-}+  TyVar+  , TySkolem+  , TyVoid+  , TyUnit+  , TyBool+  , TyProp+  , TyN+  , TyZ+  , TyF+  , TyQ+  , TyC+  , (:->:)+  , (:*:)+  , (:+:)+  , TyList+  , TyBag+  , TySet+  , TyGraph+  , TyMap+  , TyUser+  #-}  -- | Is this a type variable? isTyVar :: Type -> Bool isTyVar (TyAtom (AVar _)) = True-isTyVar _                 = False+isTyVar _ = False  -- orphans instance (Ord a, Subst t a) => Subst t (Set a) where   subst x t = S.map (subst x t)-  substs s  = S.map (substs s)+  substs s = S.map (substs s) instance (Ord k, Subst t a) => Subst t (Map k a) where   subst x t = M.map (subst x t)-  substs s  = M.map (substs s)+  substs s = M.map (substs s)  -- | The definition of a user-defined type contains: --@@ -555,14 +565,13 @@  -- | Pretty-print a type definition. instance Pretty (String, TyDefBody) where--  pretty (tyName, TyDefBody ps body)-    = "type" <+> (text tyName <> prettyArgs ps) <+> text "=" <+> pretty (body (map (TyVar . string2Name) ps))-    where-      prettyArgs [] = empty-      prettyArgs _  = do-          ds <- punctuate (text ",") (map text ps)-          parens (hsep ds)+  pretty (tyName, TyDefBody ps body) =+    "type" <+> (text tyName <> prettyArgs ps) <+> text "=" <+> pretty (body (map (TyVar . string2Name) ps))+   where+    prettyArgs [] = empty+    prettyArgs _ = do+      ds <- punctuate (text ",") (map text ps)+      parens (hsep ds)  --------------------------------- --  Universally quantified types@@ -597,52 +606,50 @@ -- | Compute the number of inhabitants of a type.  @Nothing@ means the --   type is countably infinite. countType :: Type -> Maybe Integer-countType TyVoid        = Just 0-countType TyUnit        = Just 1-countType TyBool        = Just 2+countType TyVoid = Just 0+countType TyUnit = Just 1+countType TyBool = Just 2 -- countType (TyFin n)     = Just n-countType TyC           = Just (17 * 2^(16 :: Integer))+countType TyC = Just (17 * 2 ^ (16 :: Integer)) countType (ty1 :+: ty2) = (+) <$> countType ty1 <*> countType ty2 countType (ty1 :*: ty2)-  | isEmptyTy ty1       = Just 0-  | isEmptyTy ty2       = Just 0-  | otherwise           = (*) <$> countType ty1 <*> countType ty2+  | isEmptyTy ty1 = Just 0+  | isEmptyTy ty2 = Just 0+  | otherwise = (*) <$> countType ty1 <*> countType ty2 countType (ty1 :->: ty2) =   case (countType ty1, countType ty2) of     (Just 0, _) -> Just 1     (_, Just 0) -> Just 0     (_, Just 1) -> Just 1-    (c1, c2)    -> (^) <$> c2 <*> c1+    (c1, c2) -> (^) <$> c2 <*> c1 countType (TyList ty)-  | isEmptyTy ty        = Just 1-  | otherwise           = Nothing+  | isEmptyTy ty = Just 1+  | otherwise = Nothing countType (TyBag ty)-  | isEmptyTy ty        = Just 1-  | otherwise           = Nothing-countType (TySet ty)    = (2^) <$> countType ty--  -- t = number of elements in vertex type.-  -- n = number of vertices in the graph.-  -- For each n in [0..t], we can choose which n values to use for the-  --   vertices; then for each ordered pair of vertices (u,v)-  --   (including the possibility that u = v), we choose whether or-  --   not there is a directed edge u -> v.-  ---  -- https://oeis.org/A135748--countType (TyGraph ty)  =-  (\t -> sum $ map (\n -> (t `choose` n) * 2^(n*n)) [0 .. t]) <$>-  countType ty+  | isEmptyTy ty = Just 1+  | otherwise = Nothing+countType (TySet ty) = (2 ^) <$> countType ty+-- t = number of elements in vertex type.+-- n = number of vertices in the graph.+-- For each n in [0..t], we can choose which n values to use for the+--   vertices; then for each ordered pair of vertices (u,v)+--   (including the possibility that u = v), we choose whether or+--   not there is a directed edge u -> v.+--+-- https://oeis.org/A135748 +countType (TyGraph ty) =+  (\t -> sum $ map (\n -> (t `choose` n) * 2 ^ (n * n)) [0 .. t])+    <$> countType ty countType (TyMap tyKey tyValue)-  | isEmptyTy tyKey     = Just 1     -- If we can't have any keys or values,-  | isEmptyTy tyValue   = Just 1     -- only option is empty map-  | otherwise           = (\k v -> (v+1) ^ k) <$> countType tyKey <*> countType tyValue-      -- (v+1)^k since for each key, we can choose among v values to associate with it,-      -- or we can choose to not have the key in the map.+  | isEmptyTy tyKey = Just 1 -- If we can't have any keys or values,+  | isEmptyTy tyValue = Just 1 -- only option is empty map+  | otherwise = (\k v -> (v + 1) ^ k) <$> countType tyKey <*> countType tyValue+-- (v+1)^k since for each key, we can choose among v values to associate with it,+-- or we can choose to not have the key in the map.  -- All other types are infinite. (TyN, TyZ, TyQ, TyF)-countType _             = Nothing+countType _ = Nothing  -------------------------------------------------- -- Type predicates@@ -651,19 +658,19 @@ -- | Check whether a type is a numeric type (@N@, @Z@, @F@, @Q@, or @Zn@). isNumTy :: Type -> Bool -- isNumTy (TyFin _) = True-isNumTy ty        = ty `elem` [TyN, TyZ, TyF, TyQ]+isNumTy ty = ty `elem` [TyN, TyZ, TyF, TyQ]  -- | Decide whether a type is empty, /i.e./ uninhabited. isEmptyTy :: Type -> Bool isEmptyTy ty   | Just 0 <- countType ty = True-  | otherwise              = False+  | otherwise = False  -- | Decide whether a type is finite. isFiniteTy :: Type -> Bool isFiniteTy ty   | Just _ <- countType ty = True-  | otherwise              = False+  | otherwise = False  -- XXX coinductively check whether user-defined types are searchable --   e.g.  L = Unit + N * L  ought to be searchable.@@ -671,16 +678,16 @@  -- | Decide whether a type is searchable, i.e. effectively enumerable. isSearchable :: Type -> Bool-isSearchable TyProp         = False+isSearchable TyProp = False isSearchable ty-  | isNumTy ty              = True-  | isFiniteTy ty           = True-isSearchable (TyList ty)    = isSearchable ty-isSearchable (TySet ty)     = isSearchable ty-isSearchable (ty1 :+: ty2)  = isSearchable ty1 && isSearchable ty2-isSearchable (ty1 :*: ty2)  = isSearchable ty1 && isSearchable ty2+  | isNumTy ty = True+  | isFiniteTy ty = True+isSearchable (TyList ty) = isSearchable ty+isSearchable (TySet ty) = isSearchable ty+isSearchable (ty1 :+: ty2) = isSearchable ty1 && isSearchable ty2+isSearchable (ty1 :*: ty2) = isSearchable ty1 && isSearchable ty2 isSearchable (ty1 :->: ty2) = isFiniteTy ty1 && isSearchable ty2-isSearchable _              = False+isSearchable _ = False  -------------------------------------------------- -- Strictness@@ -695,7 +702,7 @@ strictness :: Type -> Strictness strictness ty   | isNumTy ty = Strict-  | otherwise  = Lazy+  | otherwise = Lazy  -------------------------------------------------- -- Utilities@@ -705,7 +712,7 @@ --   types. unpair :: Type -> [Type] unpair (ty1 :*: ty2) = ty1 : unpair ty2-unpair ty            = [ty]+unpair ty = [ty]  -- | Define @S@ as a substitution on types (the most common kind) --   for convenience.@@ -722,8 +729,8 @@  -- | Return a set of all the free container variables in a type. containerVars :: Type -> Set (Name Type)-containerVars (TyCon (CContainer (AVar (U x))) tys)-  = x `S.insert` foldMap containerVars tys+containerVars (TyCon (CContainer (AVar (U x))) tys) =+  x `S.insert` foldMap containerVars tys containerVars (TyCon _ tys) = foldMap containerVars tys containerVars _ = S.empty @@ -733,7 +740,6 @@  -- | A type class for things whose type can be extracted or set. class HasType t where-   -- | Get the type of a thing.   getType :: t -> Type @@ -741,40 +747,3 @@   --   implementation is for 'setType' to do nothing.   setType :: Type -> t -> t   setType _ = id-------------------------------------
src/Disco/Types/Qualifiers.hs view
@@ -1,28 +1,28 @@-{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE OverloadedStrings #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Types.Qualifiers -- Copyright   :  disco team and contributors -- Maintainer  :  byorgey@gmail.com -- -- Type qualifiers and sorts.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause- module Disco.Types.Qualifiers where -import           GHC.Generics-import           Unbound.Generics.LocallyNameless+import GHC.Generics+import Unbound.Generics.LocallyNameless -import           Data.Set                         (Set)-import qualified Data.Set                         as S+import Data.Set (Set)+import qualified Data.Set as S -import           Disco.Pretty-import           Disco.Syntax.Operators+import Disco.Pretty+import Disco.Syntax.Operators  ------------------------------------------------------------ -- Qualifiers@@ -44,25 +44,33 @@ --   These qualifiers can appear in a 'CQual' constraint; see --   "Disco.Typecheck.Constraint". data Qualifier-  = QNum       -- ^ Numeric, i.e. a semiring supporting + and *-  | QSub       -- ^ Subtractive, i.e. supports --  | QDiv       -- ^ Divisive, i.e. supports /-  | QCmp       -- ^ Comparable, i.e. supports decidable ordering/comparison (see Note [QCmp])-  | QEnum      -- ^ Enumerable, i.e. supports ellipsis notation [x .. y]-  | QBool      -- ^ Boolean, i.e. supports and, or, not (Bool or Prop)-  | QBasic     -- ^ Things that do not involve Prop.-  | QSimple    -- ^ Things for which we can derive a *Haskell* Ord instance+  = -- | Numeric, i.e. a semiring supporting + and *+    QNum+  | -- | Subtractive, i.e. supports -+    QSub+  | -- | Divisive, i.e. supports /+    QDiv+  | -- | Comparable, i.e. supports decidable ordering/comparison (see Note [QCmp])+    QCmp+  | -- | Enumerable, i.e. supports ellipsis notation [x .. y]+    QEnum+  | -- | Boolean, i.e. supports and, or, not (Bool or Prop)+    QBool+  | -- | Things that do not involve Prop.+    QBasic+  | -- | Things for which we can derive a *Haskell* Ord instance+    QSimple   deriving (Show, Eq, Ord, Generic, Alpha)  instance Pretty Qualifier where   pretty = \case-    QNum    -> "num"-    QSub    -> "sub"-    QDiv    -> "div"-    QCmp    -> "cmp"-    QEnum   -> "enum"-    QBool   -> "bool"-    QBasic  -> "basic"+    QNum -> "num"+    QSub -> "sub"+    QDiv -> "div"+    QCmp -> "cmp"+    QEnum -> "enum"+    QBool -> "bool"+    QBasic -> "basic"     QSimple -> "simple"  -- ~~~~ Note [QCmp]@@ -71,7 +79,9 @@ -- comparisons at runtime any more, if we disallow functions from -- being QCmp.  With the switch to eager semantics + disallowing -- function comparison, it's now the case that QCmp should mean--- *decidable* (terminating) comparison.++-- * decidable* (terminating) comparison.+ -- -- It used to be the case that every type in disco supported -- (semi-decidable) linear ordering, so in one sense the QCmp@@ -93,15 +103,16 @@ -- | A helper function that returns the appropriate qualifier for a --   binary arithmetic operation. bopQual :: BOp -> Qualifier-bopQual Add  = QNum-bopQual Mul  = QNum-bopQual Div  = QDiv-bopQual Sub  = QSub+bopQual Add = QNum+bopQual Mul = QNum+bopQual Div = QDiv+bopQual Sub = QSub bopQual SSub = QNum--- bopQual And  = QBool--- bopQual Or   = QBool--- bopQual Impl = QBool-bopQual _    = error "No qualifier for binary operation"+bopQual And = QBool+bopQual Or = QBool+bopQual Impl = QBool+bopQual Iff = QBool+bopQual _ = error "No qualifier for binary operation"  ------------------------------------------------------------ -- Sorts
src/Disco/Types/Rules.hs view
@@ -1,4 +1,9 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- SPDX-License-Identifier: BSD-3-Clause+ -- | -- Module      :  Disco.Types.Rules -- Copyright   :  disco team and contributors@@ -6,45 +11,46 @@ -- -- "Disco.Types.Rules" defines some generic rules about arity, -- subtyping, and sorts for disco base types.------------------------------------------------------------------------------------- SPDX-License-Identifier: BSD-3-Clause--module Disco.Types.Rules-  ( -- * Arity--    Variance(..), arity--    -- * Qualifiers-  , Qualifier(..), bopQual--    -- * Sorts-  , Sort, topSort--    -- * Subtyping rules+module Disco.Types.Rules (+  -- * Arity+  Variance (..),+  arity, -  , Dir(..), other+  -- * Qualifiers+  Qualifier (..),+  bopQual, -  , isSubA, isSubB, isDirB-  , supertypes, subtypes, dirtypes+  -- * Sorts+  Sort,+  topSort, -    -- * Qualifier and sort rules+  -- * Subtyping rules+  Dir (..),+  other,+  isSubA,+  isSubB,+  isDirB,+  supertypes,+  subtypes,+  dirtypes, -  , hasQual, hasSort-  , qualRules, sortRules-  , pickSortBaseTy-  )-  where+  -- * Qualifier and sort rules+  hasQual,+  hasSort,+  qualRules,+  sortRules,+  pickSortBaseTy,+)+where -import           Control.Monad          ((>=>))-import           Data.List              (foldl')-import           Data.Map               (Map)-import qualified Data.Map               as M-import qualified Data.Set               as S+import Control.Monad ((>=>))+import Data.List (foldl')+import Data.Map (Map)+import qualified Data.Map as M+import qualified Data.Set as S -import           Disco.Types-import           Disco.Types.Qualifiers+import Disco.Types+import Disco.Types.Qualifiers  ------------------------------------------------------------ -- Arity@@ -65,16 +71,17 @@ --   That is, @S1 -> T1 <: S2 -> T2@ (@<:@ means "is a subtype of") if --   and only if @S2 <: S1@ and @T1 <: T2@. arity :: Con -> [Variance]-arity CArr           = [Contra, Co]-arity CProd          = [Co, Co]-arity CSum           = [Co, Co]+arity CArr = [Contra, Co]+arity CProd = [Co, Co]+arity CSum = [Co, Co] arity (CContainer _) = [Co]-arity CMap           = [Contra, Co]-arity CGraph         = [Co]-arity (CUser _)      = error "Impossible! arity CUser"-  -- CUsers should always be replaced by their definitions before arity-  -- is called.+arity CMap = [Contra, Co]+arity CGraph = [Co]+arity (CUser _) = error "Impossible! arity CUser" +-- CUsers should always be replaced by their definitions before arity+-- is called.+ ------------------------------------------------------------ -- Subtyping rules ------------------------------------------------------------@@ -86,7 +93,7 @@  -- | Swap directions. other :: Dir -> Dir-other SubTy   = SuperTy+other SubTy = SuperTy other SuperTy = SubTy  --------------------------------------------------@@ -96,45 +103,45 @@ --   @True@ if either they are equal, or if they are base types and --   'isSubB' returns true. isSubA :: Atom -> Atom -> Bool-isSubA a1 a2                 | a1 == a2 = True+isSubA a1 a2 | a1 == a2 = True isSubA (ABase t1) (ABase t2) = isSubB t1 t2-isSubA _ _                   = False+isSubA _ _ = False  -- | Check whether one base type is a subtype of another. isSubB :: BaseTy -> BaseTy -> Bool isSubB b1 b2 | b1 == b2 = True-isSubB N Z   = True-isSubB N F   = True-isSubB N Q   = True-isSubB Z Q   = True-isSubB F Q   = True-isSubB B P   = True-isSubB _ _   = False+isSubB N Z = True+isSubB N F = True+isSubB N Q = True+isSubB Z Q = True+isSubB F Q = True+isSubB B P = True+isSubB _ _ = False  -- | Check whether one base type is a sub- or supertype of another. isDirB :: Dir -> BaseTy -> BaseTy -> Bool-isDirB SubTy   b1 b2 = isSubB b1 b2+isDirB SubTy b1 b2 = isSubB b1 b2 isDirB SuperTy b1 b2 = isSubB b2 b1  -- | List all the supertypes of a given base type. supertypes :: BaseTy -> [BaseTy]-supertypes N  = [N, Z, F, Q]-supertypes Z  = [Z, Q]-supertypes F  = [F, Q]-supertypes B  = [B, P]+supertypes N = [N, Z, F, Q]+supertypes Z = [Z, Q]+supertypes F = [F, Q]+supertypes B = [B, P] supertypes ty = [ty]  -- | List all the subtypes of a given base type. subtypes :: BaseTy -> [BaseTy]-subtypes Q  = [Q, F, Z, N]-subtypes F  = [F, N]-subtypes Z  = [Z, N]-subtypes P  = [P, B]+subtypes Q = [Q, F, Z, N]+subtypes F = [F, N]+subtypes Z = [Z, N]+subtypes P = [P, B] subtypes ty = [ty]  -- | List all the sub- or supertypes of a given base type. dirtypes :: Dir -> BaseTy -> [BaseTy]-dirtypes SubTy   = subtypes+dirtypes SubTy = subtypes dirtypes SuperTy = supertypes  ------------------------------------------------------------@@ -143,19 +150,19 @@  -- | Check whether a given base type satisfies a qualifier. hasQual :: BaseTy -> Qualifier -> Bool-hasQual P       QCmp    = False    -- can't compare Props-hasQual _       QCmp    = True-hasQual P       QBasic  = False-hasQual _       QBasic  = True-hasQual P       QSimple = False-hasQual _       QSimple = True+hasQual P QCmp = False -- can't compare Props+hasQual _ QCmp = True+hasQual P QBasic = False+hasQual _ QBasic = True+hasQual P QSimple = False+hasQual _ QSimple = True -- hasQual (Fin _) q     | q `elem` [QNum, QSub, QEnum] = True -- hasQual (Fin n) QDiv  = isPrime n-hasQual b       QNum    = b `elem` [N, Z, F, Q]-hasQual b       QSub    = b `elem` [Z, Q]-hasQual b       QDiv    = b `elem` [F, Q]-hasQual b       QEnum   = b `elem` [N, Z, F, Q, C]-hasQual b       QBool   = b `elem` [B, P]+hasQual b QNum = b `elem` [N, Z, F, Q]+hasQual b QSub = b `elem` [Z, Q]+hasQual b QDiv = b `elem` [F, Q]+hasQual b QEnum = b `elem` [N, Z, F, Q, C]+hasQual b QBool = b `elem` [B, P]  -- | Check whether a base type has a certain sort, which simply --   amounts to whether it satisfies every qualifier in the sort.@@ -177,57 +184,65 @@ --   set of qualifiers (i.e. a general sort) on a type argument.  In --   that case one would just have to encode 'sortRules' directly. qualRulesMap :: Map Con (Map Qualifier [Maybe Qualifier])-qualRulesMap = M.fromList-  [ CProd ==> M.fromList-    [ QCmp ==> [Just QCmp, Just QCmp],-      QSimple ==> [Just QSimple, Just QSimple]-    ]-  , CSum ==> M.fromList-    [ QCmp ==> [Just QCmp, Just QCmp],-      QSimple ==> [Just QSimple, Just QSimple]-    ]-  , CList ==> M.fromList-    [ QCmp ==> [Just QCmp],-      QSimple ==> [Just QSimple]-    ]-  , CBag ==> M.fromList-    [ QCmp ==> [Just QCmp],-      QSimple ==> [Just QSimple]-    ]-  , CSet ==> M.fromList-    [ QCmp ==> [Just QCmp],-      QSimple ==> [Just QSimple]-    ]-  , CGraph ==> M.fromList-    [ QCmp ==> [Just QCmp],-      QNum ==> [Nothing]-    ]-  , CMap ==> M.fromList-    [ QCmp ==> [Just QCmp, Just QCmp]+qualRulesMap =+  M.fromList+    [ CProd+        ==> M.fromList+          [ QCmp ==> [Just QCmp, Just QCmp]+          , QSimple ==> [Just QSimple, Just QSimple]+          ]+    , CSum+        ==> M.fromList+          [ QCmp ==> [Just QCmp, Just QCmp]+          , QSimple ==> [Just QSimple, Just QSimple]+          ]+    , CList+        ==> M.fromList+          [ QCmp ==> [Just QCmp]+          , QSimple ==> [Just QSimple]+          ]+    , CBag+        ==> M.fromList+          [ QCmp ==> [Just QCmp]+          , QSimple ==> [Just QSimple]+          ]+    , CSet+        ==> M.fromList+          [ QCmp ==> [Just QCmp]+          , QSimple ==> [Just QSimple]+          ]+    , CGraph+        ==> M.fromList+          [ QCmp ==> [Just QCmp]+          , QNum ==> [Nothing]+          ]+    , CMap+        ==> M.fromList+          [ QCmp ==> [Just QCmp, Just QCmp]+          ]     ]-  ]-  where-    (==>) :: a -> b -> (a,b)-    (==>) = (,)+ where+  (==>) :: a -> b -> (a, b)+  (==>) = (,) -  -- We could (theoretically) make graphs and maps also be simple values if we require the map's values are also simple.+-- We could (theoretically) make graphs and maps also be simple values if we require the map's values are also simple. -  -- Eventually we can easily imagine adding an opt-in mode where-  -- numeric operations can be used on pairs and functions, then the-  -- qualRules would become dependent on what language extension/mode-  -- was chosen.  For example we could have rules like-  ---  -- [ CArr ==> M.fromList-  --   [ QNum ==> [Nothing, Just QNum]  -- (a -> b) can be +, * iff b can-  --   , QSub ==> [Nothing, Just QSub]  -- ditto for subtraction-  --   , QDiv ==> [Nothing, Just QDiv]  -- and division-  --   ]-  -- , CProd ==> M.fromList-  --   [ QNum ==> [Just QNum, Just QNum] -- (a,b) can be +, * iff a and b can-  --   , QSub ==> [Just QSub, Just QSub] -- etc.-  --   , QDiv ==> [Just QDiv, Just QDiv]-  --   ]-  -- ]+-- Eventually we can easily imagine adding an opt-in mode where+-- numeric operations can be used on pairs and functions, then the+-- qualRules would become dependent on what language extension/mode+-- was chosen.  For example we could have rules like+--+-- [ CArr ==> M.fromList+--   [ QNum ==> [Nothing, Just QNum]  -- (a -> b) can be +, * iff b can+--   , QSub ==> [Nothing, Just QSub]  -- ditto for subtraction+--   , QDiv ==> [Nothing, Just QDiv]  -- and division+--   ]+-- , CProd ==> M.fromList+--   [ QNum ==> [Just QNum, Just QNum] -- (a,b) can be +, * iff a and b can+--   , QSub ==> [Just QSub, Just QSub] -- etc.+--   , QDiv ==> [Just QDiv, Just QDiv]+--   ]+-- ]  -- | Given a constructor T and a qualifier we want to hold of a type T --   t1 t2 ..., return a list of qualifiers that need to hold of t1,@@ -236,7 +251,7 @@ -- T t1 t2 ... is basic (contains no Prop) iff t1, t2 ... all are. qualRules c QBasic = Just (map (const (Just QBasic)) (arity c)) -- Otherwise, just look up in the qualRulesMap.-qualRules c q      = (M.lookup c >=> M.lookup q) qualRulesMap+qualRules c q = (M.lookup c >=> M.lookup q) qualRulesMap  -- | @sortRules T s = [s1, ..., sn]@ means that sort @s@ holds of --   type @(T t1 ... tn)@ if and only if  @s1 t1 /\ ... /\ sn tn@.@@ -259,12 +274,12 @@ -- | Pick a base type (generally the "simplest") that satisfies a given sort. pickSortBaseTy :: Sort -> BaseTy pickSortBaseTy s-  | QDiv    `S.member` s && QSub `S.member` s = Q-  | QDiv    `S.member` s = F-  | QSub    `S.member` s = Z-  | QNum    `S.member` s = N-  | QCmp    `S.member` s = N-  | QEnum   `S.member` s = N-  | QBool   `S.member` s = B+  | QDiv `S.member` s && QSub `S.member` s = Q+  | QDiv `S.member` s = F+  | QSub `S.member` s = Z+  | QNum `S.member` s = N+  | QCmp `S.member` s = N+  | QEnum `S.member` s = N+  | QBool `S.member` s = B   | QSimple `S.member` s = N-  | otherwise            = Unit+  | otherwise = Unit
src/Disco/Util.hs view
@@ -1,4 +1,7 @@ -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Util -- Copyright   :  disco team and contributors@@ -7,9 +10,6 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Miscellaneous utilities.---------------------------------------------------------------------------------- module Disco.Util where  import qualified Data.Map as M@@ -18,7 +18,7 @@  -- | A synonym for pairing which makes convenient syntax for --   constructing literal maps via M.fromList.-(==>) :: a -> b -> (a,b)+(==>) :: a -> b -> (a, b) (==>) = (,)  for :: [a] -> (a -> b) -> [b]@@ -27,4 +27,4 @@ (!) :: (Show k, Ord k) => M.Map k v -> k -> v m ! k = case M.lookup k m of   Nothing -> error $ "key " ++ show k ++ " is not an element in the map"-  Just v  -> v+  Just v -> v
src/Disco/Value.hs view
@@ -1,10 +1,13 @@-{-# LANGUAGE DeriveTraversable          #-}-{-# LANGUAGE DerivingStrategies         #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE PatternSynonyms            #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}  -----------------------------------------------------------------------------++-----------------------------------------------------------------------------+ -- | -- Module      :  Disco.Value -- Copyright   :  disco team and contributors@@ -13,75 +16,94 @@ -- SPDX-License-Identifier: BSD-3-Clause -- -- Disco runtime values and environments.-----------------------------------------------------------------------------------module Disco.Value-  ( -- * Values--    Value(.., VNil, VCons, VFun)-  , SimpleValue(..)-  , toSimpleValue, fromSimpleValue--    -- ** Conversion+module Disco.Value (+  -- * Values+  Value (.., VNil, VCons, VFun),+  SimpleValue (..),+  toSimpleValue,+  fromSimpleValue, -  , ratv, vrat-  , intv, vint-  , charv, vchar-  , enumv-  , pairv, vpair-  , listv, vlist+  -- ** Conversion+  ratv,+  vrat,+  intv,+  vint,+  charv,+  vchar,+  enumv,+  pairv,+  vpair,+  listv,+  vlist, -    -- * Props & testing-  , ValProp(..), TestResult(..), TestReason_(..), TestReason-  , SearchType(..), SearchMotive(.., SMExists, SMForall)-  , TestVars(..), TestEnv(..), emptyTestEnv, getTestEnv, extendPropEnv, extendResultEnv-  , testIsOk, testIsError, testReason, testEnv+  -- * Props & testing+  ValProp (..),+  TestResult (..),+  TestReason_ (..),+  TestReason,+  SearchType (..),+  SearchMotive (.., SMExists, SMForall),+  TestVars (..),+  TestEnv (..),+  emptyTestEnv,+  getTestEnv,+  extendPropEnv,+  extendResultEnv,+  testIsOk,+  testIsError,+  testReason,+  testEnv,+  resultIsCertain,+  LOp (..),+  interpLOp,    -- * Environments--  , Env+  Env,    -- * Memory-  , Cell(..), Mem, emptyMem, allocate, allocateRec, lkup, set+  Cell (..),+  Mem,+  emptyMem,+  allocate,+  allocateRec,+  lkup,+  set,    -- * Pretty-printing--  , prettyValue', prettyValue, prettyTestFailure, prettyTestResult-  ) where+  prettyValue',+  prettyValue,+) where -import           Prelude                          hiding ((<>))-import qualified Prelude                          as P+import Prelude hiding ((<>))+import qualified Prelude as P -import           Control.Monad                    (forM)-import           Data.Bifunctor                   (first)-import           Data.Char                        (chr, ord, toLower)-import           Data.IntMap                      (IntMap)-import qualified Data.IntMap                      as IM-import           Data.List                        (foldl')-import           Data.Map                         (Map)-import qualified Data.Map                         as M-import           Data.Ratio+import Control.Monad (forM)+import Data.Bifunctor (first)+import Data.Char (chr, ord, toLower)+import Data.IntMap (IntMap)+import qualified Data.IntMap as IM+import Data.List (foldl')+import Data.Map (Map)+import qualified Data.Map as M+import Data.Ratio -import           Algebra.Graph                    (Graph, foldg)+import Algebra.Graph (Graph, foldg) -import           Disco.AST.Core-import           Disco.AST.Generic                (Side (..))-import           Disco.AST.Typed                  (AProperty)-import           Disco.Context                    as Ctx-import           Disco.Error-import           Disco.Names-import           Disco.Pretty-import           Disco.Syntax.Operators           (BOp (Add, Mul))-import           Disco.Typecheck.Erase            (eraseProperty)-import           Disco.Types+import Disco.AST.Core+import Disco.AST.Generic (Side (..))+import Disco.Context as Ctx+import Disco.Error+import Disco.Names+import Disco.Pretty+import Disco.Syntax.Operators (BOp (Add, Mul))+import Disco.Types -import           Disco.Effects.LFresh-import           Polysemy-import           Polysemy.Input-import           Polysemy.Reader-import           Polysemy.State-import           Unbound.Generics.LocallyNameless (Name)+import Disco.Effects.LFresh+import Polysemy+import Polysemy.Input+import Polysemy.Reader+import Polysemy.State+import Unbound.Generics.LocallyNameless (Name)  ------------------------------------------------------------ -- Value type@@ -90,37 +112,28 @@ -- | Different types of values which can result from the evaluation --   process. data Value where-   -- | A numeric value, which also carries a flag saying how   --   fractional values should be diplayed.-  VNum     :: RationalDisplay -> Rational -> Value-+  VNum :: RationalDisplay -> Rational -> Value   -- | A built-in function constant.-  VConst   :: Op -> Value-+  VConst :: Op -> Value   -- | An injection into a sum type.-  VInj     :: Side -> Value -> Value-+  VInj :: Side -> Value -> Value   -- | The unit value.-  VUnit    :: Value-+  VUnit :: Value   -- | A pair of values.-  VPair    :: Value -> Value -> Value-+  VPair :: Value -> Value -> Value   -- | A closure, i.e. a function body together with its   --   environment.-  VClo     :: Env -> [Name Core] -> Core -> Value-+  VClo :: Env -> [Name Core] -> Core -> Value   -- | A disco type can be a value.  For now, there are only a very   --   limited number of places this could ever show up (in   --   particular, as an argument to @enumerate@ or @count@).-  VType    :: Type -> Value-+  VType :: Type -> Value   -- | A reference, i.e. a pointer to a memory cell.  This is used to   --   implement (optional, user-requested) laziness as well as   --   recursion.-  VRef     :: Int -> Value-+  VRef :: Int -> Value   -- | A literal function value.  @VFun@ is only used when   --   enumerating function values in order to decide comparisons at   --   higher-order function types.  For example, in order to@@ -131,30 +144,25 @@   --   We assume that all @VFun@ values are /strict/, that is, their   --   arguments should be fully evaluated to RNF before being   --   passed to the function.-  VFun_   :: ValFun -> Value-+  VFun_ :: ValFun -> Value   -- | A proposition.-  VProp   :: ValProp -> Value-+  VProp :: ValProp -> Value   -- | A literal bag, containing a finite list of (perhaps only   --   partially evaluated) values, each paired with a count.  This is   --   also used to represent sets (with the invariant that all counts   --   are equal to 1).   VBag :: [(Value, Integer)] -> Value-   -- | A graph, stored using an algebraic repesentation.   VGraph :: Graph SimpleValue -> Value-   -- | A map from keys to values. Differs from functions because we can   --   actually construct the set of entries, while functions only have this   --   property when the key type is finite.   VMap :: Map SimpleValue Value -> Value--  deriving Show+  deriving (Show)  -- | Convenient pattern for the empty list. pattern VNil :: Value-pattern VNil      = VInj L VUnit+pattern VNil = VInj L VUnit  -- | Convenient pattern for list cons. pattern VCons :: Value -> Value -> Value@@ -170,31 +178,31 @@ --   only reason for actually doing this would be constructing graphs --   of graphs or maps of maps, or the like. data SimpleValue where-  SNum   :: RationalDisplay -> Rational -> SimpleValue-  SUnit  :: SimpleValue-  SInj   :: Side -> SimpleValue -> SimpleValue-  SPair  :: SimpleValue -> SimpleValue -> SimpleValue-  SBag   :: [(SimpleValue, Integer)] -> SimpleValue-  SType  :: Type -> SimpleValue+  SNum :: RationalDisplay -> Rational -> SimpleValue+  SUnit :: SimpleValue+  SInj :: Side -> SimpleValue -> SimpleValue+  SPair :: SimpleValue -> SimpleValue -> SimpleValue+  SBag :: [(SimpleValue, Integer)] -> SimpleValue+  SType :: Type -> SimpleValue   deriving (Show, Eq, Ord)  toSimpleValue :: Value -> SimpleValue toSimpleValue = \case-  VNum d n    -> SNum d n-  VUnit       -> SUnit-  VInj s v1   -> SInj s (toSimpleValue v1)+  VNum d n -> SNum d n+  VUnit -> SUnit+  VInj s v1 -> SInj s (toSimpleValue v1)   VPair v1 v2 -> SPair (toSimpleValue v1) (toSimpleValue v2)-  VBag bs     -> SBag (map (first toSimpleValue) bs)-  VType t     -> SType t-  t           -> error $ "A non-simple value was passed as simple: " ++ show t+  VBag bs -> SBag (map (first toSimpleValue) bs)+  VType t -> SType t+  t -> error $ "A non-simple value was passed as simple: " ++ show t  fromSimpleValue :: SimpleValue -> Value-fromSimpleValue (SNum d n)    = VNum d n-fromSimpleValue SUnit         = VUnit-fromSimpleValue (SInj s v)    = VInj s (fromSimpleValue v)+fromSimpleValue (SNum d n) = VNum d n+fromSimpleValue SUnit = VUnit+fromSimpleValue (SInj s v) = VInj s (fromSimpleValue v) fromSimpleValue (SPair v1 v2) = VPair (fromSimpleValue v1) (fromSimpleValue v2)-fromSimpleValue (SBag bs)     = VBag $ map (first fromSimpleValue) bs-fromSimpleValue (SType t)     = VType t+fromSimpleValue (SBag bs) = VBag $ map (first fromSimpleValue) bs+fromSimpleValue (SType t) = VType t  -- | A @ValFun@ is just a Haskell function @Value -> Value@.  It is a --   @newtype@ just so we can have a custom @Show@ instance for it and@@ -220,7 +228,7 @@  vrat :: Value -> Rational vrat (VNum _ r) = r-vrat v          = error $ "vrat " ++ show v+vrat v = error $ "vrat " ++ show v  -- | A convenience function for creating a default @VNum@ value with a --   default (@Fractional@) flag.@@ -229,7 +237,7 @@  vint :: Value -> Integer vint (VNum _ n) = numerator n-vint v          = error $ "vint " ++ show v+vint v = error $ "vint " ++ show v  vchar :: Value -> Char vchar = chr . fromIntegral . vint@@ -242,34 +250,33 @@ enumv :: Enum e => e -> Value enumv e = VInj (toEnum $ fromEnum e) VUnit -pairv :: (a -> Value) -> (b -> Value) -> (a,b) -> Value-pairv av bv (a,b) = VPair (av a) (bv b)+pairv :: (a -> Value) -> (b -> Value) -> (a, b) -> Value+pairv av bv (a, b) = VPair (av a) (bv b) -vpair :: (Value -> a) -> (Value -> b) -> Value -> (a,b)+vpair :: (Value -> a) -> (Value -> b) -> Value -> (a, b) vpair va vb (VPair a b) = (va a, vb b)-vpair _ _ v             = error $ "vpair " ++ show v+vpair _ _ v = error $ "vpair " ++ show v  listv :: (a -> Value) -> [a] -> Value-listv _ []        = VNil-listv eltv (a:as) = VCons (eltv a) (listv eltv as)+listv _ [] = VNil+listv eltv (a : as) = VCons (eltv a) (listv eltv as)  vlist :: (Value -> a) -> Value -> [a]-vlist _ VNil            = []+vlist _ VNil = [] vlist velt (VCons v vs) = velt v : vlist velt vs-vlist _ v               = error $ "vlist " ++ show v-+vlist _ v = error $ "vlist " ++ show v  ------------------------------------------------------------ -- Propositions ------------------------------------------------------------  data SearchType-  = Exhaustive-    -- ^ All possibilities were checked.-  | Randomized Integer Integer-    -- ^ A number of small cases were checked exhaustively and+  = -- | All possibilities were checked.+    Exhaustive+  | -- | A number of small cases were checked exhaustively and     --   then a number of additional cases were checked at random.-  deriving Show+    Randomized Integer Integer+  deriving (Show)  -- | The answer (success or failure) we're searching for, and --   the result (success or failure) we return when we find it.@@ -278,7 +285,7 @@ --   @(True, True)@ corresponds to "exists". The other values --   arise from negations. newtype SearchMotive = SearchMotive (Bool, Bool)-  deriving Show+  deriving (Show)  pattern SMForall :: SearchMotive pattern SMForall = SearchMotive (False, False)@@ -302,37 +309,53 @@ getTestEnv (TestVars tvs) e = fmap TestEnv . forM tvs $ \(s, ty, name) -> do   let value = Ctx.lookup' (localName name) e   case value of-    Just v  -> return (s, ty, v)+    Just v -> return (s, ty, v)     Nothing -> Left (UnboundPanic name) +-- | Binary logical operators.+data LOp = LAnd | LOr | LImpl deriving (Eq, Ord, Show, Enum, Bounded)++interpLOp :: LOp -> Bool -> Bool -> Bool+interpLOp LAnd = (&&)+interpLOp LOr = (||)+interpLOp LImpl = (==>)+ where+  True ==> False = False+  _ ==> _ = True+ -- | The possible outcomes of a property test, parametrized over --   the type of values. A @TestReason@ explains why a proposition --   succeeded or failed. data TestReason_ a-  = TestBool-    -- ^ The prop evaluated to a boolean.-  | TestEqual Type a a-    -- ^ The test was an equality test. Records the values being+  = -- | The prop evaluated to a boolean.+    TestBool+  | -- | The test was an equality test. Records the values being     --   compared and also their type (which is needed for printing).-  | TestNotFound SearchType-    -- ^ The search didn't find any examples/counterexamples.-  | TestFound TestResult-    -- ^ The search found an example/counterexample.-  | TestRuntimeError EvalError-    -- ^ The prop failed at runtime. This is always a failure, no+    TestEqual Type a a+  | -- | The test was a less than test. Records the values being+    --   compared and also their type (which is needed for printing).+    TestLt Type a a+  | -- | The search didn't find any examples/counterexamples.+    TestNotFound SearchType+  | -- | The search found an example/counterexample.+    TestFound TestResult+  | -- | A binary logical operator was used to combine the given two results.+    TestBin LOp TestResult TestResult+  | -- | The prop failed at runtime. This is always a failure, no     --   matter which quantifiers or negations it's under.+    TestRuntimeError EvalError   deriving (Show, Functor, Foldable, Traversable)  type TestReason = TestReason_ Value  -- | The possible outcomes of a proposition. data TestResult = TestResult Bool TestReason TestEnv-  deriving Show+  deriving (Show)  -- | Whether the property test resulted in a runtime error. testIsError :: TestResult -> Bool testIsError (TestResult _ (TestRuntimeError _) _) = True-testIsError _                                     = False+testIsError _ = False  -- | Whether the property test resulted in success. testIsOk :: TestResult -> Bool@@ -345,17 +368,42 @@ testEnv :: TestResult -> TestEnv testEnv (TestResult _ _ e) = e +testIsCertain :: TestResult -> Bool+testIsCertain (TestResult _ r _) = resultIsCertain r++resultIsCertain :: TestReason -> Bool+resultIsCertain TestBool = True+resultIsCertain TestEqual {} = True+resultIsCertain TestLt {} = True+resultIsCertain (TestNotFound Exhaustive) = True+resultIsCertain (TestNotFound (Randomized _ _)) = False+resultIsCertain (TestFound r) = testIsCertain r+resultIsCertain (TestRuntimeError _) = True+resultIsCertain (TestBin op tr1 tr2)+  | c1 && c2 = True+  | c1 && ((op == LOr) == ok1) = True+  | c2 && ((op /= LAnd) == ok2) = True+  | otherwise = False+ where+  c1 = testIsCertain tr1+  c2 = testIsCertain tr2+  ok1 = testIsOk tr1+  ok2 = testIsOk tr2+ -- | A @ValProp@ is the normal form of a Disco value of type @Prop@. data ValProp-  = VPDone TestResult-    -- ^ A prop that has already either succeeded or failed.-  | VPSearch SearchMotive [Type] Value TestEnv-    -- ^ A pending search.-  deriving Show+  = -- | A prop that has already either succeeded or failed.+    VPDone TestResult+  | -- | A pending search.+    VPSearch SearchMotive [Type] Value TestEnv+  | -- | A binary logical operator combining two prop values.+    VPBin LOp ValProp ValProp+  deriving (Show)  extendPropEnv :: TestEnv -> ValProp -> ValProp extendPropEnv g (VPDone (TestResult b r e)) = VPDone (TestResult b r (g P.<> e))-extendPropEnv g (VPSearch sm tys v e)       = VPSearch sm tys v (g P.<> e)+extendPropEnv g (VPSearch sm tys v e) = VPSearch sm tys v (g P.<> e)+extendPropEnv g (VPBin op vp1 vp2) = VPBin op (extendPropEnv g vp1) (extendPropEnv g vp2)  extendResultEnv :: TestEnv -> TestResult -> TestResult extendResultEnv g (TestResult b r e) = TestResult b r (g P.<> e)@@ -365,16 +413,17 @@ ------------------------------------------------------------  -- | An environment is a mapping from names to values.-type Env  = Ctx Core Value+type Env = Ctx Core Value  ------------------------------------------------------------ -- Memory ------------------------------------------------------------  -- | 'Mem' represents a memory, containing 'Cell's-data Mem = Mem { next :: Int, mu :: IntMap Cell } deriving Show-data Cell = Blackhole | E Env Core | V Value deriving Show+data Mem = Mem {next :: Int, mu :: IntMap Cell} deriving (Show) +data Cell = Blackhole | E Env Core | V Value deriving (Show)+ emptyMem :: Mem emptyMem = Mem 0 IM.empty @@ -384,7 +433,7 @@ allocate :: Members '[State Mem] r => Env -> Core -> Sem r Int allocate e t = do   Mem n m <- get-  put $ Mem (n+1) (IM.insert n (E e t) m)+  put $ Mem (n + 1) (IM.insert n (E e t) m)   return n  -- | Allocate new memory cells for a group of mutually recursive@@ -393,11 +442,11 @@ allocateRec e bs = do   Mem n m <- get   let newRefs = zip [n ..] bs-      e' = foldl' (flip (\(i,(x,_)) -> Ctx.insert x (VRef i))) e newRefs-      m' = foldl' (flip (\(i,(_,c)) -> IM.insert i (E e' c))) m newRefs+      e' = foldl' (flip (\(i, (x, _)) -> Ctx.insert x (VRef i))) e newRefs+      m' = foldl' (flip (\(i, (_, c)) -> IM.insert i (E e' c))) m newRefs       n' = n + length bs   put $ Mem n' m'-  return [n .. n'-1]+  return [n .. n' - 1]  -- | Look up the cell at a given index. lkup :: Members '[State Mem] r => Int -> Sem r (Maybe Cell)@@ -411,60 +460,51 @@ -- Pretty-printing values ------------------------------------------------------------ -prettyValue' :: Member (Input TyDefCtx) r => Type -> Value -> Sem r Doc+prettyValue' :: Member (Input TyDefCtx) r => Type -> Value -> Sem r (Doc ann) prettyValue' ty v = runLFresh . runReader initPA $ prettyValue ty v -prettyValue :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r Doc-+prettyValue :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r (Doc ann) -- Lazily expand any user-defined types prettyValue (TyUser x args) v = do   tydefs <- input-  let (TyDefBody _ body) = tydefs M.! x   -- This can't fail if typechecking succeeded+  let (TyDefBody _ body) = tydefs M.! x -- This can't fail if typechecking succeeded   prettyValue (body args) v--prettyValue _      VUnit                     = "■"-prettyValue TyProp _                         = prettyPlaceholder TyProp-prettyValue TyBool (VInj s _)                = text $ map toLower (show (s == R))+prettyValue _ VUnit = "■"+prettyValue TyProp _ = prettyPlaceholder TyProp+prettyValue TyBool (VInj s _) = text $ map toLower (show (s == R)) prettyValue TyBool v =   error $ "Non-VInj passed with Bool type to prettyValue: " ++ show v-prettyValue TyC (vchar -> c)                 = text (show c)+prettyValue TyC (vchar -> c) = text (show c) prettyValue (TyList TyC) (vlist vchar -> cs) = doubleQuotes . text . concatMap prettyChar $ cs-  where-    prettyChar = drop 1 . reverse . drop 1 . reverse . show . (:[])-prettyValue (TyList ty) (vlist id -> xs)     = do+ where+  prettyChar = drop 1 . reverse . drop 1 . reverse . show . (: [])+prettyValue (TyList ty) (vlist id -> xs) = do   ds <- punctuate (text ",") (map (prettyValue ty) xs)   brackets (hsep ds)--prettyValue ty@(_ :*: _) v                   = parens (prettyTuple ty v)--prettyValue (ty1 :+: _) (VInj L v)           = "left"  <> prettyVP ty1 v-prettyValue (_ :+: ty2) (VInj R v)           = "right" <> prettyVP ty2 v+prettyValue ty@(_ :*: _) v = parens (prettyTuple ty v)+prettyValue (ty1 :+: _) (VInj L v) = "left" <> prettyVP ty1 v+prettyValue (_ :+: ty2) (VInj R v) = "right" <> prettyVP ty2 v prettyValue (_ :+: _) v =   error $ "Non-VInj passed with sum type to prettyValue: " ++ show v- prettyValue _ (VNum d r)-  | denominator r == 1                       = text $ show (numerator r)-  | otherwise                                = text $ case d of+  | denominator r == 1 = text $ show (numerator r)+  | otherwise = text $ case d of       Fraction -> show (numerator r) ++ "/" ++ show (denominator r)-      Decimal  -> prettyDecimal r--prettyValue ty@(_ :->: _) _                  = prettyPlaceholder ty--prettyValue (TySet ty) (VBag xs)             = braces $ prettySequence ty "," (map fst xs)+      Decimal -> prettyDecimal r+prettyValue ty@(_ :->: _) _ = prettyPlaceholder ty+prettyValue (TySet ty) (VBag xs) = braces $ prettySequence ty "," (map fst xs) prettyValue (TySet _) v =   error $ "Non-VBag passed with Set type to prettyValue: " ++ show v-prettyValue (TyBag ty) (VBag xs)             = prettyBag ty xs+prettyValue (TyBag ty) (VBag xs) = prettyBag ty xs prettyValue (TyBag _) v =   error $ "Non-VBag passed with Bag type to prettyValue: " ++ show v--prettyValue (TyMap tyK tyV) (VMap m)         =+prettyValue (TyMap tyK tyV) (VMap m) =   "map" <> parens (braces (prettySequence (tyK :*: tyV) "," (assocsToValues m)))-  where-    assocsToValues = map (\(k,v) -> VPair (fromSimpleValue k) v) . M.assocs+ where+  assocsToValues = map (\(k, v) -> VPair (fromSimpleValue k) v) . M.assocs prettyValue (TyMap _ _) v =   error $ "Non-map value with map type passed to prettyValue: " ++ show v--prettyValue (TyGraph ty) (VGraph g)          =+prettyValue (TyGraph ty) (VGraph g) =   foldg     "emptyGraph"     (("vertex" <>) . prettyVP ty . fromSimpleValue)@@ -473,102 +513,34 @@     g prettyValue (TyGraph _) v =   error $ "Non-graph value with graph type passed to prettyValue: " ++ show v--prettyValue ty@TyAtom{} v =+prettyValue ty@TyAtom {} v =   error $ "Invalid atomic type passed to prettyValue: " ++ show ty ++ " " ++ show v--prettyValue ty@TyCon{} v =+prettyValue ty@TyCon {} v =   error $ "Invalid type constructor passed to prettyValue: " ++ show ty ++ " " ++ show v  -- | Pretty-print a value with guaranteed parentheses.  Do nothing for --   tuples; add an extra set of parens for other values.-prettyVP :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r Doc+prettyVP :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r (Doc ann) prettyVP ty@(_ :*: _) = prettyValue ty-prettyVP ty           = parens . prettyValue ty+prettyVP ty = parens . prettyValue ty -prettyPlaceholder :: Members '[Reader PA, LFresh] r => Type -> Sem r Doc+prettyPlaceholder :: Members '[Reader PA, LFresh] r => Type -> Sem r (Doc ann) prettyPlaceholder ty = "<" <> pretty ty <> ">" -prettyTuple :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r Doc+prettyTuple :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Value -> Sem r (Doc ann) prettyTuple (ty1 :*: ty2) (VPair v1 v2) = prettyValue ty1 v1 <> "," <+> prettyTuple ty2 v2-prettyTuple ty v                        = prettyValue ty v+prettyTuple ty v = prettyValue ty v  -- | 'prettySequence' pretty-prints a lists of values separated by a delimiter.-prettySequence :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Doc -> [Value] -> Sem r Doc+prettySequence :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> Doc ann -> [Value] -> Sem r (Doc ann) prettySequence ty del vs = hsep =<< punctuate (return del) (map (prettyValue ty) vs)  -- | Pretty-print a literal bag value.-prettyBag :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> [(Value,Integer)] -> Sem r Doc+prettyBag :: Members '[Input TyDefCtx, LFresh, Reader PA] r => Type -> [(Value, Integer)] -> Sem r (Doc ann) prettyBag _ [] = bag empty prettyBag ty vs-  | all ((==1) . snd) vs = bag $ prettySequence ty "," (map fst vs)-  | otherwise            = bag $ hsep =<< punctuate (return ",") (map prettyCount vs)-  where-    prettyCount (v,1) = prettyValue ty v-    prettyCount (v,n) = prettyValue ty v <+> "#" <+> text (show n)----------------------------------------------------------------- Pretty-printing for test results---------------------------------------------------------------prettyTestFailure-  :: Members '[Input TyDefCtx, LFresh, Reader PA] r-  => AProperty -> TestResult -> Sem r Doc-prettyTestFailure _    (TestResult True _ _)    = empty-prettyTestFailure prop (TestResult False r env) =-  prettyFailureReason prop r-  $+$-  prettyTestEnv "Counterexample:" env--prettyTestResult-  :: Members '[Input TyDefCtx, LFresh, Reader PA] r-  => AProperty -> TestResult -> Sem r Doc-prettyTestResult prop r | not (testIsOk r) = prettyTestFailure prop r-prettyTestResult prop (TestResult _ r _)   =-  ("Test passed:" <+> pretty (eraseProperty prop))-  $+$-  prettySuccessReason r--prettySuccessReason-  :: Members '[Input TyDefCtx, LFresh, Reader PA] r-  => TestReason -> Sem r Doc-prettySuccessReason (TestFound (TestResult _ _ vs)) = prettyTestEnv "Found example:" vs-prettySuccessReason (TestNotFound Exhaustive) = "No counterexamples exist."-prettySuccessReason (TestNotFound (Randomized n m)) =-  "Checked" <+> text (show (n + m)) <+> "possibilities without finding a counterexample."-prettySuccessReason _ = empty--prettyFailureReason-  :: Members '[Input TyDefCtx, LFresh, Reader PA] r-  => AProperty -> TestReason -> Sem r Doc-prettyFailureReason prop TestBool = "Test is false:" <+> pretty (eraseProperty prop)-prettyFailureReason prop (TestEqual ty v1 v2) =-  "Test result mismatch for:" <+> pretty (eraseProperty prop)-  $+$-  bulletList "-"-  [ "Left side:  " <> prettyValue ty v1-  , "Right side: " <> prettyValue ty v2-  ]-prettyFailureReason prop (TestRuntimeError e) =-  "Test failed:" <+> pretty (eraseProperty prop)-  $+$-  pretty (EvalErr e)-prettyFailureReason prop (TestFound (TestResult _ r _)) = prettyFailureReason prop r-prettyFailureReason prop (TestNotFound Exhaustive) =-  "No example exists:" <+> pretty (eraseProperty prop)-  $+$-  "All possible values were checked."-prettyFailureReason prop (TestNotFound (Randomized n m)) = do-  "No example was found:" <+> pretty (eraseProperty prop)-  $+$-  ("Checked" <+> text (show (n + m)) <+> "possibilities.")--prettyTestEnv-  :: Members '[Input TyDefCtx, LFresh, Reader PA] r-  => String -> TestEnv -> Sem r Doc-prettyTestEnv _ (TestEnv []) = empty-prettyTestEnv s (TestEnv vs) = text s $+$ nest 2 (vcat (map prettyBind vs))-  where-    maxNameLen = maximum . map (\(n, _, _) -> length n) $ vs-    prettyBind (x, ty, v) =-      text x <> text (replicate (maxNameLen - length x) ' ') <+> "=" <+> prettyValue ty v+  | all ((== 1) . snd) vs = bag $ prettySequence ty "," (map fst vs)+  | otherwise = bag $ hsep =<< punctuate (return ",") (map prettyCount vs)+ where+  prettyCount (v, 1) = prettyValue ty v+  prettyCount (v, n) = prettyValue ty v <+> "#" <+> text (show n)
stack.yaml view
@@ -2,7 +2,7 @@ # For more information, see: http://docs.haskellstack.org/en/stable/yaml_configuration/  # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)-resolver: lts-18.13+resolver: lts-21.13  # Local packages, usually specified by relative directory name packages:@@ -10,15 +10,12 @@  # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) extra-deps:-  - unbound-generics-0.4.0-  - simple-enumeration-0.2-  - oeis-0.3.10-  - capability-0.4.0.0@sha256:d86d85a1691ef0165c77c47ea72eac75c99d21fb82947efe8b2f758991cf1837,3345-  - polysemy-1.6.0.0@sha256:29a73b1bf3d0049b12041016b7ee25e76bd8f6e99f9c37c2dde2b46368246697,6184-  - polysemy-plugin-0.4.0.0-  - polysemy-zoo-0.7.0.1@sha256:60c2921df95f61d43222a75adde4f330e9510320b416132838a354cd81b4bcc5,3846-  - compact-0.2.0.0@sha256:75ef98cb51201b4a0d6de95cbbb62be6237c092a3d594737346c70c5d56c2380,2413-  - constraints-0.12   # needed since polysemy-zoo hasn't updated upper bound to allow 0.13+  - unbound-generics-0.4.2+    # unbound-generics-0.4.3 contains breaking changes (adding methods to Subst class)+  - simple-enumeration-0.2.1@sha256:8625b269c1650d3dd0e3887351c153049f4369853e0d525219e07480ea004b9f,1178+  - polysemy-zoo-0.8.2.0+    # - HTTP-4000.3.16@sha256:6042643c15a0b43e522a6693f1e322f05000d519543a84149cb80aeffee34f71,5947+    # HTTP-4000.3 needed for oeis-0.3.10  # Override default flag values for local packages and extra-deps flags: {}
test/compile-misc/expected view
@@ -2,8 +2,8 @@ λx, y. x (λ_. (λk. (λy. (λp, q. p) (fst y) (snd y)) (frac (2 / 3))) (λ_1. matchErr)) unit (λ_. (λk. case (3 < 2) of {-            left _1 -> k unit-            right px -> (λ_2. 1) px-          }) (λ_1. (λk. 17) (λ_2. matchErr))) unit+  left _1 -> k unit+  right px -> (λ_2. 1) px+  }) (λ_1. (λk. 17) (λ_2. matchErr))) unit (10 choose right (5, left unit)) 5!
test/containers-filter/expected view
@@ -1,4 +1,3 @@-Loading list.disco... [4, 5, 6, 7, 8, 9, 10] ⟅4, 5, 6, 7, 8, 9, 10⟆ {4, 5, 6, 7, 8, 9, 10}
test/containers-reduce/containers-reduce.disco view
@@ -1,6 +1,6 @@ import num -!!! ∀ x : ℕ. (x > 0) ==> reduce(~*~, 1, factor x) == x+!!! ∀ x : ℕ. reduce(~*~, 1, factor (x+1)) == (x+1) dummy : Unit dummy = unit 
+ test/error-nonlinear/expected view
@@ -0,0 +1,5 @@+While checking REPL.f:+  Error: pattern (x, x) contains duplicate variable x.+  https://disco-lang.readthedocs.io/en/latest/reference/nonlinear.html+Error: pattern (x, left(y, x)) contains duplicate variable x.+https://disco-lang.readthedocs.io/en/latest/reference/nonlinear.html
+ test/error-nonlinear/input view
@@ -0,0 +1,3 @@+f : N*N -> N+f(x,x) = 3+{? 5 when (2, left(3,5)) is (x, left(y,x)), 7 otherwise ?}
test/error-parse-RHS/expected view
@@ -5,10 +5,10 @@ unexpected end of input expecting expression or operator -1:18:+1:22:   |-1 | g(x) = {? 5 if x =< 5, 6 otherwise ?}-  |                  ^^-unexpected "=<"-expecting "?}", "if", "let", "when", ',', expression, operator, or type annotation+1 | g(x) = {? 5 if x > 5 .+ 4, 6 otherwise ?}+  |                      ^^+unexpected ".+"+expecting "?}", "if", "is", "let", "when", ',', expression, operator, or type annotation 
test/error-parse-RHS/input view
@@ -2,4 +2,4 @@ f(x) = x +  g : N -> N-g(x) = {? 5 if x =< 5, 6 otherwise ?}+g(x) = {? 5 if x > 5 .+ 4, 6 otherwise ?}
test/error-pattype/expected view
@@ -10,7 +10,7 @@     (x1, y)   is supposed to have type     ℕ,-  but instead it has a product type.+  but instead it has a pair type.   https://disco-lang.readthedocs.io/en/latest/reference/pattern-type.html While checking REPL.h:   Error: the shape of two types does not match.
− test/lib-oeis/expected
@@ -1,8 +0,0 @@-Loading oeis.disco...-right("https://oeis.org/A000045")-left(■)-left(■)-[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131]-[]-[1, 10011]-[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131]
− test/lib-oeis/input
@@ -1,22 +0,0 @@-import oeis---- valid sequence-lookupSequence [1,1,2,3,5]---- empty list-lookupSequence []---- unknown sequence-lookupSequence [1,10011]---- known sequence-extendSequence [1,3,5,7]---- empty list-extendSequence []---- unknown sequence-extendSequence [1,10011]---- extend a long sequence-extendSequence [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99]
test/list-comp/expected view
@@ -1,4 +1,3 @@-Loading list.disco... Loading num.disco... [1, 2, 3] []
test/list-poly/expected view
@@ -1,4 +1,3 @@-Loading list.disco... [1, 2, 3, 4, 5] [5, 4, 3, 2, 1] [1, 2, 3, 4, 5]
test/logic-cmp/expected view
@@ -5,6 +5,7 @@ true true false+false true false false@@ -12,7 +13,10 @@ true false false+false true+true+false true false true
test/logic-cmp/input view
@@ -5,16 +5,20 @@ 1 <= 2 2 <= 2 3 <= 2+3 =< 2 3 > 1 3 > 3 3 > 4 3 >= 1 3 >= 3 3 >= 4+3 => 4 3 == 5 3 == 3 3 /= 5 3 /= 3+3 != 5+3 != 3 (1,2) < (1,3) (1,3) < (2,1) (2,2) < (2,1)
test/map-basic/expected view
@@ -1,4 +1,3 @@-Loading list.disco... map : Set(ℕ × a) → Map(ℕ, a) map({(1, 3), (4, 6)}) : Map(ℕ, ℕ) map({(1, 3), (4, 6)})
+ test/parse-patguard/expected view
@@ -0,0 +1,7 @@+1:18:+  |+1 | {? 1 if 3 is (x, ?}+  |                  ^^+unexpected "?}"+expecting expression+
+ test/parse-patguard/input view
@@ -0,0 +1,1 @@+{? 1 if 3 is (x, ?}
test/parse-quantifiers/expected view
@@ -1,37 +1,96 @@ TAbs_ Lam () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3])) TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Lam () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TAbs_ Lam () (<[PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0])))+TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TAbs_ Lam () (<[PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0])))+TAbs_ Lam () (<[PVar_ () x]> TAbs_ Lam () (<[PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0]))) TAbs_ Lam () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3])) TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Lam () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TAbs_ Lam () (<[PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0])))+TAbs_ Lam () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TAbs_ Lam () (<[PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0])))+TAbs_ Lam () (<[PVar_ () x]> TAbs_ Lam () (<[PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 1@0,TVar_ () 0@0]))) 8 Error: the shape of two types does not match. https://disco-lang.readthedocs.io/en/latest/reference/shape-mismatch.html 8 8-8-TAbs_ Ex () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))+1:16:+  |+1 | :parse exists x. x > 3+  |                ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+1:23:+  |+1 | :parse exists (x:N), y. x > y+  |                       ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Ex () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Ex () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))+1:16:+  |+1 | :parse exists x, (y:F). x > y+  |                ^+Variables introduced by ∀ or ∃ must have a type++1:11:+  |+1 | :parse ∃ x. x > 3+  |           ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+1:18:+  |+1 | :parse ∃ (x:N), y. x > y+  |                  ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ Ex () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ Ex () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ All () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))+1:11:+  |+1 | :parse ∃ x, (y:F). x > y+  |           ^+Variables introduced by ∀ or ∃ must have a type++1:16:+  |+1 | :parse forall x. x > 3+  |                ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+1:23:+  |+1 | :parse forall (x:N), y. x > y+  |                       ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ All () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ All () (<[PVar_ () x]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))+1:16:+  |+1 | :parse forall x, (y:F). x > y+  |                ^+Variables introduced by ∀ or ∃ must have a type++1:11:+  |+1 | :parse ∀ x. x > 3+  |           ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TNat_ () 3]))-TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PVar_ () y]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+1:18:+  |+1 | :parse ∀ (x:N), y. x > y+  |                  ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ All () (<[PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))-TAbs_ All () (<[PVar_ () x,PAscr_ () (PVar_ () y) (TyAtom (ABase F))]> TApp_ () (TPrim_ () (PrimBOp Gt)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1]))+1:11:+  |+1 | :parse ∀ x, (y:F). x > y+  |           ^+Variables introduced by ∀ or ∃ must have a type+ TAbs_ All () (<[PTup_ () [PAscr_ () (PVar_ () x) (TyAtom (ABase N)),PAscr_ () (PVar_ () y) (TyAtom (ABase N)),PAscr_ () (PVar_ () z) (TyAtom (ABase N))]]> TApp_ () (TPrim_ () (PrimBOp Impl)) (TTup_ () [TApp_ () (TPrim_ () (PrimBOp And)) (TTup_ () [TParens_ () (TApp_ () (TPrim_ () (PrimBOp Eq)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@1])),TParens_ () (TApp_ () (TPrim_ () (PrimBOp Eq)) (TTup_ () [TVar_ () 0@1,TVar_ () 0@2]))]),TApp_ () (TPrim_ () (PrimBOp Eq)) (TTup_ () [TVar_ () 0@0,TVar_ () 0@2])]))
test/parse-quantifiers/input view
@@ -1,17 +1,16 @@ :parse λ x. x > 3 :parse λ (x:N). x > 3-:parse λ (x:N), y. x > y-:parse λ (x:N), (y:F). x > y-:parse λ x, (y:F). x > y+:parse λ (x:N). λy. x > y+:parse λ x:N. λy:F. x > y+:parse λ x. λy:F. x > y :parse \ x. x > 3 :parse \ (x:N). x > 3-:parse \ (x:N), y. x > y-:parse \ (x:N), (y:F). x > y-:parse \ x, (y:F). x > y-(\x, y. x + y) 3 5+:parse \ (x:N). \y. x > y+:parse \ (x:N). \(y:F). x > y+:parse \ x. \(y:F). x > y+(\x. \y. x + y) 3 5 (\(x : N, y : N). x + y) 3 5 (\(x : N, y : N). x + y) (3, 5)-(\(x:N), (y:N). x + y) 3 5 (\(x:N). \(y:N). x + y) 3 5 :parse exists x. x > 3 :parse exists (x:N). x > 3
test/poly-bad/input view
@@ -1,6 +1,6 @@ let f : a -> a = \x. x+1 in f let f : a -> a = \x. x+x in f-let f : a -> b -> a = \x, y. y in f+let f : a -> b -> a = \x. \y. y in f let f : a*b -> b = \p. {? a when p is (a,_) ?} in f let f : List(a) -> List(a) = \x. list (bag x) in f-let f : a -> a -> Bool = \x, y. x < y in f+let f : a -> a -> Bool = \x. \y. x < y in f
test/poly-infer-sort/expected view
@@ -3,9 +3,9 @@ let f : (a → a) → a → a = λg. λx. g(g(x)) in f(λx. x / x) : 𝔽 → 𝔽 let f : (a → a) → a → a = λg. λx. g(g(x)) in f(λx. -x / x) : ℚ → ℚ λx. x : a → a-λx, y. x : a1 → a → a1-λx, y, z. x + y + z : ℕ → ℕ → ℕ → ℕ-λx, y : ℕ. x - y : ℤ → ℕ → ℤ-λw, x : ℕ, y, z : 𝔽. w - x + y + z : ℚ → ℕ → ℚ → 𝔽 → ℚ+λx. λy. x : a1 → a → a1+λx. λy. λz. x + y + z : ℕ → ℕ → ℕ → ℕ+λx. λy : ℕ. x - y : ℤ → ℕ → ℤ+λw. λx : ℕ. λy. λz : 𝔽. w - x + y + z : ℚ → ℕ → ℚ → 𝔽 → ℚ Error: typechecking failed. https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html
test/poly-infer-sort/input view
@@ -3,8 +3,8 @@ :type let f : (a -> a) -> a -> a = \g. (\x. g (g x)) in f (\x. x/x) :type let f : (a -> a) -> a -> a = \g. (\x. g (g x)) in f (\x. -x/x) :type \x.x-:type \x, y. x-:type \x, y, z. x + y + z-:type \x, (y:Nat). x - y-:type \w, (x:Nat), y, (z : Frac). w - x + y + z-\x, (y:Bool). x - y+:type \x. \y. x+:type \x. \y. \z. x + y + z+:type \x. \(y:Nat). x - y+:type \w. \x:Nat. \y. \z : Frac. w - x + y + z+\x. \y:Bool. x - y
test/poly-instantiate/expected view
@@ -1,12 +1,12 @@ Loading poly-instantiate.disco... Loaded. foldr : (a → r → r) → r → List(a) → r-foldr(λx, y. x) : a → List(a) → a-foldr(λx, y. y) : r → List(a) → r-foldr(λx, y. x + 1) : ℕ → List(ℕ) → ℕ-foldr(λx, y. y + 1) : ℕ → List(a) → ℕ-foldr(λx, y. y + 1)(1) : List(a) → ℕ-foldr(λx, y. y + 1)(-1) : List(a) → ℤ-foldr(λx, y. x)(false) : List(Bool) → Bool-foldr(λx, y. x + 1)(1 / 2) : List(ℕ) → 𝔽-foldr(λx, y. x - 1)(1 / 2) : List(ℤ) → ℚ+foldr(λx. λy. x) : a → List(a) → a+foldr(λx. λy. y) : r → List(a) → r+foldr(λx. λy. x + 1) : ℕ → List(ℕ) → ℕ+foldr(λx. λy. y + 1) : ℕ → List(a) → ℕ+foldr(λx. λy. y + 1)(1) : List(a) → ℕ+foldr(λx. λy. y + 1)(-1) : List(a) → ℤ+foldr(λx. λy. x)(false) : List(Bool) → Bool+foldr(λx. λy. x + 1)(1 / 2) : List(ℕ) → 𝔽+foldr(λx. λy. x - 1)(1 / 2) : List(ℤ) → ℚ
test/poly-instantiate/input view
@@ -1,11 +1,11 @@ :load test/poly-instantiate/poly-instantiate.disco :type foldr-:type foldr (\x, y. x)-:type foldr (\x, y. y)-:type foldr (\x, y. x+1)-:type foldr (\x, y. y+1)-:type foldr (\x, y. y+1) 1-:type foldr (\x, y. y+1) (-1)-:type foldr (\x, y. x) false-:type foldr (\x, y. x+1) (1/2)-:type foldr (\x, y. x-1) (1/2)+:type foldr (\x. \y. x)+:type foldr (\x. \y. y)+:type foldr (\x. \y. x+1)+:type foldr (\x. \y. y+1)+:type foldr (\x. \y. y+1) 1+:type foldr (\x. \y. y+1) (-1)+:type foldr (\x. \y. x) false+:type foldr (\x. \y. x+1) (1/2)+:type foldr (\x. \y. x-1) (1/2)
− test/pretty-issue258/expected
@@ -1,12 +0,0 @@-Loading catalan.disco...-Loading list.disco...-Loading oeis.disco...-Loaded.-treesOfSize : ℕ → List(BT)-treesOfSize(0) = [left(■)]-treesOfSize(k + 1) = [right(l, r) | x in [0 .. k], l in treesOfSize(x), r in treesOfSize(k .- x)]-Loading tree.disco...-Loaded.-treeFold : r11 × (ℕ × r11 × r11 → r11) × Tree → r11-treeFold(x, f, left(■)) = x-treeFold(x, f, right(n, l, r)) = f(n, treeFold(x, f, l), treeFold(x, f, r))
− test/pretty-issue258/input
@@ -1,4 +0,0 @@-:load example/catalan.disco-:defn treesOfSize-:load example/tree.disco-:defn treeFold
test/prop-basic/expected view
@@ -1,12 +1,12 @@ Loading prop-basic.disco... Loaded.-  - Test passed: injective(λx. x * 2)+  - Possibly true: injective(λx. x * 2)     Checked 100 possibilities without finding a counterexample.-  - Test passed: idempotent(λx. x max 10)+  - Possibly true: idempotent(λx. x max 10)     Checked 100 possibilities without finding a counterexample.-  - Test passed: commutative(λ(a, b). a * b)+  - Possibly true: commutative(λ(a, b). a * b)     Checked 100 possibilities without finding a counterexample.-  - Test passed: associative(λ(a, b). a + b)+  - Possibly true: associative(λ(a, b). a + b)     Checked 100 possibilities without finding a counterexample.-  - Test passed: identityFor(0, λ(a, b). a max b)+  - Possibly true: identityFor(0, λ(a, b). a max b)     Checked 100 possibilities without finding a counterexample.
+ test/prop-binary/expected view
@@ -0,0 +1,401 @@+  - Certainly true: (∃n. 2 * n == 0) /\ (∃n. 2 * n == 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∃n. 2 * n == 0) /\ (∀n. n >= 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∃n. 2 * n == 0) /\ (∀n. n > 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∃n. 2 * n == 0) /\ (∃n. 2 * n == 1)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Possibly true: (∀n. n >= 0) /\ (∃n. 2 * n == 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∀n. n >= 0) /\ (∀n. n >= 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∀n. n >= 0) /\ (∀n. n > 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∀n. n >= 0) /\ (∃n. 2 * n == 1)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly false: (∀n. n > 0) /\ (∃n. 2 * n == 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Certainly false: (∀n. n > 0) /\ (∀n. n >= 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∀n. n > 0) /\ (∀n. n > 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Certainly false: (∀n. n > 0) /\ (∃n. 2 * n == 1)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Possibly false: (∃n. 2 * n == 1) /\ (∃n. 2 * n == 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly false: (∃n. 2 * n == 1) /\ (∀n. n >= 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∃n. 2 * n == 1) /\ (∀n. n > 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∃n. 2 * n == 1) /\ (∃n. 2 * n == 1)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∃n. 2 * n == 0) \/ (∃n. 2 * n == 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Certainly true: (∃n. 2 * n == 0) \/ (∀n. n >= 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly true: (∃n. 2 * n == 0) \/ (∀n. n > 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Certainly true: (∃n. 2 * n == 0) \/ (∃n. 2 * n == 1)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∀n. n >= 0) \/ (∃n. 2 * n == 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∀n. n >= 0) \/ (∀n. n >= 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Possibly true: (∀n. n >= 0) \/ (∀n. n > 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly true: (∀n. n >= 0) \/ (∃n. 2 * n == 1)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∀n. n > 0) \/ (∃n. 2 * n == 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∀n. n > 0) \/ (∀n. n >= 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∀n. n > 0) \/ (∀n. n > 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∀n. n > 0) \/ (∃n. 2 * n == 1)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∃n. 2 * n == 1) \/ (∃n. 2 * n == 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∃n. 2 * n == 1) \/ (∀n. n >= 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Possibly false: (∃n. 2 * n == 1) \/ (∀n. n > 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∃n. 2 * n == 1) \/ (∃n. 2 * n == 1)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∃n. 2 * n == 0) -> (∃n. 2 * n == 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∃n. 2 * n == 0) -> (∀n. n >= 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly false: (∃n. 2 * n == 0) -> (∀n. n > 0)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∃n. 2 * n == 0) -> (∃n. 2 * n == 1)+    - Left side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∀n. n >= 0) -> (∃n. 2 * n == 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∀n. n >= 0) -> (∀n. n >= 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Possibly false: (∀n. n >= 0) -> (∀n. n > 0)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly false: (∀n. n >= 0) -> (∃n. 2 * n == 1)+    - Left side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∀n. n > 0) -> (∃n. 2 * n == 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Certainly true: (∀n. n > 0) -> (∀n. n >= 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Certainly true: (∀n. n > 0) -> (∀n. n > 0)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Certainly true: (∀n. n > 0) -> (∃n. 2 * n == 1)+    - Left side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Certainly true: (∃n. 2 * n == 1) -> (∃n. 2 * n == 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly true: ∃n. 2 * n == 0+        Found example:+          n = 0+  - Possibly true: (∃n. 2 * n == 1) -> (∀n. n >= 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly true: ∀n. n >= 0+        Checked 100 possibilities without finding a counterexample.+  - Possibly true: (∃n. 2 * n == 1) -> (∀n. n > 0)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Certainly false: ∀n. n > 0+        Found counterexample:+          n = 0+  - Possibly true: (∃n. 2 * n == 1) -> (∃n. 2 * n == 1)+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+  - Possibly true: (∃n. 2 * n == 1) -> (∃n. 2 * n == 1) /\ (5 == 5 \/ (∀n. n < 5))+    - Left side:+        Possibly false: ∃n. 2 * n == 1+        No example was found; checked 100 possibilities.+    - Right side:+        Possibly false: (∃n. 2 * n == 1) /\ (5 == 5 \/ (∀n. n < 5))+        - Left side:+            Possibly false: ∃n. 2 * n == 1+            No example was found; checked 100 possibilities.+        - Right side:+            Certainly true: 5 == 5 \/ (∀n. n < 5)+            - Left side:+                Certainly true: 5 == 5+            - Right side:+                Certainly false: ∀n. n < 5+                Found counterexample:+                  n = 5
+ test/prop-binary/input view
@@ -0,0 +1,49 @@+:test (exists n:N. 2n == 0) /\ (exists n:N. 2n == 0)+:test (exists n:N. 2n == 0) /\ (forall n:N. n >= 0)+:test (exists n:N. 2n == 0) /\ (forall n:N. n > 0)+:test (exists n:N. 2n == 0) /\ (exists n:N. 2n == 1)+:test (forall n:N. n >= 0) /\ (exists n:N. 2n == 0)+:test (forall n:N. n >= 0) /\ (forall n:N. n >= 0)+:test (forall n:N. n >= 0) /\ (forall n:N. n > 0)+:test (forall n:N. n >= 0) /\ (exists n:N. 2n == 1)+:test (forall n:N. n > 0) /\ (exists n:N. 2n == 0)+:test (forall n:N. n > 0) /\ (forall n:N. n >= 0)+:test (forall n:N. n > 0) /\ (forall n:N. n > 0)+:test (forall n:N. n > 0) /\ (exists n:N. 2n == 1)+:test (exists n:N. 2n == 1) /\ (exists n:N. 2n == 0)+:test (exists n:N. 2n == 1) /\ (forall n:N. n >= 0)+:test (exists n:N. 2n == 1) /\ (forall n:N. n > 0)+:test (exists n:N. 2n == 1) /\ (exists n:N. 2n == 1)+:test (exists n:N. 2n == 0) \/ (exists n:N. 2n == 0)+:test (exists n:N. 2n == 0) \/ (forall n:N. n >= 0)+:test (exists n:N. 2n == 0) \/ (forall n:N. n > 0)+:test (exists n:N. 2n == 0) \/ (exists n:N. 2n == 1)+:test (forall n:N. n >= 0) \/ (exists n:N. 2n == 0)+:test (forall n:N. n >= 0) \/ (forall n:N. n >= 0)+:test (forall n:N. n >= 0) \/ (forall n:N. n > 0)+:test (forall n:N. n >= 0) \/ (exists n:N. 2n == 1)+:test (forall n:N. n > 0) \/ (exists n:N. 2n == 0)+:test (forall n:N. n > 0) \/ (forall n:N. n >= 0)+:test (forall n:N. n > 0) \/ (forall n:N. n > 0)+:test (forall n:N. n > 0) \/ (exists n:N. 2n == 1)+:test (exists n:N. 2n == 1) \/ (exists n:N. 2n == 0)+:test (exists n:N. 2n == 1) \/ (forall n:N. n >= 0)+:test (exists n:N. 2n == 1) \/ (forall n:N. n > 0)+:test (exists n:N. 2n == 1) \/ (exists n:N. 2n == 1)+:test (exists n:N. 2n == 0) -> (exists n:N. 2n == 0)+:test (exists n:N. 2n == 0) -> (forall n:N. n >= 0)+:test (exists n:N. 2n == 0) -> (forall n:N. n > 0)+:test (exists n:N. 2n == 0) -> (exists n:N. 2n == 1)+:test (forall n:N. n >= 0) -> (exists n:N. 2n == 0)+:test (forall n:N. n >= 0) -> (forall n:N. n >= 0)+:test (forall n:N. n >= 0) -> (forall n:N. n > 0)+:test (forall n:N. n >= 0) -> (exists n:N. 2n == 1)+:test (forall n:N. n > 0) -> (exists n:N. 2n == 0)+:test (forall n:N. n > 0) -> (forall n:N. n >= 0)+:test (forall n:N. n > 0) -> (forall n:N. n > 0)+:test (forall n:N. n > 0) -> (exists n:N. 2n == 1)+:test (exists n:N. 2n == 1) -> (exists n:N. 2n == 0)+:test (exists n:N. 2n == 1) -> (forall n:N. n >= 0)+:test (exists n:N. 2n == 1) -> (forall n:N. n > 0)+:test (exists n:N. 2n == 1) -> (exists n:N. 2n == 1)+:test (exists n:N. 2n == 1) -> ((exists n:N. 2n == 1) /\ (5 == 5 \/ (forall n:N. n < 5)))
test/prop-fail/expected view
@@ -1,21 +1,20 @@ Loading bad-tests.disco... Running tests...   badmap:-  - Test failed: badmap(λx. x / 0)([3, 4, 5]) =!= [6, 7, 8]-    Error: division by zero.-  - Test result mismatch for: badmap(λx. x)([1, 2]) =!= [1, 2]+  - Certainly false: badmap(λx. x / 0)([3, 4, 5]) =!= [6, 7, 8]+    Test failed with an error:+      Error: division by zero.+  - Certainly false: badmap(λx. x)([1, 2]) =!= [1, 2]     - Left side:  [1, 1, 2, 2, 3]     - Right side: [1, 2]-  - Test is false: badmap(λx. x + 1)([3, 4]) > [5, 6]+  - Certainly false: badmap(λx. x + 1)([3, 4]) > [5, 6]   divide:-  - Test failed: ∀a, b. divide(a)(b) * b =!= a-    Error: division by zero.-    Counterexample:-      a = 0-      b = 0-  - Test is false: ∀a. divide(a)(2) < a-    Counterexample:+  - Certainly false: ∀a, b. divide(a)(b) * b =!= a+    Test failed with an error:+      Error: division by zero.+  - Certainly false: ∀a. divide(a)(2) < a+    Found counterexample:       a = 0-  - No example was found: ∃a. divide(a)(2) =!= abs(a) + 1-    Checked 50 possibilities.+  - Possibly false: ∃a. divide(a)(2) =!= abs(a) + 1+    No example was found; checked 50 possibilities. Loaded.
test/prop-higher-order/expected view
@@ -1,19 +1,10 @@ Loading higher-order.disco...-Running tests...-  pand: OK-  por: OK Loaded.-  - Test passed: ∀x. por(x =!= 0, ∃n. n * x >= 1)+  - Possibly true: ∀x. x =!= 0 \/ (∃n. n * x >= 1)     Checked 100 possibilities without finding a counterexample.-  - Test passed: ∀f. any([∀x. f(x) =!= not x, ∀x. f(x) =!= x, ∀x. f(x) =!= false, ∀x. f(x) =!= true])-    No counterexamples exist.-  - Test is false: all([true, true, true, false, true])-    Counterexample:-      and_side = right(■)-      and_side = right(■)-      and_side = right(■)-      and_side = left(■)-  - Test passed: ∃k. hasFactors(2 ^ k + 1)+  - Certainly true: ∀f. any([∀x. f(x) =!= not x, ∀x. f(x) =!= x, ∀x. f(x) =!= false, ∀x. f(x) =!= true])+    No counterexamples exist; all possible values were checked.+  - Certainly false: all([true, true, true, false, true])+  - Certainly true: ∃k. hasFactors(2 ^ k + 1)     Found example:       k = 3-      n = 3
test/prop-higher-order/higher-order.disco view
@@ -1,30 +1,8 @@-||| 'and' for propositions.--!!! pand(true, true)--pand : Prop * Prop -> Prop-pand(p, q) = forall and_side : Unit + Unit. {?-    p when and_side is left _,-    q otherwise-  ?}- all : List(Prop) -> Prop-all ps = reduce(pand, true, ps)--||| 'or' for propositions.--!!! por(false, true)-!!! por(true, false)-!!! por(true, true)--por : Prop * Prop -> Prop-por(p, q) = exists or_side : Unit + Unit. {?-    p when or_side is left _,-    q otherwise-  ?}+all ps = reduce(~/\~, true, ps)  any : List(Prop) -> Prop-any ps = reduce(por, false, ps)+any ps = reduce(~\/~, false, ps)  ||| Assert that a proposition holds on some number in a range. 
test/prop-higher-order/input view
@@ -1,5 +1,5 @@ :load test/prop-higher-order/higher-order.disco-:test forall x:F. por(x =!= 0, exists n:N. n * x >= 1)+:test forall x:F. (x =!= 0) \/ (exists n:N. n * x >= 1) :test forall f:Bool->Bool. any [forall (x: Bool). f x =!= not x, forall (x: Bool). f x =!= x, forall (x: Bool). f x =!= false, forall (x: Bool). f x =!= true] :test all [true, true, true, false, true] :test exists k:N. hasFactors(2^k + 1)
test/prop-tests/prop-tests.disco view
@@ -48,7 +48,7 @@ !!! ∀ v:Void. 2 == 3    -- this is true! !!! ∀ u:Unit. u == u    -- there is only one Unit value !!! ∀ l:List(Void). l == l-!!! ∀ l:List(Unit). l == reverse l+-- !!! ∀ l:List(Unit). l == reverse l    -- too slow! !!! ∀ bs : List(Bool). toBin (fromBin bs) == bs !!! ∀ q : F. q >= 0 !!! ∀ p : N * N. {? x + y when p is (x,y) ?} >= 0
test/prop-type/expected view
@@ -4,9 +4,6 @@ ∃x : ℕ. ∃y : ℕ. x > y : Prop ∀x : ℕ, y : ℕ, z : ℕ. x + y + z == x + (y + z) : Prop ∃x : ℕ. ∀y : ℕ. x <= y : Prop-Error: typechecking failed.-https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html-Error: typechecking failed.-https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html-Error: typechecking failed.-https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html+not(∀x : Bool. true \/ x) : Prop+(∀x : Void. false) /\ true : Prop+(∀x : Void. false) \/ true : Prop
test/repl-ann/expected view
@@ -1,3 +1,3 @@ Loading num.disco... factor : ℕ → Bag(ℕ)-(λx : ℤ, y : a. (~-~ : ℤ × ℤ → ℤ)((x : ℤ, 7 : ℤ) : ℤ × ℤ) : ℤ) : ℤ → a3 → ℤ+(λx : ℤ. (λy : a. (~-~ : ℤ × ℤ → ℤ)((x : ℤ, 7 : ℤ) : ℤ × ℤ) : ℤ) : a3 → ℤ) : ℤ → a3 → ℤ
test/repl-ann/input view
@@ -1,3 +1,3 @@ import num :ann factor-:ann \x, y. x - 7+:ann \x. \y. x - 7
test/repl-doc/expected view
@@ -1,3 +1,7 @@+append : List(a) × List(a) → List(a)++Append two lists into a single list.+ Loading doc.disco... Running tests...   f: OK
test/repl-doc/input view
@@ -1,3 +1,4 @@+:doc append :load test/repl-doc/doc.disco :doc P :doc f
test/repl-proptest/expected view
@@ -1,12 +1,12 @@-  - Test passed: not false-  - Test passed: {1, 2} =!= {2, 1}-  - Test passed: ∃a, b. (a /\ b) =!= (a \/ b)+  - Certainly true: not false+  - Certainly true: {1, 2} =!= {2, 1}+    - Left side:  {1, 2}+    - Right side: {1, 2}+  - Certainly true: ∃a, b. (a /\ b) =!= (a \/ b)     Found example:       a = false       b = false-  - Test result mismatch for: ∀a, b. (a /\ b) =!= (a \/ b)-    - Left side:  false-    - Right side: true-    Counterexample:+  - Certainly false: ∀a, b. (a /\ b) =!= (a \/ b)+    Found counterexample:       a = false       b = true
test/syntax-clause/input view
@@ -1,3 +1,3 @@ :load test/syntax-clause/clauses.disco-zipWithN (\x, y. x*y + 1) [1,2,3] [4,5,6]+zipWithN (\x. \y. x*y + 1) [1,2,3] [4,5,6] append ([1,2,3], [4,5,6])
+ test/syntax-ellipsis/expected view
@@ -0,0 +1,10 @@+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]+[1, 2, 3, 4]
+ test/syntax-ellipsis/input view
@@ -0,0 +1,10 @@+[1 .. 4]+[1 ,.. 4]+[1, .. 4]+[1,..4]+[1 , .. , 4]+[1 .. , 4]+[1, 2 .. 4]+[1, 2, .. 4]+[1, 2, .., 4]+[1, 2 .., 4]
test/syntax-lambda-pat/expected view
@@ -4,4 +4,4 @@ 10 ((1, 3), 2, 4) 42-λf, (a, b). f(a)(b) : (a2 → a1 → a) → a2 × a1 → a+λf. λ(a, b). f(a)(b) : (a2 → a1 → a) → a2 × a1 → a
test/syntax-lambda-pat/input view
@@ -2,6 +2,6 @@ let f2 = \(x:N, y:N).     x + y in f2 (1, 2) let f3 = \((x, y) : N*N). x + y in f3 (1, 2) let h = \left (x, y). x in h (left (10, -10))-let fwop = \(x1, y1), (x2, y2). ((x1, x2), (y1, y2)) in fwop (1, 2) (3, 4)-let uncurry = \f, (a, b). f a b in uncurry (\a, b. a + b) (40, 2)-:type         \f, (a, b). f a b+let fwop = \((x1, y1), (x2, y2)). ((x1, x2), (y1, y2)) in fwop((1, 2), (3, 4))+let uncurry = \f. \(a, b). f a b in uncurry (\a. \b. a + b) (40, 2)+:type         \f. \(a, b). f a b
test/syntax-lambda/expected view
@@ -6,9 +6,17 @@ let f = λx. x + 1 : ℕ in f : ℕ → ℕ (9, 8) 5-λx : ℤ, y : ℕ. x * y : ℤ → ℕ → ℤ+λx : ℤ. λy : ℕ. x * y : ℤ → ℕ → ℤ [false, true, true]-let f = λg : ℤ → ℕ → Bool. [g(1)(1), g(1)(2), g(-1)(0)] in f(λx, y : ℤ. x + 1 == y) : List(Bool)+let f = λg : ℤ → ℕ → Bool. [g(1)(1), g(1)(2), g(-1)(0)] in f(λx. λy : ℤ. x + 1 == y) : List(Bool) 3 TAbs_ Lam () (<[PWild_ ()]> TNat_ () 3) 3+1:13:+  |+1 | :parse \x, y. x+  |             ^+Anonymous functions (lambdas) can only have a single argument.+Instead of \x, y. ... you can write  \x. \y. ...+https://disco-lang.readthedocs.io/en/latest/reference/anonymous-func.html+
test/syntax-lambda/input view
@@ -4,11 +4,12 @@ :type let f = (λx. x + 1) : N -> N in f :type let f = λx. (x + 1 : N) in f :type let f = λx. x + 1 : N in f-let g = λx:N, b:Bool.{? x*x if b, x+2 otherwise ?} in (g 3 true, g 6 false)+let g = λx:N. λb:Bool.{? x*x if b, x+2 otherwise ?} in (g 3 true, g 6 false) let q = \ (f : (N -> N) -> N) . f (\(x:N) . x*x) in q (\g. g 1 + g 2)-:type (\ x:Z, y:N . x * y)-let f = \(g : Z -> N -> Bool).[g 1 1, g 1 2, g (-1) 0] in f (\x, y:Z. x + 1 == y)-:type let f = \(g : Z -> N -> Bool).[g 1 1, g 1 2, g (-1) 0] in f (\x, y:Z. x + 1 == y)+:type (\ x:Z. \y:N . x * y)+let f = \(g : Z -> N -> Bool).[g 1 1, g 1 2, g (-1) 0] in f (\x. \y:Z. x + 1 == y)+:type let f = \(g : Z -> N -> Bool).[g 1 1, g 1 2, g (-1) 0] in f (\x. \y:Z. x + 1 == y) let f : N -> N -> N = \x.\y.x+y in f 1 2 :parse \_.3 (\_.3) "hello"+:parse \x, y. x
test/types-192/expected view
@@ -1,1 +1,1 @@-λx, y, z. x + y / z : 𝔽 → 𝔽 → 𝔽 → 𝔽+λx. λy. λz. x + y / z : 𝔽 → 𝔽 → 𝔽 → 𝔽
test/types-192/input view
@@ -1,1 +1,1 @@-:type \x,y,z. x + y / z+:type \x. \y. \z. x + y / z
test/types-306/expected view
@@ -1,13 +1,13 @@-Error: the shape of two types does not match.-https://disco-lang.readthedocs.io/en/latest/reference/shape-mismatch.html-Error: the shape of two types does not match.-https://disco-lang.readthedocs.io/en/latest/reference/shape-mismatch.html+Error: values of type a3 → a4 cannot be compared.+https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html+Error: values of type a8 → a9 cannot be compared.+https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html Error: values of type Bool → Bool cannot be compared. https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html-Error: values of type a5 → a6 cannot be compared.+Error: values of type a6 → a7 cannot be compared. https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html-Error: the shape of two types does not match.-https://disco-lang.readthedocs.io/en/latest/reference/shape-mismatch.html-Error: the shape of two types does not match.-https://disco-lang.readthedocs.io/en/latest/reference/shape-mismatch.html+Error: values of type a3 → a4 cannot be compared.+https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html+Error: values of type a16 → a17 are not boolean.+https://disco-lang.readthedocs.io/en/latest/reference/not-qual.html {}
+ test/types-357/expected view
@@ -0,0 +1,6 @@+While checking REPL.f:+  Error: typechecking failed.+  https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html+While checking REPL.sumto:+  Error: typechecking failed.+  https://disco-lang.readthedocs.io/en/latest/reference/typecheck-fail.html
+ test/types-357/input view
@@ -0,0 +1,5 @@+f : Z -> N+f(n) = 2n++sumto : N -> N+sumto(n) = (n^2 + n)*(0.5)
test/types-char-string/expected view
@@ -12,7 +12,7 @@ 2 3 (λx. {? 1 when x is 'a' -     ?})("Disco")+?})("Disco") 1:3:   | 1 | ' a'
test/types-ops/expected view
@@ -1,4 +1,3 @@-Loading list.disco... right(0) right(1) right(2)
test/types-tydef-param/types-tydef-param.disco view
@@ -13,12 +13,12 @@  type Tree(a) = Unit + a * Tree(a) * Tree(a) -foldTree : r -> (a -> r -> r -> r) -> Tree(a) -> r+foldTree : r -> (a * r * r -> r) -> Tree(a) -> r foldTree z f (left(■)) = z-foldTree z f (right (a,l,r)) = f a (foldTree z f l) (foldTree z f r)+foldTree z f (right (a,l,r)) = f (a, foldTree z f l, foldTree z f r)  sumTree : Tree(N) -> N-sumTree = foldTree 0 (\a, l, r. a+l+r)+sumTree = foldTree 0 (\(a, l, r). a+l+r)  t : Tree(N) t = right (5, right (2, left(■), left(■)), right (7, right (1, left(■), left(■)), left(■)))@@ -29,9 +29,9 @@ alt1 : AltList(N, Bool) alt1 = right (3, right (true, right (5, right (false, right (7, left(■)))))) -foldAltList : r -> (a -> r -> r) -> (b -> r -> r) -> AltList(a, b) -> r+foldAltList : r -> (a * r -> r) -> (b * r -> r) -> AltList(a, b) -> r foldAltList z _ _ (left(■)) = z-foldAltList z f g (right (a, l)) = f a (foldAltList z g f l)+foldAltList z f g (right (a, l)) = f(a, foldAltList z g f l)  sumAltList : AltList(N,Bool) -> N-sumAltList = foldAltList 0 (\x, y. x+y) (\b, r. r)+sumAltList = foldAltList 0 (\(x,y). x+y) (\(b,r). r)