packages feed

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 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