language-dickinson 1.4.0.0 → 1.4.1.0
raw patch · 23 files changed
+215/−83 lines, 23 filesdep ~composition-preludePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: composition-prelude
API changes (from Hackage documentation)
+ Language.Dickinson: dckPath :: IO [FilePath]
+ Language.Dickinson: defaultLibPath :: IO ([FilePath] -> [FilePath])
+ Language.Dickinson: patternExhaustivenessBSL :: [FilePath] -> FilePath -> ByteString -> IO ()
+ Language.Dickinson: pipelineBSL :: [FilePath] -> FilePath -> ByteString -> IO Text
+ Language.Dickinson: validateBSL :: [FilePath] -> FilePath -> ByteString -> IO ()
+ Language.Dickinson: warnBSL :: ByteString -> IO ()
Files
- CHANGELOG.md +5/−0
- examples/divinationBot.dck +9/−6
- language-dickinson.cabal +3/−2
- lib/fun/bool.dck +25/−0
- lib/noun.dck +1/−0
- man/emd.1 +1/−1
- public/Language/Dickinson.hs +10/−0
- run/Format.hs +11/−5
- run/REPL.hs +5/−1
- run/REPL/Completions.hs +0/−4
- src/Language/Dickinson/Check/Exhaustive.hs +2/−0
- src/Language/Dickinson/Check/Internal.hs +7/−0
- src/Language/Dickinson/Check/Scope.hs +13/−10
- src/Language/Dickinson/Eval.hs +2/−4
- src/Language/Dickinson/File.hs +55/−5
- src/Language/Dickinson/Lexer.x +1/−0
- src/Language/Dickinson/Lib.hs +1/−0
- src/Language/Dickinson/Lib/Get.hs +18/−3
- src/Language/Dickinson/Pipeline.hs +0/−8
- src/Language/Dickinson/Rename.hs +19/−20
- src/Language/Dickinson/Rename/Amalgamate.hs +10/−0
- src/Language/Dickinson/Type.hs +4/−4
- src/Language/Dickinson/TypeCheck.hs +13/−10
CHANGELOG.md view
@@ -1,5 +1,10 @@ # dickinson +## 1.4.1.0++ * Export `dckPath` and `defaultLibPath`+ * Add `pipelineBSL`, `validateBSL`, `patternExhaustivenessBSL`, and `warnBSL`+ ## 1.4.0.0 * Add `:bind` construct, like `:let` but stricter
examples/divinationBot.dck view
@@ -1,9 +1,9 @@ %- -; @botmancy from twitter but more sensical\+; @botmancy from twitter but more sensical ; http://phrontistery.info/divine.html ; https://en.wikipedia.org/wiki/Methods_of_divination-; bones, topo-, xylo-, crystallo-+; crystallo- tydecl means = Fish | Stars | Plants@@ -32,6 +32,7 @@ | Teeth | Bones | Land+ | Nails (:def prefix (:lambda x means@@ -63,7 +64,8 @@ [Small "micro"] [Teeth "odonto"] [Bones "osteo"]- [Land "topo"])))+ [Land "topo"]+ [Nails "onycho"]))) (:def english (:lambda x means@@ -92,10 +94,11 @@ [Rock "rocks"] [Large "large objects"] [Pearl "pearls"]- [Small "micro"]+ [Small "small objects"] [Teeth "teeth"] [Bones "bones"]- [Land "landforms"])))+ [Land "landforms"]+ [Nails "fingernails"]))) (:def mod (:branch@@ -112,7 +115,7 @@ (| 1.0 "mancy") (| 0.065 "scopy") (| 0.03 "spication")- (| 0.015 "logy")))+ (| 0.06 "logy"))) (:def main (:bind
language-dickinson.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: language-dickinson-version: 1.4.0.0+version: 1.4.1.0 license: BSD3 license-file: LICENSE copyright: Copyright: (c) 2020 Vanessa McHale@@ -19,6 +19,7 @@ lib/*.dck prelude/*.dck lib/grammar/*.dck+ lib/fun/*.dck extra-source-files: test/data/*.pretty@@ -141,7 +142,7 @@ deepseq -any, microlens -any, microlens-mtl -any,- composition-prelude >=1.1.0.1,+ composition-prelude >=2.0.0.0, binary >=0.8.4.0, filepath -any, directory -any,
+ lib/fun/bool.dck view
@@ -0,0 +1,25 @@+%-++tydecl bool = True | False++(:def and+ (:lambda x bool+ (:lambda y bool+ (:match (x, y)+ [(True, True) True]+ [_ False]+ ))))++(:def or+ (:lambda x bool+ (:lambda y bool+ (:match (x, y)+ [(False, False) False]+ [_ True]+ ))))++(:def not+ (:lambda x bool+ (:match x+ [True False]+ [False True])))
lib/noun.dck view
@@ -36,6 +36,7 @@ (| "head") (| "punnet") (| "sitophobia")+ (| "acedia") (| "tail") (| "joke") (| "autocracy")
man/emd.1 view
@@ -1,4 +1,4 @@-.\" Automatically generated by Pandoc 2.10.1+.\" Automatically generated by Pandoc 2.11.1 .\" .TH "emd (1)" "" "" "" "" .hy
public/Language/Dickinson.hs view
@@ -20,14 +20,24 @@ , renameExpressionM -- * Imports , resolveImport+ -- * Evaluation+ , pipelineBSL+ , validateBSL+ , patternExhaustivenessBSL+ , warnBSL+ -- * Path+ , defaultLibPath+ , dckPath -- * Version info , dickinsonVersion , dickinsonVersionString ) where import qualified Data.Version as V+import Language.Dickinson.File import Language.Dickinson.Import import Language.Dickinson.Lexer+import Language.Dickinson.Lib import Language.Dickinson.Name import Language.Dickinson.Parser import Language.Dickinson.Rename
run/Format.hs view
@@ -2,11 +2,17 @@ , fmtInplace ) where -import Control.Monad ((<=<))-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BSL-import Data.Text.IO as TIO-import Language.Dickinson.Pipeline+import Control.Exception.Value (eitherThrow)+import Control.Monad ((<=<))+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.Text as T+import Data.Text.IO as TIO+import Data.Text.Prettyprint.Doc.Ext (prettyText)+import Language.Dickinson.Parser++format :: BSL.ByteString -> T.Text+format = prettyText . eitherThrow . parse fmtFile :: FilePath -> IO () fmtFile = TIO.putStrLn . format <=< BSL.readFile
run/REPL.hs view
@@ -44,7 +44,8 @@ import System.FilePath ((</>)) import System.Random (newStdGen, randoms) -dickinsonRepl :: [FilePath] -> IO ()+dickinsonRepl :: [FilePath] -- ^ Files to be loaded beforehand+ -> IO () dickinsonRepl fps = runRepl (traverse_ loadFile fps *> loop) type Repl a = InputT (StateT (EvalSt a) IO)@@ -132,6 +133,9 @@ listNames :: Repl AlexPosn () listNames = liftIO . traverse_ TIO.putStrLn =<< names++namesState :: StateT (EvalSt a) IO [T.Text]+namesState = gets (M.keys . topLevel) names :: Repl AlexPosn [T.Text] names = appendBuiltins <$> lift namesState
run/REPL/Completions.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE TupleSections #-} module REPL.Completions ( emdCompletions- , namesState ) where import Control.Monad.State (StateT, gets)@@ -10,9 +9,6 @@ import qualified Data.Text as T import Language.Dickinson.Eval import System.Console.Haskeline (Completion, CompletionFunc, simpleCompletion)--namesState :: StateT (EvalSt a) IO [T.Text]-namesState = gets (M.keys . topLevel) namesStr :: StateT (EvalSt a) IO [String] namesStr = gets (fmap T.unpack . M.keys . topLevel)
src/Language/Dickinson/Check/Exhaustive.hs view
@@ -10,6 +10,8 @@ import Language.Dickinson.Pattern.Useless import Language.Dickinson.Type +-- | Check that there are no useless pattern clauses and check that the pattern+-- matches are exhaustive checkExhaustive :: [Declaration a] -> Maybe (DickinsonWarning a) checkExhaustive ds = runPatternM (checkDeclsM ds)
src/Language/Dickinson/Check/Internal.hs view
@@ -31,6 +31,7 @@ -- TODO: see http://hackage.haskell.org/package/uniplate-1.6.12/docs/Data-Generics-Uniplate-Operations.html -- TODO: recursion schemes? would need benchmark...+-- | I exported this so I could benchmark it maxUniqueDeclaration :: Declaration a -> Int maxUniqueDeclaration (Define _ (Name _ (Unique i) _) e) = max i (maxUniqueExpression e) maxUniqueDeclaration (TyDecl _ (Name _ (Unique i) _) tns) =@@ -69,6 +70,12 @@ , maximum (fmap (maxUniqueExpression . snd) brs) ] maxUniqueExpression (Let _ bs e) =+ maximum+ [ maxUniqueExpression e+ , maximum (maxUniqueExpression . snd <$> bs)+ , maximum (unUnique . unique . fst <$> bs)+ ]+maxUniqueExpression (Bind _ bs e) = maximum [ maxUniqueExpression e , maximum (maxUniqueExpression . snd <$> bs)
src/Language/Dickinson/Check/Scope.hs view
@@ -10,6 +10,7 @@ import Control.Monad.State.Strict (State, evalState, get, modify) import Data.Foldable (traverse_) import qualified Data.IntSet as IS+import Data.List.NonEmpty (NonEmpty) import Language.Dickinson.Check.Common import Language.Dickinson.Check.Pattern import Language.Dickinson.Error@@ -85,20 +86,22 @@ if i `IS.member` b then pure Nothing else pure $ Just (UnfoundName l n)-checkExpr (Let _ bs e) = do- let ns = fst <$> bs- traverse_ insertName ns- (<|>) <$> checkExpr e <*> mapSumM checkExpr (snd <$> bs)- <* traverse_ deleteName ns-checkExpr (Bind _ bs e) = do- let ns = fst <$> bs- traverse_ insertName ns- (<|>) <$> checkExpr e <*> mapSumM checkExpr (snd <$> bs)- <* traverse_ deleteName ns+checkExpr (Let _ bs e) = checkLet bs e+checkExpr (Bind _ bs e) = checkLet bs e checkExpr (Match _ e brs) = ((<|>) <$> checkExpr e) <*> mapSumM checkPair brs checkExpr (Random l n) = checkType (TyNamed l n)++-- @:let@s and @:bind@s are the same here+checkLet :: NonEmpty (Name a, Expression a)+ -> Expression a+ -> CheckM (Maybe (DickinsonError a))+checkLet bs e = do+ let ns = fst <$> bs+ traverse_ insertName ns+ (<|>) <$> checkExpr e <*> mapSumM checkExpr (snd <$> bs)+ <* traverse_ deleteName ns checkPair :: (Pattern a, Expression a) -> CheckM (Maybe (DickinsonError a)) checkPair (p, e) = do
src/Language/Dickinson/Eval.hs view
@@ -51,10 +51,8 @@ , topLevel :: M.Map T.Text Unique -- Used in the REPL, for instance , lexerState :: AlexUserState- -- For error messages- , tyEnv :: TyEnv a- -- For :pick expressions- , constructors :: IM.IntMap (NonEmpty (TyName a))+ , tyEnv :: TyEnv a -- ^ For error messages+ , constructors :: IM.IntMap (NonEmpty (TyName a)) -- ^ This is used for @:pick@ expressions. } instance HasLexerState (EvalSt a) where
src/Language/Dickinson/File.hs view
@@ -4,18 +4,22 @@ , evalFile , checkFile , validateFile+ , validateBSL , validateAmalgamate , warnFile+ , warnBSL , patternExhaustivenessFile+ , patternExhaustivenessBSL , tcFile , amalgamateRename , amalgamateRenameM , pipeline+ , pipelineBSL , resolveFile ) where import Control.Applicative ((<|>))-import Control.Composition ((.*))+import Control.Composition ((.*), (.**), (<=*<)) import Control.Exception (Exception) import Control.Exception.Value import Control.Monad (void, (<=<))@@ -69,11 +73,24 @@ -> m [Declaration AlexPosn] amalgamateRenameM is = (balanceMax *>) . renameDeclarationsM <=< fileDecls is +amalgamateRenameInpM :: (HasRenames s, HasLexerState s, MonadIO m, MonadError (DickinsonError AlexPosn) m, MonadState s m)+ => [FilePath]+ -> FilePath -- ^ For error reporting+ -> BSL.ByteString+ -> m [Declaration AlexPosn]+amalgamateRenameInpM is = (balanceMax *>) . renameDeclarationsM <=*< bslDecls is+ amalgamateRename :: [FilePath] -> FilePath -> IO [Declaration AlexPosn] amalgamateRename is fp = flip evalStateT initAmalgamateSt $ fmap eitherThrow $ runExceptT $ amalgamateRenameM is fp +amalgamateRenameBSL :: [FilePath]+ -> FilePath -- ^ For error reporting+ -> BSL.ByteString+ -> IO [Declaration AlexPosn]+amalgamateRenameBSL is fp bsl = flip evalStateT initAmalgamateSt $ fmap eitherThrow $ runExceptT $ amalgamateRenameInpM is fp bsl+ -- | Check scoping checkFile :: [FilePath] -> FilePath -> IO () checkFile = ioChecker checkScope@@ -82,17 +99,33 @@ validateFile :: [FilePath] -> FilePath -> IO () validateFile = void .* validateAmalgamate +-- | Check scoping and types+--+-- @since 1.4.1.0+validateBSL :: [FilePath] -> FilePath -> BSL.ByteString -> IO ()+validateBSL = void .** validateAmalgamateBSL+ validateAmalgamate :: [FilePath] -> FilePath -> IO [Declaration AlexPosn] validateAmalgamate is fp = do d <- amalgamateRename is fp maybeThrowIO $ checkScope d eitherThrowIO (tyRun d) $> d --- | Run some lints+validateAmalgamateBSL :: [FilePath] -> FilePath -> BSL.ByteString -> IO [Declaration AlexPosn]+validateAmalgamateBSL is fp bsl = do+ d <- amalgamateRenameBSL is fp bsl+ maybeThrowIO $ checkScope d+ eitherThrowIO (tyRun d) $> d+ warnFile :: FilePath -> IO ()-warnFile = maybeThrowIO . (\x -> checkDuplicates x <|> checkMultiple x) . modDefs+warnFile = warnBSL <=< BSL.readFile++-- | Run some lints+--+-- @since 1.4.2.0+warnBSL :: BSL.ByteString -> IO ()+warnBSL = maybeThrowIO . (\x -> checkDuplicates x <|> checkMultiple x) . modDefs <=< eitherThrowIO . parse- <=< BSL.readFile ioChecker :: Exception e => ([Declaration AlexPosn] -> Maybe e) -> [FilePath] -> FilePath -> IO () ioChecker checker is = maybeThrowIO . checker <=< amalgamateRename is@@ -100,9 +133,18 @@ tcFile :: [FilePath] -> FilePath -> IO () tcFile is = eitherThrowIO . tyRun <=< amalgamateRename is -patternExhaustivenessFile :: [FilePath] -> FilePath -> IO ()+patternExhaustivenessFile :: [FilePath] -- ^ Includes+ -> FilePath+ -> IO () patternExhaustivenessFile is = maybeThrowIO . checkExhaustive <=< amalgamateRename is +-- | @since 1.4.1.0+patternExhaustivenessBSL :: [FilePath] -- ^ Includes+ -> FilePath -- ^ Source file (for error reporting)+ -> BSL.ByteString+ -> IO ()+patternExhaustivenessBSL is = maybeThrowIO . checkExhaustive <=*< amalgamateRenameBSL is+ evalFile :: [FilePath] -> FilePath -> IO T.Text evalFile is fp = (\g -> evalFileGen g is fp) =<< newStdGen @@ -115,3 +157,11 @@ pipeline :: [FilePath] -> FilePath -> IO T.Text pipeline is fp = fmap eitherThrow $ evalIO $ checkEvalM =<< amalgamateRenameM is fp++-- | @since 1.4.1.0+pipelineBSL :: [FilePath]+ -> FilePath -- ^ For error reporting+ -> BSL.ByteString+ -> IO T.Text+pipelineBSL is fp bsl = fmap eitherThrow $ evalIO $+ checkEvalM =<< amalgamateRenameInpM is fp bsl
src/Language/Dickinson/Lexer.x view
@@ -207,6 +207,7 @@ doSym t act = tok (\p _ -> TokSym p t <$ act) +-- | Used to track nesting level for string interpolations data ScdState = InStr | InMultiStr
src/Language/Dickinson/Lib.hs view
@@ -7,6 +7,7 @@ import System.Environment (lookupEnv) import System.FilePath ((</>)) +-- | Parsed @DCK_PATH@ environment variable dckPath :: IO [FilePath] dckPath = maybe [] splitEnv <$> lookupEnv "DCK_PATH"
src/Language/Dickinson/Lib/Get.hs view
@@ -2,6 +2,7 @@ module Language.Dickinson.Lib.Get ( parseImportM , parseFpM+ , parseBSLM ) where import Control.Composition ((.*))@@ -43,17 +44,31 @@ Just fp -> parseFp fp lSt Nothing -> throwError $ ModuleNotFound l n +parseBSL :: (MonadError (DickinsonError AlexPosn) m)+ => FilePath+ -> BSL.ByteString+ -> AlexUserState+ -> m (AlexUserState, Dickinson AlexPosn)+parseBSL fp bsl lSt =+ case parseWithCtx bsl lSt of+ Right x -> pure x+ Left err -> throwError (ParseErr fp err)+ parseFp :: (MonadError (DickinsonError AlexPosn) m, MonadIO m) => FilePath -- ^ Source file -> AlexUserState -- ^ Lexer state -> m (AlexUserState, Dickinson AlexPosn) parseFp fp lSt = do bsl <- liftIO $ BSL.readFile fp- case parseWithCtx bsl lSt of- Right x -> pure x- Left err -> throwError (ParseErr fp err)+ parseBSL fp bsl lSt parseFpM :: (HasLexerState s, MonadState s m, MonadError (DickinsonError AlexPosn) m, MonadIO m) => FilePath -- ^ Source file -> m (Dickinson AlexPosn) parseFpM fp = liftLexerState (parseFp fp)++parseBSLM :: (HasLexerState s, MonadState s m, MonadError (DickinsonError AlexPosn) m)+ => FilePath -- ^ Source file name (for error reporting)+ -> BSL.ByteString+ -> m (Dickinson AlexPosn)+parseBSLM fp bsl = liftLexerState (parseBSL fp bsl)
src/Language/Dickinson/Pipeline.hs view
@@ -1,20 +1,15 @@ {-# LANGUAGE FlexibleContexts #-} module Language.Dickinson.Pipeline ( checkEvalM- , format , validateDecl ) where -import Control.Exception.Value (eitherThrow) import Control.Monad.Except (MonadError) import Control.Monad.State.Lazy (MonadState)-import qualified Data.ByteString.Lazy as BSL import qualified Data.Text as T-import Data.Text.Prettyprint.Doc.Ext (prettyText) import Language.Dickinson.Check.Scope import Language.Dickinson.Error import Language.Dickinson.Eval-import Language.Dickinson.Parser import Language.Dickinson.Type import Language.Dickinson.TypeCheck @@ -23,9 +18,6 @@ checkEvalM ds = validateDecl ds *> evalDickinsonAsMain ds--format :: BSL.ByteString -> T.Text-format = prettyText . eitherThrow . parse -- the 'do'-notation is faster than using *> validateDecl :: (HasTyEnv s, MonadState (s a) m, MonadError (DickinsonError a) m) => [Declaration a] -> m ()
src/Language/Dickinson/Rename.hs view
@@ -19,7 +19,6 @@ import Control.Composition (thread) import Control.Monad (forM, (<=<))-import Control.Monad.Ext (zipWithM) import Control.Monad.State (MonadState, State, runState) import Data.Bifunctor (second) import Data.Binary (Binary)@@ -173,17 +172,24 @@ (modP, p') <- renamePatternM p (p' ,) <$> withRenames modP (renameExpressionM e') pure $ Match l preE brs'-renameExpressionM (Bind p bs e) = do- newBs <- traverse withName (fst <$> bs)- let localRenames = snd <$> newBs- newBinds = thread localRenames- newNames = fst <$> newBs- preNewBound = snd <$> bs- newBound <-- traverse renameExpressionM preNewBound- withRenames newBinds $- Bind p (NE.zip newNames newBound) <$> renameExpressionM e-renameExpressionM (Let p bs e) = do+renameExpressionM (Bind p bs e) = renameLet Bind p bs e+renameExpressionM (Let p bs e) = renameLet Let p bs e+renameExpressionM (Flatten l e) =+ Flatten l <$> renameExpressionM e+renameExpressionM (Annot l e ty) =+ Annot l <$> renameExpressionM e <*> pure ty+renameExpressionM c@Constructor{} = pure c+renameExpressionM c@BuiltinFn{} = pure c+renameExpressionM c@Random{} = pure c++-- since bind/let are the same at this stage+renameLet :: (MonadState s m, HasRenames s)+ => (a -> NE.NonEmpty (Name a, Expression a) -> Expression a -> Expression a)+ -> a+ -> NE.NonEmpty (Name a, Expression a)+ -> Expression a+ -> m (Expression a)+renameLet constructor p bs e = do newBs <- traverse withName (fst <$> bs) let localRenames = snd <$> newBs newBinds = thread localRenames@@ -192,11 +198,4 @@ newBound <- traverse renameExpressionM preNewBound withRenames newBinds $- Let p (NE.zip newNames newBound) <$> renameExpressionM e-renameExpressionM (Flatten l e) =- Flatten l <$> renameExpressionM e-renameExpressionM (Annot l e ty) =- Annot l <$> renameExpressionM e <*> pure ty-renameExpressionM c@Constructor{} = pure c-renameExpressionM c@BuiltinFn{} = pure c-renameExpressionM c@Random{} = pure c+ constructor p (NE.zip newNames newBound) <$> renameExpressionM e
src/Language/Dickinson/Rename/Amalgamate.hs view
@@ -2,12 +2,15 @@ module Language.Dickinson.Rename.Amalgamate ( amalgamateM , fileDecls+ , bslDecls ) where +import Control.Composition ((<=*<)) import Control.Monad ((<=<)) import Control.Monad.Except (MonadError) import Control.Monad.IO.Class (MonadIO) import Control.Monad.State (MonadState)+import qualified Data.ByteString.Lazy as BSL import Data.Functor (($>)) import Data.Semigroup ((<>)) import Language.Dickinson.Check.Pattern@@ -39,3 +42,10 @@ -> FilePath -- ^ Source file -> m [Declaration AlexPosn] fileDecls is = amalgamateM is <=< parseFpM++bslDecls :: (HasLexerState s, MonadIO m, MonadError (DickinsonError AlexPosn) m, MonadState s m)+ => [FilePath] -- ^ Includes+ -> FilePath -- ^ Source file (for reporting errors)+ -> BSL.ByteString+ -> m [Declaration AlexPosn]+bslDecls is = amalgamateM is <=*< parseBSLM
src/Language/Dickinson/Type.hs view
@@ -65,10 +65,10 @@ , letBinds :: NonEmpty (Name a, Expression a) , letExpr :: Expression a }- | Bind { exprAnn :: a- , letBinds :: NonEmpty (Name a, Expression a)- , letExpr :: Expression a- }+ | Bind { exprAnn :: a+ , letBinds :: NonEmpty (Name a, Expression a)+ , letExpr :: Expression a+ } | Var { exprAnn :: a, exprVar :: Name a } | Interp { exprAnn :: a, exprInterp :: [Expression a] } | MultiInterp { exprAnn :: a, exprMultiInterp :: [Expression a] }
src/Language/Dickinson/TypeCheck.hs view
@@ -121,16 +121,8 @@ tyAssert ty' e' pure ty'' _ -> throwError $ ExpectedLambda e ty-typeOf (Let _ bs e) = do- es' <- traverse typeOf (snd <$> bs)- let ns = fst <$> bs- Ext.zipWithM_ tyInsert ns es'- typeOf e-typeOf (Bind _ bs e) = do- es' <- traverse typeOf (snd <$> bs)- let ns = fst <$> bs- Ext.zipWithM_ tyInsert ns es'- typeOf e+typeOf (Let _ bs e) = tyLet bs e+typeOf (Bind _ bs e) = tyLet bs e typeOf (Match _ e brs@((_,e') :| _)) = do ty <- typeOf e forM_ (fst <$> brs) $ \p ->@@ -149,3 +141,14 @@ typeOf (BuiltinFn l _) = pure $ -- all builtins have type (-> text text) TyFun l (TyText l) (TyText l) typeOf (Random l n) = pure $ TyNamed l n++-- since :let and :bind are the same in this scheme+tyLet :: (HasTyEnv s, MonadState (s a) m, MonadError (DickinsonError a) m)+ => NonEmpty (Name a, Expression a)+ -> Expression a+ -> m (DickinsonTy a)+tyLet bs e = do+ es' <- traverse typeOf (snd <$> bs)+ let ns = fst <$> bs+ Ext.zipWithM_ tyInsert ns es'+ typeOf e