From 8133060dd606f357a115a723c0d9cf9b7f242a9d Mon Sep 17 00:00:00 2001 From: Hugh JF Chen Date: Wed, 30 Mar 2022 10:11:01 -0500 Subject: [PATCH 1/4] make it buildable for Aeson 2.0 --- src/Web/Api/WebDriver/Endpoints.hs | 4 +++- src/Web/Api/WebDriver/Types.hs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Web/Api/WebDriver/Endpoints.hs b/src/Web/Api/WebDriver/Endpoints.hs index d65ef93..98b5158 100644 --- a/src/Web/Api/WebDriver/Endpoints.hs +++ b/src/Web/Api/WebDriver/Endpoints.hs @@ -174,6 +174,8 @@ import Control.Monad.Trans.Class ( MonadTrans(..) ) import Data.Aeson ( Value(..), encode, object, (.=), toJSON ) +import Data.Aeson.Key + ( fromText ) import Data.Text ( Text, unpack, pack ) import Data.Text.Encoding @@ -558,7 +560,7 @@ switchToFrame ref = do !frame = case ref of TopLevelFrame -> Null FrameNumber k -> Number $ fromIntegral k - FrameContainingElement element_id -> object [ _WEB_ELEMENT_ID .= show element_id ] + FrameContainingElement element_id -> object [ (fromText _WEB_ELEMENT_ID) .= show element_id ] !payload = encode $ object [ "id" .= toJSON frame ] diff --git a/src/Web/Api/WebDriver/Types.hs b/src/Web/Api/WebDriver/Types.hs index b3aa997..a63af64 100644 --- a/src/Web/Api/WebDriver/Types.hs +++ b/src/Web/Api/WebDriver/Types.hs @@ -105,6 +105,8 @@ import Data.HashMap.Strict import Data.Aeson.Types ( ToJSON(..), FromJSON(..), Value(..), KeyValue , Pair, (.:?), (.:), (.=), object, typeMismatch ) +import Data.Aeson.Key + ( fromText ) import Data.Text ( Text, pack, unpack ) import Data.Text.Encoding @@ -135,10 +137,10 @@ object_ :: [Maybe Pair] -> Value object_ = object . filter (\(_, v) -> v /= Null) . catMaybes (.==) :: (ToJSON v, KeyValue kv) => Text -> v -> Maybe kv -(.==) key value = Just (key .= value) +(.==) key value = Just $ (fromText key) .= value (.=?) :: (ToJSON v, KeyValue kv) => Text -> Maybe v -> Maybe kv -(.=?) key = fmap (key .=) +(.=?) key = fmap ((fromText key) .=) From 900556d14b79805376391f5e14698abb122ccba6 Mon Sep 17 00:00:00 2001 From: Hugh JF Chen Date: Wed, 30 Mar 2022 10:17:19 -0500 Subject: [PATCH 2/4] nixify the project to bulid with nix --- .dir-locals.el | 1 + .ghci | 3 +- .../build-with-nix-on-self-hosted.yml | 33 + .github/workflows/ci.yml | 51 + .gitignore | 46 +- .hlint.yaml | 3290 +++++++++++++++++ .travis.yml | 82 +- cabal.project | 2 + cabal.project.local | 0 ci/common/common.sh | 166 + ci/pack-tarball/do.sh | 27 + ci/pack-tarball/undo.sh | 18 + ci/prepare-env/do.sh | 105 + ci/prepare-env/undo.sh | 59 + cross-build.nix | 31 + default.nix | 45 + docker.nix | 20 + hie.yaml | 10 + nix/cross-build/systems.nix | 17 + nix/overlay/default.nix | 2 + nix/sources.json | 50 + nix/sources.nix | 174 + script-monad-http-client-remove-Eq.patch | 52 + shell.nix | 32 + stack.yaml | 7 - start-dev | 1 + tarball.nix | 27 + 27 files changed, 4276 insertions(+), 75 deletions(-) create mode 100644 .dir-locals.el create mode 100644 .github/workflows/build-with-nix-on-self-hosted.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .hlint.yaml create mode 100644 cabal.project create mode 100644 cabal.project.local create mode 100644 ci/common/common.sh create mode 100644 ci/pack-tarball/do.sh create mode 100644 ci/pack-tarball/undo.sh create mode 100644 ci/prepare-env/do.sh create mode 100644 ci/prepare-env/undo.sh create mode 100644 cross-build.nix create mode 100644 default.nix create mode 100644 docker.nix create mode 100644 hie.yaml create mode 100644 nix/cross-build/systems.nix create mode 100644 nix/overlay/default.nix create mode 100644 nix/sources.json create mode 100644 nix/sources.nix create mode 100644 script-monad-http-client-remove-Eq.patch create mode 100644 shell.nix delete mode 100644 stack.yaml create mode 100755 start-dev create mode 100644 tarball.nix diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..17b1116 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1 @@ +((nil . ((lsp-haskell-server-path . "haskell-language-server")))) diff --git a/.ghci b/.ghci index caf0ccd..7ab2933 100644 --- a/.ghci +++ b/.ghci @@ -1 +1,2 @@ -:set prompt "\x03BB: " +:def hg \x -> return $ ":!hoogle \"" ++ x ++ "\"" +:def hgi \x -> return $ ":!hoogle --info \"" ++ x ++ "\"" diff --git a/.github/workflows/build-with-nix-on-self-hosted.yml b/.github/workflows/build-with-nix-on-self-hosted.yml new file mode 100644 index 0000000..8044912 --- /dev/null +++ b/.github/workflows/build-with-nix-on-self-hosted.yml @@ -0,0 +1,33 @@ +name: build-with-nix + +# Trigger the workflow on push or pull request, but only for the master branch +on: + pull_request: + push: + branches: [master] + +jobs: + build: + name: build with haskell.nix + runs-on: self-hosted + + steps: + - uses: actions/checkout@v2 + if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' + + - name: Prepare Nix + run: | + ./ci/prepare-env/do.sh + + - name: Build + run: | + [[ -f ~/.nix-profile/etc/profile.d/nix.sh ]] && . ~/.nix-profile/etc/profile.d/nix.sh + nix-build ./cross-build.nix + + #- name: Test + #run: | + #nix-shell --run "cabal v2-test --enable-tests" + + #- name: Build Docker Image + #run: | + #nix-build ./docker.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ec42d50 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +# Trigger the workflow on push or pull request, but only for the main branch +on: + pull_request: + push: + branches: [master] + +jobs: + cabal: + name: ${{ matrix.os }} / ghc ${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macOS-latest, windows-latest] + cabal: ["2.4"] + ghc: + - 8.10.7 + exclude: + - os: macOS-latest + ghc: 8.8.4 + - os: windows-latest + ghc: 8.8.4 + + steps: + - uses: actions/checkout@v2.3.3 + if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' + + - uses: haskell/actions/setup@v1 + id: setup-haskell-cabal + name: Setup Haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + + - name: Freeze + run: | + cabal v2-freeze + - uses: actions/cache@v2.1.2 + name: Cache ~/.cabal/store + with: + path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + + - name: Build + run: | + cabal v2-configure --enable-tests --enable-benchmarks --test-show-details=direct + cabal v2-build all + - name: Test + run: | + cabal v2-test all diff --git a/.gitignore b/.gitignore index 7f2b66d..d03bbee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +### Haskell dist dist-* cabal-dev @@ -7,17 +8,50 @@ cabal-dev *.chs.h *.dyn_o *.dyn_hi -.hpc -.hsenv -.cabal-sandbox/ -cabal.sandbox.config *.prof *.aux *.hp *.eventlog -.stack-work/ -cabal.project.local +.virtualenv +.hsenv +.hpc +.cabal-sandbox/ +cabal.sandbox.config +cabal.config +.ghc.environment.* .HTF/ +# Stack +.stack-work/ stack.yaml.lock +### IDE/support +# Vim +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +*~ +tags + +# IntellijIDEA +.idea/ +.ideaHaskellLib/ +*.iml + +# Atom +.haskell-ghc-mod.json + +# VS +.vscode/ + +# Emacs +*# +TAGS + +# other .DS_Store + +# nix +result +result-* + diff --git a/.hlint.yaml b/.hlint.yaml new file mode 100644 index 0000000..57f9b51 --- /dev/null +++ b/.hlint.yaml @@ -0,0 +1,3290 @@ + +- arguments: + - "-XConstraintKinds" + - "-XDeriveGeneric" + - "-XGeneralizedNewtypeDeriving" + - "-XLambdaCase" + - "-XOverloadedStrings" + - "-XRecordWildCards" + - "-XScopedTypeVariables" + - "-XStandaloneDeriving" + - "-XTupleSections" + - "-XTypeApplications" + - "-XViewPatterns" +- ignore: + name: Use head +- ignore: + name: Use Foldable.forM_ +- hint: + lhs: "pure ()" + note: "Use 'pass'" + rhs: pass +- hint: + lhs: "return ()" + note: "Use 'pass'" + rhs: pass +- hint: + lhs: "(: [])" + note: "Use `one`" + rhs: one +- hint: + lhs: "(:| [])" + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Sequence.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Text.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Text.Lazy.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.ByteString.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.ByteString.Lazy.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Map.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Map.Strict.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.HashMap.Strict.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.HashMap.Lazy.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.IntMap.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.IntMap.Strict.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.Set.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.HashSet.singleton + note: "Use `one`" + rhs: one +- hint: + lhs: Data.IntSet.singleton + note: "Use `one`" + rhs: one +- warn: + lhs: Control.Exception.evaluate + rhs: evaluateWHNF +- warn: + lhs: "Control.Exception.evaluate (force x)" + rhs: evaluateNF x +- warn: + lhs: "Control.Exception.evaluate (x `deepseq` ())" + rhs: evaluateNF_ x +- warn: + lhs: "void (evaluateWHNF x)" + rhs: evaluateWHNF_ x +- warn: + lhs: "void (evaluateNF x)" + rhs: evaluateNF_ x +- hint: + lhs: Control.Exception.throw + note: "Use 'impureThrow'" + rhs: impureThrow +- warn: + lhs: Data.Text.IO.readFile + rhs: readFileText +- warn: + lhs: Data.Text.IO.writeFile + rhs: writeFileText +- warn: + lhs: Data.Text.IO.appendFile + rhs: appendFileText +- warn: + lhs: Data.Text.Lazy.IO.readFile + rhs: readFileLText +- warn: + lhs: Data.Text.Lazy.IO.writeFile + rhs: writeFileLText +- warn: + lhs: Data.Text.Lazy.IO.appendFile + rhs: appendFileLText +- warn: + lhs: Data.ByteString.readFile + rhs: readFileBS +- warn: + lhs: Data.ByteString.writeFile + rhs: writeFileBS +- warn: + lhs: Data.ByteString.appendFile + rhs: appendFileBS +- warn: + lhs: Data.ByteString.Lazy.readFile + rhs: readFileLBS +- warn: + lhs: Data.ByteString.Lazy.writeFile + rhs: writeFileLBS +- warn: + lhs: Data.ByteString.Lazy.appendFile + rhs: appendFileLBS +- hint: + lhs: "foldl' (flip f)" + note: "Use 'flipfoldl''" + rhs: "flipfoldl' f" +- warn: + lhs: "foldl' (+) 0" + rhs: sum +- warn: + lhs: "foldl' (*) 1" + rhs: product +- hint: + lhs: "fmap and (sequence s)" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: andM s +- hint: + lhs: "and <$> sequence s" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: andM s +- hint: + lhs: "fmap or (sequence s)" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: orM s +- hint: + lhs: "or <$> sequence s" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: orM s +- hint: + lhs: "fmap and (mapM f s)" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: allM f s +- hint: + lhs: "and <$> mapM f s" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: allM f s +- hint: + lhs: "fmap or (mapM f s)" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: anyM f s +- hint: + lhs: "or <$> mapM f s" + note: Applying this hint would mean that some actions that were being executed previously would no longer be executed. + rhs: anyM f s +- warn: + lhs: "getAlt (foldMap (Alt . f) xs)" + rhs: asumMap xs +- warn: + lhs: "getAlt . foldMap (Alt . f)" + rhs: asumMap +- hint: + lhs: "foldr (\\x acc -> f x <|> acc) empty" + note: "Use 'asumMap'" + rhs: asumMap f +- hint: + lhs: "asum (map f xs)" + note: "Use 'asumMap'" + rhs: asumMap f xs +- warn: + lhs: "map fst &&& map snd" + rhs: unzip +- hint: + lhs: "fmap (fmap f) x" + note: "Use '(<<$>>)'" + rhs: "f <<$>> x" +- hint: + lhs: "(\\f -> f x) <$> ff" + note: Use flap operator + rhs: "ff ?? x" +- hint: + lhs: "fmap (\\f -> f x) ff" + note: Use flap operator + rhs: "ff ?? x" +- hint: + lhs: "fmap ($ x) ff" + note: Use flap operator + rhs: "ff ?? x" +- hint: + lhs: "($ x) <$> ff" + note: Use flap operator + rhs: "ff ?? x" +- warn: + lhs: "fmap f (nonEmpty x)" + rhs: viaNonEmpty f x +- warn: + lhs: fmap f . nonEmpty + rhs: viaNonEmpty f +- warn: + lhs: "f <$> nonEmpty x" + rhs: viaNonEmpty f x +- warn: + lhs: "f >>= guard" + rhs: guardM f +- warn: + lhs: "guard =<< f" + rhs: guardM f +- warn: + lhs: "whenM (not <$> x)" + rhs: unlessM x +- warn: + lhs: "unlessM (not <$> x)" + rhs: whenM x +- warn: + lhs: "either (const True) (const False)" + rhs: isLeft +- warn: + lhs: "either (const False) (const True)" + rhs: isRight +- warn: + lhs: "either id (const a)" + rhs: fromLeft a +- warn: + lhs: "either (const b) id" + rhs: fromRight b +- warn: + lhs: "either Just (const Nothing)" + rhs: leftToMaybe +- warn: + lhs: "either (const Nothing) Just" + rhs: rightToMaybe +- warn: + lhs: "maybe (Left l) Right" + rhs: maybeToRight l +- warn: + lhs: "maybe (Right r) Left" + rhs: maybeToLeft r +- warn: + lhs: "case m of Just x -> f x; Nothing -> pure ()" + rhs: whenJust m f +- warn: + lhs: "case m of Just x -> f x; Nothing -> return ()" + rhs: whenJust m f +- warn: + lhs: "case m of Just x -> f x; Nothing -> pass" + rhs: whenJust m f +- warn: + lhs: "case m of Nothing -> pure () ; Just x -> f x" + rhs: whenJust m f +- warn: + lhs: "case m of Nothing -> return (); Just x -> f x" + rhs: whenJust m f +- warn: + lhs: "case m of Nothing -> pass ; Just x -> f x" + rhs: whenJust m f +- warn: + lhs: "maybe (pure ()) f m" + rhs: whenJust m f +- warn: + lhs: "maybe (return ()) f m" + rhs: whenJust m f +- warn: + lhs: maybe pass f m + rhs: whenJust m f +- warn: + lhs: "m >>= \\a -> whenJust a f" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Just x -> f x; Nothing -> pure ()" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Just x -> f x; Nothing -> return ()" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Just x -> f x; Nothing -> pass" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Nothing -> pure () ; Just x -> f x" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Nothing -> return (); Just x -> f x" + rhs: whenJustM m f +- warn: + lhs: "m >>= \\case Nothing -> pass ; Just x -> f x" + rhs: whenJustM m f +- warn: + lhs: "maybe (pure ()) f =<< m" + rhs: whenJustM m f +- warn: + lhs: "maybe (return ()) f =<< m" + rhs: whenJustM m f +- warn: + lhs: "maybe pass f =<< m" + rhs: whenJustM m f +- warn: + lhs: "m >>= maybe (pure ()) f" + rhs: whenJustM m f +- warn: + lhs: "m >>= maybe (return ()) f" + rhs: whenJustM m f +- warn: + lhs: "m >>= maybe pass f" + rhs: whenJustM m f +- warn: + lhs: "case m of Just _ -> pure () ; Nothing -> x" + rhs: whenNothing_ m x +- warn: + lhs: "case m of Just _ -> return (); Nothing -> x" + rhs: whenNothing_ m x +- warn: + lhs: "case m of Just _ -> pass ; Nothing -> x" + rhs: whenNothing_ m x +- warn: + lhs: "case m of Nothing -> x; Just _ -> pure ()" + rhs: whenNothing_ m x +- warn: + lhs: "case m of Nothing -> x; Just _ -> return ()" + rhs: whenNothing_ m x +- warn: + lhs: "case m of Nothing -> x; Just _ -> pass" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (\\_ -> pure () ) m" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (\\_ -> return () ) m" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (\\_ -> pass ) m" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (const (pure () )) m" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (const (return ())) m" + rhs: whenNothing_ m x +- warn: + lhs: "maybe x (const pass) m" + rhs: whenNothing_ m x +- warn: + lhs: "m >>= \\a -> whenNothing_ a x" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Just _ -> pure () ; Nothing -> x" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Just _ -> return (); Nothing -> x" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Just _ -> pass ; Nothing -> x" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Nothing -> x; Just _ -> pure ()" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Nothing -> x; Just _ -> return ()" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= \\case Nothing -> x; Just _ -> pass" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (\\_ -> pure () ) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (\\_ -> return () ) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (\\_ -> pass ) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (const (pure () )) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (const (return ())) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "maybe x (const pass) =<< m" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (\\_ -> pure ())" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (\\_ -> return ())" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (\\_ -> pass)" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (const (pure ()) )" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (const (return ()))" + rhs: whenNothingM_ m x +- warn: + lhs: "m >>= maybe x (const pass)" + rhs: whenNothingM_ m x +- warn: + lhs: "whenLeft ()" + rhs: whenLeft_ +- warn: + lhs: "case m of Left x -> f x; Right _ -> pure ()" + rhs: whenLeft_ m f +- warn: + lhs: "case m of Left x -> f x; Right _ -> return ()" + rhs: whenLeft_ m f +- warn: + lhs: "case m of Left x -> f x; Right _ -> pass" + rhs: whenLeft_ m f +- warn: + lhs: "case m of Right _ -> pure () ; Left x -> f x" + rhs: whenLeft_ m f +- warn: + lhs: "case m of Right _ -> return (); Left x -> f x" + rhs: whenLeft_ m f +- warn: + lhs: "case m of Right _ -> pass ; Left x -> f x" + rhs: whenLeft_ m f +- warn: + lhs: "either f (\\_ -> pure () ) m" + rhs: whenLeft_ m f +- warn: + lhs: "either f (\\_ -> return () ) m" + rhs: whenLeft_ m f +- warn: + lhs: "either f (\\_ -> pass ) m" + rhs: whenLeft_ m f +- warn: + lhs: "either f (const (pure () )) m" + rhs: whenLeft_ m f +- warn: + lhs: "either f (const (return ())) m" + rhs: whenLeft_ m f +- warn: + lhs: "either f (const pass) m" + rhs: whenLeft_ m f +- warn: + lhs: "m >>= \\a -> whenLeft_ a f" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Left x -> f x; Right _ -> pure ()" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Left x -> f x; Right _ -> return ()" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Left x -> f x; Right _ -> pass" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Right _ -> pure () ; Left x -> f x" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Right _ -> return (); Left x -> f x" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= \\case Right _ -> pass ; Left x -> f x" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (\\_ -> pure () ) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (\\_ -> return () ) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (\\_ -> pass ) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (const (pure () )) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (const (return ())) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "either f (const pass) =<< m" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (\\_ -> pure ())" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (\\_ -> return ())" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (\\_ -> pass)" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (const (pure ()) )" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (const (return ()))" + rhs: whenLeftM_ m f +- warn: + lhs: "m >>= either f (const pass)" + rhs: whenLeftM_ m f +- warn: + lhs: "whenRight ()" + rhs: whenRight_ +- warn: + lhs: "case m of Right x -> f x; Left _ -> pure ()" + rhs: whenRight_ m f +- warn: + lhs: "case m of Right x -> f x; Left _ -> return ()" + rhs: whenRight_ m f +- warn: + lhs: "case m of Right x -> f x; Left _ -> pass" + rhs: whenRight_ m f +- warn: + lhs: "case m of Left _ -> pure () ; Right x -> f x" + rhs: whenRight_ m f +- warn: + lhs: "case m of Left _ -> return (); Right x -> f x" + rhs: whenRight_ m f +- warn: + lhs: "case m of Left _ -> pass ; Right x -> f x" + rhs: whenRight_ m f +- warn: + lhs: "either (\\_ -> pure () ) f m" + rhs: whenRight_ m f +- warn: + lhs: "either (\\_ -> return () ) f m" + rhs: whenRight_ m f +- warn: + lhs: "either (\\_ -> pass ) f m" + rhs: whenRight_ m f +- warn: + lhs: "either (const (pure () )) f m" + rhs: whenRight_ m f +- warn: + lhs: "either (const (return ())) f m" + rhs: whenRight_ m f +- warn: + lhs: "either (const pass) f m" + rhs: whenRight_ m f +- warn: + lhs: "m >>= \\a -> whenRight_ a f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Right x -> f x; Left _ -> pure () " + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Right x -> f x; Left _ -> return ()" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Right x -> f x; Left _ -> pass" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Left _ -> pure () ; Right x -> f x" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Left _ -> return (); Right x -> f x" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= \\case Left _ -> pass ; Right x -> f x" + rhs: whenRightM_ m f +- warn: + lhs: "either (\\_ -> pure () ) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "either (\\_ -> return () ) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "either (\\_ -> pass ) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "either (const (pure () )) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "either (const (return ())) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "either (const pass) f =<< m" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (\\_ -> pure ()) f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (\\_ -> return ()) f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (\\_ -> pass) f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (const (pure ()) ) f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (const (return ())) f" + rhs: whenRightM_ m f +- warn: + lhs: "m >>= either (const pass) f" + rhs: whenRightM_ m f +- warn: + lhs: "case m of Left x -> f x; Right _ -> pure d " + rhs: whenLeft d m f +- warn: + lhs: "case m of Left x -> f x; Right _ -> return d" + rhs: whenLeft d m f +- warn: + lhs: "case m of Right _ -> pure d ; Left x -> f x" + rhs: whenLeft d m f +- warn: + lhs: "case m of Right _ -> return d; Left x -> f x" + rhs: whenLeft d m f +- warn: + lhs: "either f (\\_ -> pure d ) m" + rhs: whenLeft d m f +- warn: + lhs: "either f (\\_ -> return d ) m" + rhs: whenLeft d m f +- warn: + lhs: "either f (const (pure d )) m" + rhs: whenLeft d m f +- warn: + lhs: "either f (const (return d)) m" + rhs: whenLeft d m f +- warn: + lhs: "m >>= \\a -> whenLeft d a f" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= \\case Left x -> f x; Right _ -> pure d" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= \\case Left x -> f x; Right _ -> return d" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= \\case Right _ -> pure d ; Left x -> f x" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= \\case Right _ -> return d; Left x -> f x" + rhs: whenLeftM d m f +- warn: + lhs: "either f (\\_ -> pure d ) =<< m" + rhs: whenLeftM d m f +- warn: + lhs: "either f (\\_ -> return d ) =<< m" + rhs: whenLeftM d m f +- warn: + lhs: "either f (const (pure d )) =<< m" + rhs: whenLeftM d m f +- warn: + lhs: "either f (const (return d)) =<< m" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= either f (\\_ -> pure d)" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= either f (\\_ -> return d)" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= either f (const (pure d))" + rhs: whenLeftM d m f +- warn: + lhs: "m >>= either f (const (return d))" + rhs: whenLeftM d m f +- warn: + lhs: "case m of Right x -> f x; Left _ -> pure d" + rhs: whenRight d m f +- warn: + lhs: "case m of Right x -> f x; Left _ -> return d" + rhs: whenRight d m f +- warn: + lhs: "case m of Left _ -> pure d ; Right x -> f x" + rhs: whenRight d m f +- warn: + lhs: "case m of Left _ -> return d; Right x -> f x" + rhs: whenRight d m f +- warn: + lhs: "either (\\_ -> pure d ) f m" + rhs: whenRight d m f +- warn: + lhs: "either (\\_ -> return d ) f m" + rhs: whenRight d m f +- warn: + lhs: "either (const (pure d )) f m" + rhs: whenRight d m f +- warn: + lhs: "either (const (return d)) f m" + rhs: whenRight d m f +- warn: + lhs: "m >>= \\a -> whenRight d a f" + rhs: whenRightM d m f +- warn: + lhs: "m >>= \\case Right x -> f x; Left _ -> pure d" + rhs: whenRightM d m f +- warn: + lhs: "m >>= \\case Right x -> f x; Left _ -> return d" + rhs: whenRightM d m f +- warn: + lhs: "m >>= \\case Left _ -> pure d ; Right x -> f x" + rhs: whenRightM d m f +- warn: + lhs: "m >>= \\case Left _ -> return d; Right x -> f x" + rhs: whenRightM d m f +- warn: + lhs: "either (\\_ -> pure d ) f =<< m" + rhs: whenRightM d m f +- warn: + lhs: "either (\\_ -> return d ) f =<< m" + rhs: whenRightM d m f +- warn: + lhs: "either (const (pure d )) f =<< m" + rhs: whenRightM d m f +- warn: + lhs: "either (const (return d)) f =<< m" + rhs: whenRightM d m f +- warn: + lhs: "m >>= either (\\_ -> pure d) f" + rhs: whenRightM d m f +- warn: + lhs: "m >>= either (\\_ -> return d) f" + rhs: whenRightM d m f +- warn: + lhs: "m >>= either (const (pure d) ) f" + rhs: whenRightM d m f +- warn: + lhs: "m >>= either (const (return d)) f" + rhs: whenRightM d m f +- warn: + lhs: "case m of [] -> return (); (x:xs) -> f (x :| xs)" + rhs: whenNotNull m f +- warn: + lhs: "case m of [] -> pure () ; (x:xs) -> f (x :| xs)" + rhs: whenNotNull m f +- warn: + lhs: "case m of [] -> pass ; (x:xs) -> f (x :| xs)" + rhs: whenNotNull m f +- warn: + lhs: "case m of (x:xs) -> f (x :| xs); [] -> return ()" + rhs: whenNotNull m f +- warn: + lhs: "case m of (x:xs) -> f (x :| xs); [] -> pure () " + rhs: whenNotNull m f +- warn: + lhs: "case m of (x:xs) -> f (x :| xs); [] -> pass " + rhs: whenNotNull m f +- warn: + lhs: "m >>= \\case [] -> pass ; (x:xs) -> f (x :| xs)" + rhs: whenNotNullM m f +- warn: + lhs: "m >>= \\case [] -> pure () ; (x:xs) -> f (x :| xs)" + rhs: whenNotNullM m f +- warn: + lhs: "m >>= \\case [] -> return (); (x:xs) -> f (x :| xs)" + rhs: whenNotNullM m f +- warn: + lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> pass " + rhs: whenNotNullM m f +- warn: + lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> pure () " + rhs: whenNotNullM m f +- warn: + lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> return ()" + rhs: whenNotNullM m f +- warn: + lhs: mapMaybe leftToMaybe + rhs: lefts +- warn: + lhs: mapMaybe rightToMaybe + rhs: rights +- warn: + lhs: flip runReaderT + rhs: usingReaderT +- warn: + lhs: flip runReader + rhs: usingReader +- warn: + lhs: flip runStateT + rhs: usingStateT +- warn: + lhs: flip runState + rhs: usingState +- warn: + lhs: "fst <$> usingStateT s st" + rhs: evaluatingStateT s st +- warn: + lhs: "fst (usingState s st)" + rhs: evaluatingState s st +- warn: + lhs: "snd <$> usingStateT s st" + rhs: executingStateT s st +- warn: + lhs: "snd (usingState s st)" + rhs: executingState s st +- warn: + lhs: "MaybeT (pure m)" + rhs: hoistMaybe m +- warn: + lhs: "MaybeT (return m)" + rhs: hoistMaybe m +- warn: + lhs: MaybeT . pure + rhs: hoistMaybe +- warn: + lhs: MaybeT . return + rhs: hoistMaybe +- warn: + lhs: "ExceptT (pure m)" + rhs: hoistEither m +- warn: + lhs: "ExceptT (return m)" + rhs: hoistEither m +- warn: + lhs: ExceptT . pure + rhs: hoistEither +- warn: + lhs: ExceptT . return + rhs: hoistEither +- warn: + lhs: fromMaybe mempty + rhs: maybeToMonoid +- warn: + lhs: "m ?: mempty" + rhs: maybeToMonoid m +- warn: + lhs: "Data.Map.toAscList (Data.Map.fromList x)" + rhs: sortWith fst x +- warn: + lhs: "Data.Map.toDescList (Data.Map.fromList x)" + rhs: "sortWith (Down . fst) x" +- warn: + lhs: "Data.Set.toList (Data.Set.fromList l)" + rhs: sortNub l +- warn: + lhs: "Data.Set.assocs (Data.Set.fromList l)" + rhs: sortNub l +- warn: + lhs: "Data.Set.toAscList (Data.Set.fromList l)" + rhs: sortNub l +- warn: + lhs: "Data.HashSet.toList (Data.HashSet.fromList l)" + rhs: unstableNub l +- warn: + lhs: nub + note: "'nub' is O(n^2), 'ordNub' is O(n log n)" + rhs: ordNub +- warn: + lhs: "sortBy (comparing f)" + note: "If the function you are using for 'comparing' is slow, use 'sortOn' instead of 'sortWith', because 'sortOn' caches applications the function and 'sortWith' doesn't." + rhs: sortWith f +- warn: + lhs: sortOn fst + note: "'sortWith' will be faster here because it doesn't do caching" + rhs: sortWith fst +- warn: + lhs: sortOn snd + note: "'sortWith' will be faster here because it doesn't do caching" + rhs: sortWith snd +- warn: + lhs: "sortOn (Down . fst)" + note: "'sortWith' will be faster here because it doesn't do caching" + rhs: "sortWith (Down . fst)" +- warn: + lhs: "sortOn (Down . snd)" + note: "'sortWith' will be faster here because it doesn't do caching" + rhs: "sortWith (Down . snd)" +- warn: + lhs: Data.Text.IO.putStr + rhs: putText +- warn: + lhs: Data.Text.IO.putStrLn + rhs: putTextLn +- warn: + lhs: Data.Text.Lazy.IO.putStr + rhs: putLText +- warn: + lhs: Data.Text.Lazy.IO.putStrLn + rhs: putLTextLn +- warn: + lhs: Data.ByteString.Char8.putStr + rhs: putBS +- warn: + lhs: Data.ByteString.Char8.putStrLn + rhs: putBSLn +- warn: + lhs: Data.ByteString.Lazy.Char8.putStr + rhs: putLBS +- warn: + lhs: Data.ByteString.Lazy.Char8.putStrLn + rhs: putLBSLn +- warn: + lhs: Data.Text.Lazy.Text + rhs: LText +- warn: + lhs: Data.ByteString.Lazy.ByteString + rhs: LByteString +- warn: + lhs: Data.ByteString.UTF8.fromString + rhs: encodeUtf8 +- warn: + lhs: Data.ByteString.UTF8.toString + rhs: decodeUtf8 +- warn: + lhs: Data.Text.Encoding.encodeUtf8 + rhs: encodeUtf8 +- warn: + lhs: Data.Text.Encoding.decodeUtf8 + rhs: decodeUtf8 +- warn: + lhs: "Data.ByteString.Lazy.toStrict (encodeUtf8 x)" + rhs: encodeUtf8 x +- warn: + lhs: "toStrict (encodeUtf8 x)" + rhs: encodeUtf8 x +- warn: + lhs: "decodeUtf8 (Data.ByteString.Lazy.fromStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: "decodeUtf8 (fromStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: Data.ByteString.Lazy.UTF8.fromString + rhs: encodeUtf8 +- warn: + lhs: Data.ByteString.Lazy.UTF8.toString + rhs: decodeUtf8 +- warn: + lhs: "Data.ByteString.Lazy.fromStrict (Data.Text.Encoding.encodeUtf8 x)" + rhs: encodeUtf8 x +- warn: + lhs: "Data.ByteString.Lazy.fromStrict (encodeUtf8 x)" + rhs: encodeUtf8 x +- warn: + lhs: "Data.Text.Encoding.decodeUtf8 (Data.ByteString.Lazy.toStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: "Data.Text.Encoding.decodeUtf8 (toStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: "decodeUtf8 (Data.ByteString.Lazy.toStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: "decodeUtf8 (toStrict x)" + rhs: decodeUtf8 x +- warn: + lhs: Data.Text.pack + rhs: toText +- warn: + lhs: Data.Text.unpack + rhs: toString +- warn: + lhs: Data.Text.Lazy.pack + rhs: toLText +- warn: + lhs: Data.Text.Lazy.unpack + rhs: toString +- warn: + lhs: Data.Text.Lazy.toStrict + rhs: toText +- warn: + lhs: Data.Text.Lazy.fromStrict + rhs: toLText +- warn: + lhs: "Data.Text.pack (show x)" + rhs: show x +- warn: + lhs: "Data.Text.Lazy.pack (show x)" + rhs: show x +- warn: + lhs: Data.ByteString.Lazy.fromStrict + rhs: fromStrict +- warn: + lhs: Data.ByteString.Lazy.toStrict + rhs: toStrict +- warn: + lhs: Data.Text.Lazy.fromStrict + rhs: fromStrict +- warn: + lhs: Data.Text.Lazy.toStrict + rhs: toStrict +- warn: + lhs: Control.Applicative.Alternative + name: "Use 'Alternative' from Relude" + note: "'Alternative' is already exported from Relude" + rhs: Alternative +- warn: + lhs: Control.Applicative.empty + name: "Use 'empty' from Relude" + note: "'empty' is already exported from Relude" + rhs: empty +- warn: + lhs: "(Control.Applicative.<|>)" + name: "Use '<|>' from Relude" + note: "Operator '(<|>)' is already exported from Relude" + rhs: "(<|>)" +- warn: + lhs: Control.Applicative.some + name: "Use 'some' from Relude" + note: "'some' is already exported from Relude" + rhs: some +- warn: + lhs: Control.Applicative.many + name: "Use 'many' from Relude" + note: "'many' is already exported from Relude" + rhs: many +- warn: + lhs: Control.Applicative.Const + name: "Use 'Const' from Relude" + note: "'Const' is already exported from Relude" + rhs: Const +- warn: + lhs: Control.Applicative.getConst + name: "Use 'getConst' from Relude" + note: "'getConst' is already exported from Relude" + rhs: getConst +- warn: + lhs: Control.Applicative.ZipList + name: "Use 'ZipList' from Relude" + note: "'ZipList' is already exported from Relude" + rhs: ZipList +- warn: + lhs: Control.Applicative.getZipList + name: "Use 'getZipList' from Relude" + note: "'getZipList' is already exported from Relude" + rhs: getZipList +- warn: + lhs: Control.Applicative.liftA2 + name: "Use 'liftA2' from Relude" + note: "'liftA2' is already exported from Relude" + rhs: liftA2 +- warn: + lhs: Control.Applicative.liftA3 + name: "Use 'liftA3' from Relude" + note: "'liftA3' is already exported from Relude" + rhs: liftA3 +- warn: + lhs: Control.Applicative.optional + name: "Use 'optional' from Relude" + note: "'optional' is already exported from Relude" + rhs: optional +- warn: + lhs: "(Control.Applicative.<**>)" + name: "Use '<**>' from Relude" + note: "Operator '(<**>)' is already exported from Relude" + rhs: "(<**>)" +- warn: + lhs: Data.Bits.xor + name: "Use 'xor' from Relude" + note: "'xor' is already exported from Relude" + rhs: xor +- warn: + lhs: Data.Char.chr + name: "Use 'chr' from Relude" + note: "'chr' is already exported from Relude" + rhs: chr +- warn: + lhs: Data.Int.Int8 + name: "Use 'Int8' from Relude" + note: "'Int8' is already exported from Relude" + rhs: Int8 +- warn: + lhs: Data.Int.Int16 + name: "Use 'Int16' from Relude" + note: "'Int16' is already exported from Relude" + rhs: Int16 +- warn: + lhs: Data.Int.Int32 + name: "Use 'Int32' from Relude" + note: "'Int32' is already exported from Relude" + rhs: Int32 +- warn: + lhs: Data.Int.Int64 + name: "Use 'Int64' from Relude" + note: "'Int64' is already exported from Relude" + rhs: Int64 +- warn: + lhs: Data.Word.Word8 + name: "Use 'Word8' from Relude" + note: "'Word8' is already exported from Relude" + rhs: Word8 +- warn: + lhs: Data.Word.Word16 + name: "Use 'Word16' from Relude" + note: "'Word16' is already exported from Relude" + rhs: Word16 +- warn: + lhs: Data.Word.Word32 + name: "Use 'Word32' from Relude" + note: "'Word32' is already exported from Relude" + rhs: Word32 +- warn: + lhs: Data.Word.Word64 + name: "Use 'Word64' from Relude" + note: "'Word64' is already exported from Relude" + rhs: Word64 +- warn: + lhs: Data.Word.byteSwap16 + name: "Use 'byteSwap16' from Relude" + note: "'byteSwap16' is already exported from Relude" + rhs: byteSwap16 +- warn: + lhs: Data.Word.byteSwap32 + name: "Use 'byteSwap32' from Relude" + note: "'byteSwap32' is already exported from Relude" + rhs: byteSwap32 +- warn: + lhs: Data.Word.byteSwap64 + name: "Use 'byteSwap64' from Relude" + note: "'byteSwap64' is already exported from Relude" + rhs: byteSwap64 +- warn: + lhs: Numeric.Natural.Natural + name: "Use 'Natural' from Relude" + note: "'Natural' is already exported from Relude" + rhs: Natural +- warn: + lhs: System.IO.IOMode + name: "Use 'IOMode' from Relude" + note: "'IOMode' is already exported from Relude" + rhs: IOMode +- warn: + lhs: System.IO.ReadMode + name: "Use 'ReadMode' from Relude" + note: "'ReadMode' is already exported from Relude" + rhs: ReadMode +- warn: + lhs: System.IO.WriteMode + name: "Use 'WriteMode' from Relude" + note: "'WriteMode' is already exported from Relude" + rhs: WriteMode +- warn: + lhs: System.IO.AppendMode + name: "Use 'AppendMode' from Relude" + note: "'AppendMode' is already exported from Relude" + rhs: AppendMode +- warn: + lhs: System.IO.ReadWriteMode + name: "Use 'ReadWriteMode' from Relude" + note: "'ReadWriteMode' is already exported from Relude" + rhs: ReadWriteMode +- warn: + lhs: Data.Ord.Down + name: "Use 'Down' from Relude" + note: "'Down' is already exported from Relude" + rhs: Down +- warn: + lhs: Data.Ord.comparing + name: "Use 'comparing' from Relude" + note: "'comparing' is already exported from Relude" + rhs: comparing +- warn: + lhs: Data.Coerce.Coercible + name: "Use 'Coercible' from Relude" + note: "'Coercible' is already exported from Relude" + rhs: Coercible +- warn: + lhs: Data.Coerce.coerce + name: "Use 'coerce' from Relude" + note: "'coerce' is already exported from Relude" + rhs: coerce +- warn: + lhs: Data.Kind.Constraint + name: "Use 'Constraint' from Relude" + note: "'Constraint' is already exported from Relude" + rhs: Constraint +- warn: + lhs: Data.Kind.Type + name: "Use 'Type' from Relude" + note: "'Type' is already exported from Relude" + rhs: Type +- warn: + lhs: Data.Typeable.Typeable + name: "Use 'Typeable' from Relude" + note: "'Typeable' is already exported from Relude" + rhs: Typeable +- warn: + lhs: Data.Proxy.Proxy + name: "Use 'Proxy' from Relude" + note: "'Proxy' is already exported from Relude" + rhs: Proxy +- warn: + lhs: Data.Typeable.Typeable + name: "Use 'Typeable' from Relude" + note: "'Typeable' is already exported from Relude" + rhs: Typeable +- warn: + lhs: Data.Void.Void + name: "Use 'Void' from Relude" + note: "'Void' is already exported from Relude" + rhs: Void +- warn: + lhs: Data.Void.absurd + name: "Use 'absurd' from Relude" + note: "'absurd' is already exported from Relude" + rhs: absurd +- warn: + lhs: Data.Void.vacuous + name: "Use 'vacuous' from Relude" + note: "'vacuous' is already exported from Relude" + rhs: vacuous +- warn: + lhs: Data.Base.maxInt + name: "Use 'maxInt' from Relude" + note: "'maxInt' is already exported from Relude" + rhs: maxInt +- warn: + lhs: Data.Base.minInt + name: "Use 'minInt' from Relude" + note: "'minInt' is already exported from Relude" + rhs: minInt +- warn: + lhs: Data.Base.ord + name: "Use 'ord' from Relude" + note: "'ord' is already exported from Relude" + rhs: ord +- warn: + lhs: GHC.Enum.boundedEnumFrom + name: "Use 'boundedEnumFrom' from Relude" + note: "'boundedEnumFrom' is already exported from Relude" + rhs: boundedEnumFrom +- warn: + lhs: GHC.Enum.boundedEnumFromThen + name: "Use 'boundedEnumFromThen' from Relude" + note: "'boundedEnumFromThen' is already exported from Relude" + rhs: boundedEnumFromThen +- warn: + lhs: GHC.Generics.Generic + name: "Use 'Generic' from Relude" + note: "'Generic' is already exported from Relude" + rhs: Generic +- warn: + lhs: GHC.Real.Ratio + name: "Use 'Ratio' from Relude" + note: "'Ratio' is already exported from Relude" + rhs: Ratio +- warn: + lhs: GHC.Real.Rational + name: "Use 'Rational' from Relude" + note: "'Rational' is already exported from Relude" + rhs: Rational +- warn: + lhs: GHC.Real.denominator + name: "Use 'denominator' from Relude" + note: "'denominator' is already exported from Relude" + rhs: denominator +- warn: + lhs: GHC.Real.numerator + name: "Use 'numerator' from Relude" + note: "'numerator' is already exported from Relude" + rhs: numerator +- warn: + lhs: GHC.TypeNats.CmpNat + name: "Use 'CmpNat' from Relude" + note: "'CmpNat' is already exported from Relude" + rhs: CmpNat +- warn: + lhs: GHC.TypeNats.KnownNat + name: "Use 'KnownNat' from Relude" + note: "'KnownNat' is already exported from Relude" + rhs: KnownNat +- warn: + lhs: GHC.TypeNats.Nat + name: "Use 'Nat' from Relude" + note: "'Nat' is already exported from Relude" + rhs: Nat +- warn: + lhs: GHC.TypeNats.SomeNat + name: "Use 'SomeNat' from Relude" + note: "'SomeNat' is already exported from Relude" + rhs: SomeNat +- warn: + lhs: GHC.TypeNats.natVal + name: "Use 'natVal' from Relude" + note: "'natVal' is already exported from Relude" + rhs: natVal +- warn: + lhs: GHC.TypeNats.someNatVal + name: "Use 'someNatVal' from Relude" + note: "'someNatVal' is already exported from Relude" + rhs: someNatVal +- warn: + lhs: GHC.TypeLits.CmpNat + name: "Use 'CmpNat' from Relude" + note: "'CmpNat' is already exported from Relude" + rhs: CmpNat +- warn: + lhs: GHC.TypeLits.KnownNat + name: "Use 'KnownNat' from Relude" + note: "'KnownNat' is already exported from Relude" + rhs: KnownNat +- warn: + lhs: GHC.TypeLits.Nat + name: "Use 'Nat' from Relude" + note: "'Nat' is already exported from Relude" + rhs: Nat +- warn: + lhs: GHC.TypeLits.SomeNat + name: "Use 'SomeNat' from Relude" + note: "'SomeNat' is already exported from Relude" + rhs: SomeNat +- warn: + lhs: GHC.TypeLits.natVal + name: "Use 'natVal' from Relude" + note: "'natVal' is already exported from Relude" + rhs: natVal +- warn: + lhs: GHC.TypeLits.someNatVal + name: "Use 'someNatVal' from Relude" + note: "'someNatVal' is already exported from Relude" + rhs: someNatVal +- warn: + lhs: GHC.ExecutionStack.getStackTrace + name: "Use 'getStackTrace' from Relude" + note: "'getStackTrace' is already exported from Relude" + rhs: getStackTrace +- warn: + lhs: GHC.ExecutionStack.showStackTrace + name: "Use 'showStackTrace' from Relude" + note: "'showStackTrace' is already exported from Relude" + rhs: showStackTrace +- warn: + lhs: GHC.OverloadedLabels.IsLabel + name: "Use 'IsLabel' from Relude" + note: "'IsLabel' is already exported from Relude" + rhs: IsLabel +- warn: + lhs: GHC.OverloadedLabels.fromLabel + name: "Use 'fromLabel' from Relude" + note: "'fromLabel' is already exported from Relude" + rhs: fromLabel +- warn: + lhs: GHC.Stack.CallStack + name: "Use 'CallStack' from Relude" + note: "'CallStack' is already exported from Relude" + rhs: CallStack +- warn: + lhs: GHC.Stack.HasCallStack + name: "Use 'HasCallStack' from Relude" + note: "'HasCallStack' is already exported from Relude" + rhs: HasCallStack +- warn: + lhs: GHC.Stack.callStack + name: "Use 'callStack' from Relude" + note: "'callStack' is already exported from Relude" + rhs: callStack +- warn: + lhs: GHC.Stack.currentCallStack + name: "Use 'currentCallStack' from Relude" + note: "'currentCallStack' is already exported from Relude" + rhs: currentCallStack +- warn: + lhs: GHC.Stack.getCallStack + name: "Use 'getCallStack' from Relude" + note: "'getCallStack' is already exported from Relude" + rhs: getCallStack +- warn: + lhs: GHC.Stack.prettyCallStack + name: "Use 'prettyCallStack' from Relude" + note: "'prettyCallStack' is already exported from Relude" + rhs: prettyCallStack +- warn: + lhs: GHC.Stack.prettySrcLoc + name: "Use 'prettySrcLoc' from Relude" + note: "'prettySrcLoc' is already exported from Relude" + rhs: prettySrcLoc +- warn: + lhs: GHC.Stack.withFrozenCallStack + name: "Use 'withFrozenCallStack' from Relude" + note: "'withFrozenCallStack' is already exported from Relude" + rhs: withFrozenCallStack +- warn: + lhs: Data.Bifoldable.Bifoldable + name: "Use 'Bifoldable' from Relude" + note: "'Bifoldable' is already exported from Relude" + rhs: Bifoldable +- warn: + lhs: Data.Bifoldable.bifold + name: "Use 'bifold' from Relude" + note: "'bifold' is already exported from Relude" + rhs: bifold +- warn: + lhs: Data.Bifoldable.bifoldMap + name: "Use 'bifoldMap' from Relude" + note: "'bifoldMap' is already exported from Relude" + rhs: bifoldMap +- warn: + lhs: Data.Bifoldable.bifoldr + name: "Use 'bifoldr' from Relude" + note: "'bifoldr' is already exported from Relude" + rhs: bifoldr +- warn: + lhs: Data.Bifoldable.bifoldl + name: "Use 'bifoldl' from Relude" + note: "'bifoldl' is already exported from Relude" + rhs: bifoldl +- warn: + lhs: "Data.Bifoldable.bifoldl'" + name: "Use 'bifoldl'' from Relude" + note: "'bifoldl'' is already exported from Relude" + rhs: "bifoldl'" +- warn: + lhs: Data.Bifoldable.bifoldlM + name: "Use 'bifoldlM' from Relude" + note: "'bifoldlM' is already exported from Relude" + rhs: bifoldlM +- warn: + lhs: "Data.Bifoldable.bifoldr'" + name: "Use 'bifoldr'' from Relude" + note: "'bifoldr'' is already exported from Relude" + rhs: "bifoldr'" +- warn: + lhs: Data.Bifoldable.bifoldrM + name: "Use 'bifoldrM' from Relude" + note: "'bifoldrM' is already exported from Relude" + rhs: bifoldrM +- warn: + lhs: Data.Bifoldable.bitraverse_ + name: "Use 'bitraverse_' from Relude" + note: "'bitraverse_' is already exported from Relude" + rhs: bitraverse_ +- warn: + lhs: Data.Bifoldable.bifor_ + name: "Use 'bifor_' from Relude" + note: "'bifor_' is already exported from Relude" + rhs: bifor_ +- warn: + lhs: Data.Bifoldable.biasum + name: "Use 'biasum' from Relude" + note: "'biasum' is already exported from Relude" + rhs: biasum +- warn: + lhs: Data.Bifoldable.bisequence_ + name: "Use 'bisequence_' from Relude" + note: "'bisequence_' is already exported from Relude" + rhs: bisequence_ +- warn: + lhs: Data.Bifoldable.biList + name: "Use 'biList' from Relude" + note: "'biList' is already exported from Relude" + rhs: biList +- warn: + lhs: Data.Bifoldable.binull + name: "Use 'binull' from Relude" + note: "'binull' is already exported from Relude" + rhs: binull +- warn: + lhs: Data.Bifoldable.bilength + name: "Use 'bilength' from Relude" + note: "'bilength' is already exported from Relude" + rhs: bilength +- warn: + lhs: Data.Bifoldable.bielem + name: "Use 'bielem' from Relude" + note: "'bielem' is already exported from Relude" + rhs: bielem +- warn: + lhs: Data.Bifoldable.biand + name: "Use 'biand' from Relude" + note: "'biand' is already exported from Relude" + rhs: biand +- warn: + lhs: Data.Bifoldable.bior + name: "Use 'bior' from Relude" + note: "'bior' is already exported from Relude" + rhs: bior +- warn: + lhs: Data.Bifoldable.biany + name: "Use 'biany' from Relude" + note: "'biany' is already exported from Relude" + rhs: biany +- warn: + lhs: Data.Bifoldable.biall + name: "Use 'biall' from Relude" + note: "'biall' is already exported from Relude" + rhs: biall +- warn: + lhs: Data.Bifoldable.bifind + name: "Use 'bifind' from Relude" + note: "'bifind' is already exported from Relude" + rhs: bifind +- warn: + lhs: Data.Bitraversable.Bitraversable + name: "Use 'Bitraversable' from Relude" + note: "'Bitraversable' is already exported from Relude" + rhs: Bitraversable +- warn: + lhs: Data.Bitraversable.bitraverse + name: "Use 'bitraverse' from Relude" + note: "'bitraverse' is already exported from Relude" + rhs: bitraverse +- warn: + lhs: Data.Bitraversable.bisequence + name: "Use 'bisequence' from Relude" + note: "'bisequence' is already exported from Relude" + rhs: bisequence +- warn: + lhs: Data.Bitraversable.bifor + name: "Use 'bifor' from Relude" + note: "'bifor' is already exported from Relude" + rhs: bifor +- warn: + lhs: Data.Bitraversable.bimapDefault + name: "Use 'bimapDefault' from Relude" + note: "'bimapDefault' is already exported from Relude" + rhs: bimapDefault +- warn: + lhs: Data.Bitraversable.bifoldMapDefault + name: "Use 'bifoldMapDefault' from Relude" + note: "'bifoldMapDefault' is already exported from Relude" + rhs: bifoldMapDefault +- warn: + lhs: Control.Monad.guard + name: "Use 'guard' from Relude" + note: "'guard' is already exported from Relude" + rhs: guard +- warn: + lhs: Control.Monad.unless + name: "Use 'unless' from Relude" + note: "'unless' is already exported from Relude" + rhs: unless +- warn: + lhs: Control.Monad.when + name: "Use 'when' from Relude" + note: "'when' is already exported from Relude" + rhs: when +- warn: + lhs: Data.Bool.bool + name: "Use 'bool' from Relude" + note: "'bool' is already exported from Relude" + rhs: bool +- warn: + lhs: Data.Hashable.Hashable + name: "Use 'Hashable' from Relude" + note: "'Hashable' is already exported from Relude" + rhs: Hashable +- warn: + lhs: Data.Hashable.hashWithSalt + name: "Use 'hashWithSalt' from Relude" + note: "'hashWithSalt' is already exported from Relude" + rhs: hashWithSalt +- warn: + lhs: Data.HashMap.Strict.HashMap + name: "Use 'HashMap' from Relude" + note: "'HashMap' is already exported from Relude" + rhs: HashMap +- warn: + lhs: Data.HashSet.HashSet + name: "Use 'HashSet' from Relude" + note: "'HashSet' is already exported from Relude" + rhs: HashSet +- warn: + lhs: Data.IntMap.Strict.IntMap + name: "Use 'IntMap' from Relude" + note: "'IntMap' is already exported from Relude" + rhs: IntMap +- warn: + lhs: Data.IntSet.IntSet + name: "Use 'IntSet' from Relude" + note: "'IntSet' is already exported from Relude" + rhs: IntSet +- warn: + lhs: Data.Map.Strict.Map + name: "Use 'Map' from Relude" + note: "'Map' is already exported from Relude" + rhs: Map +- warn: + lhs: Data.Sequence.Sequence + name: "Use 'Sequence' from Relude" + note: "'Sequence' is already exported from Relude" + rhs: Sequence +- warn: + lhs: Data.Set.Set + name: "Use 'Set' from Relude" + note: "'Set' is already exported from Relude" + rhs: Set +- warn: + lhs: Data.Tuple.swap + name: "Use 'swap' from Relude" + note: "'swap' is already exported from Relude" + rhs: swap +- warn: + lhs: Data.Vector.Vector + name: "Use 'Vector' from Relude" + note: "'Vector' is already exported from Relude" + rhs: Vector +- warn: + lhs: GHC.Exts.IsList + name: "Use 'IsList' from Relude" + note: "'IsList' is already exported from Relude" + rhs: IsList +- warn: + lhs: GHC.Exts.fromList + name: "Use 'fromList' from Relude" + note: "'fromList' is already exported from Relude" + rhs: fromList +- warn: + lhs: GHC.Exts.fromListN + name: "Use 'fromListN' from Relude" + note: "'fromListN' is already exported from Relude" + rhs: fromListN +- warn: + lhs: Debug.Trace.trace + name: "Use 'trace' from Relude" + note: "'trace' is already exported from Relude" + rhs: trace +- warn: + lhs: Debug.Trace.traceShow + name: "Use 'traceShow' from Relude" + note: "'traceShow' is already exported from Relude" + rhs: traceShow +- warn: + lhs: Debug.Trace.traceShowId + name: "Use 'traceShowId' from Relude" + note: "'traceShowId' is already exported from Relude" + rhs: traceShowId +- warn: + lhs: Debug.Trace.traceShowM + name: "Use 'traceShowM' from Relude" + note: "'traceShowM' is already exported from Relude" + rhs: traceShowM +- warn: + lhs: Debug.Trace.traceM + name: "Use 'traceM' from Relude" + note: "'traceM' is already exported from Relude" + rhs: traceM +- warn: + lhs: Debug.Trace.traceId + name: "Use 'traceId' from Relude" + note: "'traceId' is already exported from Relude" + rhs: traceId +- warn: + lhs: Control.DeepSeq.NFData + name: "Use 'NFData' from Relude" + note: "'NFData' is already exported from Relude" + rhs: NFData +- warn: + lhs: Control.DeepSeq.rnf + name: "Use 'rnf' from Relude" + note: "'rnf' is already exported from Relude" + rhs: rnf +- warn: + lhs: Control.DeepSeq.deepseq + name: "Use 'deepseq' from Relude" + note: "'deepseq' is already exported from Relude" + rhs: deepseq +- warn: + lhs: Control.DeepSeq.force + name: "Use 'force' from Relude" + note: "'force' is already exported from Relude" + rhs: force +- warn: + lhs: "(Control.DeepSeq.$!!)" + name: "Use '$!!' from Relude" + note: "Operator '($!!)' is already exported from Relude" + rhs: "($!!)" +- warn: + lhs: Control.Exception.Exception + name: "Use 'Exception' from Relude" + note: "'Exception' is already exported from Relude" + rhs: Exception +- warn: + lhs: Control.Exception.SomeException + name: "Use 'SomeException' from Relude" + note: "'SomeException' is already exported from Relude" + rhs: SomeException +- warn: + lhs: Control.Exception.toException + name: "Use 'toException' from Relude" + note: "'toException' is already exported from Relude" + rhs: toException +- warn: + lhs: Control.Exception.fromException + name: "Use 'fromException' from Relude" + note: "'fromException' is already exported from Relude" + rhs: fromException +- warn: + lhs: Control.Exception.displayException + name: "Use 'displayException' from Relude" + note: "'displayException' is already exported from Relude" + rhs: displayException +- warn: + lhs: Data.Foldable.asum + name: "Use 'asum' from Relude" + note: "'asum' is already exported from Relude" + rhs: asum +- warn: + lhs: Data.Foldable.find + name: "Use 'find' from Relude" + note: "'find' is already exported from Relude" + rhs: find +- warn: + lhs: Data.Foldable.find + name: "Use 'find' from Relude" + note: "'find' is already exported from Relude" + rhs: find +- warn: + lhs: Data.Foldable.fold + name: "Use 'fold' from Relude" + note: "'fold' is already exported from Relude" + rhs: fold +- warn: + lhs: "Data.Foldable.foldl'" + name: "Use 'foldl'' from Relude" + note: "'foldl'' is already exported from Relude" + rhs: "foldl'" +- warn: + lhs: Data.Foldable.foldrM + name: "Use 'foldrM' from Relude" + note: "'foldrM' is already exported from Relude" + rhs: foldrM +- warn: + lhs: Data.Foldable.forM_ + name: "Use 'forM_' from Relude" + note: "'forM_' is already exported from Relude" + rhs: forM_ +- warn: + lhs: Data.Foldable.for_ + name: "Use 'for_' from Relude" + note: "'for_' is already exported from Relude" + rhs: for_ +- warn: + lhs: Data.Foldable.sequenceA_ + name: "Use 'sequenceA_' from Relude" + note: "'sequenceA_' is already exported from Relude" + rhs: sequenceA_ +- warn: + lhs: Data.Foldable.toList + name: "Use 'toList' from Relude" + note: "'toList' is already exported from Relude" + rhs: toList +- warn: + lhs: Data.Foldable.traverse_ + name: "Use 'traverse_' from Relude" + note: "'traverse_' is already exported from Relude" + rhs: traverse_ +- warn: + lhs: Data.Traversable.forM + name: "Use 'forM' from Relude" + note: "'forM' is already exported from Relude" + rhs: forM +- warn: + lhs: Data.Traversable.mapAccumL + name: "Use 'mapAccumL' from Relude" + note: "'mapAccumL' is already exported from Relude" + rhs: mapAccumL +- warn: + lhs: Data.Traversable.mapAccumR + name: "Use 'mapAccumR' from Relude" + note: "'mapAccumR' is already exported from Relude" + rhs: mapAccumR +- warn: + lhs: "(Control.Arrow.&&&)" + name: "Use '&&&' from Relude" + note: "Operator '(&&&)' is already exported from Relude" + rhs: "(&&&)" +- warn: + lhs: "(Control.Category.>>>)" + name: "Use '>>>' from Relude" + note: "Operator '(>>>)' is already exported from Relude" + rhs: "(>>>)" +- warn: + lhs: "(Control.Category.<<<)" + name: "Use '<<<' from Relude" + note: "Operator '(<<<)' is already exported from Relude" + rhs: "(<<<)" +- warn: + lhs: Data.Function.fix + name: "Use 'fix' from Relude" + note: "'fix' is already exported from Relude" + rhs: fix +- warn: + lhs: Data.Function.on + name: "Use 'on' from Relude" + note: "'on' is already exported from Relude" + rhs: "on" +- warn: + lhs: Data.Bifunctor.Bifunctor + name: "Use 'Bifunctor' from Relude" + note: "'Bifunctor' is already exported from Relude" + rhs: Bifunctor +- warn: + lhs: Data.Bifunctor.bimap + name: "Use 'bimap' from Relude" + note: "'bimap' is already exported from Relude" + rhs: bimap +- warn: + lhs: Data.Bifunctor.first + name: "Use 'first' from Relude" + note: "'first' is already exported from Relude" + rhs: first +- warn: + lhs: Data.Bifunctor.second + name: "Use 'second' from Relude" + note: "'second' is already exported from Relude" + rhs: second +- warn: + lhs: Data.Functor.void + name: "Use 'void' from Relude" + note: "'void' is already exported from Relude" + rhs: void +- warn: + lhs: "(Data.Functor.$>)" + name: "Use '$>' from Relude" + note: "Operator '($>)' is already exported from Relude" + rhs: "($>)" +- warn: + lhs: "(Data.Functor.<&>)" + name: "Use '<&>' from Relude" + note: "Operator '(<&>)' is already exported from Relude" + rhs: "(<&>)" +- warn: + lhs: Data.Functor.Compose.Compose + name: "Use 'Compose' from Relude" + note: "'Compose' is already exported from Relude" + rhs: Compose +- warn: + lhs: Data.Functor.Compose.getCompose + name: "Use 'getCompose' from Relude" + note: "'getCompose' is already exported from Relude" + rhs: getCompose +- warn: + lhs: Data.Functor.Identity.Identity + name: "Use 'Identity' from Relude" + note: "'Identity' is already exported from Relude" + rhs: Identity +- warn: + lhs: Data.Functor.Identity.runIdentity + name: "Use 'runIdentity' from Relude" + note: "'runIdentity' is already exported from Relude" + rhs: runIdentity +- warn: + lhs: Control.Concurrent.MVar.MVar + name: "Use 'MVar' from Relude" + note: "'MVar' is already exported from Relude" + rhs: MVar +- warn: + lhs: Control.Concurrent.MVar.newEmptyMVar + name: "Use 'newEmptyMVar' from Relude" + note: "'newEmptyMVar' is already exported from Relude" + rhs: newEmptyMVar +- warn: + lhs: Control.Concurrent.MVar.newMVar + name: "Use 'newMVar' from Relude" + note: "'newMVar' is already exported from Relude" + rhs: newMVar +- warn: + lhs: Control.Concurrent.MVar.putMVar + name: "Use 'putMVar' from Relude" + note: "'putMVar' is already exported from Relude" + rhs: putMVar +- warn: + lhs: Control.Concurrent.MVar.readMVar + name: "Use 'readMVar' from Relude" + note: "'readMVar' is already exported from Relude" + rhs: readMVar +- warn: + lhs: Control.Concurrent.MVar.swapMVar + name: "Use 'swapMVar' from Relude" + note: "'swapMVar' is already exported from Relude" + rhs: swapMVar +- warn: + lhs: Control.Concurrent.MVar.takeMVar + name: "Use 'takeMVar' from Relude" + note: "'takeMVar' is already exported from Relude" + rhs: takeMVar +- warn: + lhs: Control.Concurrent.MVar.tryPutMVar + name: "Use 'tryPutMVar' from Relude" + note: "'tryPutMVar' is already exported from Relude" + rhs: tryPutMVar +- warn: + lhs: Control.Concurrent.MVar.tryReadMVar + name: "Use 'tryReadMVar' from Relude" + note: "'tryReadMVar' is already exported from Relude" + rhs: tryReadMVar +- warn: + lhs: Control.Concurrent.MVar.tryTakeMVar + name: "Use 'tryTakeMVar' from Relude" + note: "'tryTakeMVar' is already exported from Relude" + rhs: tryTakeMVar +- warn: + lhs: Control.Monad.STM.STM + name: "Use 'STM' from Relude" + note: "'STM' is already exported from Relude" + rhs: STM +- warn: + lhs: Control.Monad.STM.atomically + name: "Use 'atomically' from Relude" + note: "'atomically' is already exported from Relude" + rhs: atomically +- warn: + lhs: Control.Monad.STM.throwSTM + name: "Use 'throwSTM' from Relude" + note: "'throwSTM' is already exported from Relude" + rhs: throwSTM +- warn: + lhs: Control.Monad.STM.catchSTM + name: "Use 'catchSTM' from Relude" + note: "'catchSTM' is already exported from Relude" + rhs: catchSTM +- warn: + lhs: Control.Concurrent.STM.TVar.TVar + name: "Use 'TVar' from Relude" + note: "'TVar' is already exported from Relude" + rhs: TVar +- warn: + lhs: Control.Concurrent.STM.TVar.newTVarIO + name: "Use 'newTVarIO' from Relude" + note: "'newTVarIO' is already exported from Relude" + rhs: newTVarIO +- warn: + lhs: Control.Concurrent.STM.TVar.readTVarIO + name: "Use 'readTVarIO' from Relude" + note: "'readTVarIO' is already exported from Relude" + rhs: readTVarIO +- warn: + lhs: "Control.Concurrent.STM.TVar.modifyTVar'" + name: "Use 'modifyTVar'' from Relude" + note: "'modifyTVar'' is already exported from Relude" + rhs: "modifyTVar'" +- warn: + lhs: Control.Concurrent.STM.TVar.newTVar + name: "Use 'newTVar' from Relude" + note: "'newTVar' is already exported from Relude" + rhs: newTVar +- warn: + lhs: Control.Concurrent.STM.TVar.readTVar + name: "Use 'readTVar' from Relude" + note: "'readTVar' is already exported from Relude" + rhs: readTVar +- warn: + lhs: Control.Concurrent.STM.TVar.writeTVar + name: "Use 'writeTVar' from Relude" + note: "'writeTVar' is already exported from Relude" + rhs: writeTVar +- warn: + lhs: Control.Concurrent.STM.TMVar.TMVar + name: "Use 'TMVar' from Relude" + note: "'TMVar' is already exported from Relude" + rhs: TMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.newTMVar + name: "Use 'newTMVar' from Relude" + note: "'newTMVar' is already exported from Relude" + rhs: newTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.newEmptyTMVar + name: "Use 'newEmptyTMVar' from Relude" + note: "'newEmptyTMVar' is already exported from Relude" + rhs: newEmptyTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.newTMVarIO + name: "Use 'newTMVarIO' from Relude" + note: "'newTMVarIO' is already exported from Relude" + rhs: newTMVarIO +- warn: + lhs: Control.Concurrent.STM.TMVar.newEmptyTMVarIO + name: "Use 'newEmptyTMVarIO' from Relude" + note: "'newEmptyTMVarIO' is already exported from Relude" + rhs: newEmptyTMVarIO +- warn: + lhs: Control.Concurrent.STM.TMVar.takeTMVar + name: "Use 'takeTMVar' from Relude" + note: "'takeTMVar' is already exported from Relude" + rhs: takeTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.putTMVar + name: "Use 'putTMVar' from Relude" + note: "'putTMVar' is already exported from Relude" + rhs: putTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.readTMVar + name: "Use 'readTMVar' from Relude" + note: "'readTMVar' is already exported from Relude" + rhs: readTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.tryReadTMVar + name: "Use 'tryReadTMVar' from Relude" + note: "'tryReadTMVar' is already exported from Relude" + rhs: tryReadTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.swapTMVar + name: "Use 'swapTMVar' from Relude" + note: "'swapTMVar' is already exported from Relude" + rhs: swapTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.tryTakeTMVar + name: "Use 'tryTakeTMVar' from Relude" + note: "'tryTakeTMVar' is already exported from Relude" + rhs: tryTakeTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.tryPutTMVar + name: "Use 'tryPutTMVar' from Relude" + note: "'tryPutTMVar' is already exported from Relude" + rhs: tryPutTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.isEmptyTMVar + name: "Use 'isEmptyTMVar' from Relude" + note: "'isEmptyTMVar' is already exported from Relude" + rhs: isEmptyTMVar +- warn: + lhs: Control.Concurrent.STM.TMVar.mkWeakTMVar + name: "Use 'mkWeakTMVar' from Relude" + note: "'mkWeakTMVar' is already exported from Relude" + rhs: mkWeakTMVar +- warn: + lhs: Data.IORef.IORef + name: "Use 'IORef' from Relude" + note: "'IORef' is already exported from Relude" + rhs: IORef +- warn: + lhs: Data.IORef.atomicModifyIORef + name: "Use 'atomicModifyIORef' from Relude" + note: "'atomicModifyIORef' is already exported from Relude" + rhs: atomicModifyIORef +- warn: + lhs: "Data.IORef.atomicModifyIORef'" + name: "Use 'atomicModifyIORef'' from Relude" + note: "'atomicModifyIORef'' is already exported from Relude" + rhs: "atomicModifyIORef'" +- warn: + lhs: Data.IORef.atomicWriteIORef + name: "Use 'atomicWriteIORef' from Relude" + note: "'atomicWriteIORef' is already exported from Relude" + rhs: atomicWriteIORef +- warn: + lhs: Data.IORef.modifyIORef + name: "Use 'modifyIORef' from Relude" + note: "'modifyIORef' is already exported from Relude" + rhs: modifyIORef +- warn: + lhs: "Data.IORef.modifyIORef'" + name: "Use 'modifyIORef'' from Relude" + note: "'modifyIORef'' is already exported from Relude" + rhs: "modifyIORef'" +- warn: + lhs: Data.IORef.newIORef + name: "Use 'newIORef' from Relude" + note: "'newIORef' is already exported from Relude" + rhs: newIORef +- warn: + lhs: Data.IORef.readIORef + name: "Use 'readIORef' from Relude" + note: "'readIORef' is already exported from Relude" + rhs: readIORef +- warn: + lhs: Data.IORef.writeIORef + name: "Use 'writeIORef' from Relude" + note: "'writeIORef' is already exported from Relude" + rhs: writeIORef +- warn: + lhs: "atomicModifyIORef ref (\\a -> (f a, ()))" + rhs: atomicModifyIORef_ ref f +- warn: + lhs: "atomicModifyIORef ref $ \\a -> (f a, ())" + rhs: atomicModifyIORef_ ref f +- warn: + lhs: "atomicModifyIORef' ref $ \\a -> (f a, ())" + rhs: "atomicModifyIORef'_ ref f" +- warn: + lhs: "atomicModifyIORef' ref (\\a -> (f a, ()))" + rhs: "atomicModifyIORef'_ ref f" +- warn: + lhs: Data.Text.IO.getLine + name: "Use 'getLine' from Relude" + note: "'getLine' is already exported from Relude" + rhs: getLine +- warn: + lhs: System.IO.hFlush + name: "Use 'hFlush' from Relude" + note: "'hFlush' is already exported from Relude" + rhs: hFlush +- warn: + lhs: System.IO.hIsEOF + name: "Use 'hIsEOF' from Relude" + note: "'hIsEOF' is already exported from Relude" + rhs: hIsEOF +- warn: + lhs: System.IO.hSetBuffering + name: "Use 'hSetBuffering' from Relude" + note: "'hSetBuffering' is already exported from Relude" + rhs: hSetBuffering +- warn: + lhs: System.IO.hGetBuffering + name: "Use 'hGetBuffering' from Relude" + note: "'hGetBuffering' is already exported from Relude" + rhs: hGetBuffering +- warn: + lhs: System.IO.Handle + name: "Use 'Handle' from Relude" + note: "'Handle' is already exported from Relude" + rhs: Handle +- warn: + lhs: System.IO.stdin + name: "Use 'stdin' from Relude" + note: "'stdin' is already exported from Relude" + rhs: stdin +- warn: + lhs: System.IO.stdout + name: "Use 'stdout' from Relude" + note: "'stdout' is already exported from Relude" + rhs: stdout +- warn: + lhs: System.IO.stderr + name: "Use 'stderr' from Relude" + note: "'stderr' is already exported from Relude" + rhs: stderr +- warn: + lhs: System.IO.withFile + name: "Use 'withFile' from Relude" + note: "'withFile' is already exported from Relude" + rhs: withFile +- warn: + lhs: System.IO.BufferMode + name: "Use 'BufferMode' from Relude" + note: "'BufferMode' is already exported from Relude" + rhs: BufferMode +- warn: + lhs: System.Environment.getArgs + name: "Use 'getArgs' from Relude" + note: "'getArgs' is already exported from Relude" + rhs: getArgs +- warn: + lhs: System.Environment.lookupEnv + name: "Use 'lookupEnv' from Relude" + note: "'lookupEnv' is already exported from Relude" + rhs: lookupEnv +- warn: + lhs: Data.List.genericDrop + name: "Use 'genericDrop' from Relude" + note: "'genericDrop' is already exported from Relude" + rhs: genericDrop +- warn: + lhs: Data.List.genericLength + name: "Use 'genericLength' from Relude" + note: "'genericLength' is already exported from Relude" + rhs: genericLength +- warn: + lhs: Data.List.genericReplicate + name: "Use 'genericReplicate' from Relude" + note: "'genericReplicate' is already exported from Relude" + rhs: genericReplicate +- warn: + lhs: Data.List.genericSplitAt + name: "Use 'genericSplitAt' from Relude" + note: "'genericSplitAt' is already exported from Relude" + rhs: genericSplitAt +- warn: + lhs: Data.List.genericTake + name: "Use 'genericTake' from Relude" + note: "'genericTake' is already exported from Relude" + rhs: genericTake +- warn: + lhs: Data.List.group + name: "Use 'group' from Relude" + note: "'group' is already exported from Relude" + rhs: group +- warn: + lhs: Data.List.inits + name: "Use 'inits' from Relude" + note: "'inits' is already exported from Relude" + rhs: inits +- warn: + lhs: Data.List.intercalate + name: "Use 'intercalate' from Relude" + note: "'intercalate' is already exported from Relude" + rhs: intercalate +- warn: + lhs: Data.List.intersperse + name: "Use 'intersperse' from Relude" + note: "'intersperse' is already exported from Relude" + rhs: intersperse +- warn: + lhs: Data.List.isPrefixOf + name: "Use 'isPrefixOf' from Relude" + note: "'isPrefixOf' is already exported from Relude" + rhs: isPrefixOf +- warn: + lhs: Data.List.permutations + name: "Use 'permutations' from Relude" + note: "'permutations' is already exported from Relude" + rhs: permutations +- warn: + lhs: "Data.List.scanl'" + name: "Use 'scanl'' from Relude" + note: "'scanl'' is already exported from Relude" + rhs: "scanl'" +- warn: + lhs: Data.List.sort + name: "Use 'sort' from Relude" + note: "'sort' is already exported from Relude" + rhs: sort +- warn: + lhs: Data.List.sortBy + name: "Use 'sortBy' from Relude" + note: "'sortBy' is already exported from Relude" + rhs: sortBy +- warn: + lhs: Data.List.sortOn + name: "Use 'sortOn' from Relude" + note: "'sortOn' is already exported from Relude" + rhs: sortOn +- warn: + lhs: Data.List.subsequences + name: "Use 'subsequences' from Relude" + note: "'subsequences' is already exported from Relude" + rhs: subsequences +- warn: + lhs: Data.List.tails + name: "Use 'tails' from Relude" + note: "'tails' is already exported from Relude" + rhs: tails +- warn: + lhs: Data.List.transpose + name: "Use 'transpose' from Relude" + note: "'transpose' is already exported from Relude" + rhs: transpose +- warn: + lhs: Data.List.uncons + name: "Use 'uncons' from Relude" + note: "'uncons' is already exported from Relude" + rhs: uncons +- warn: + lhs: Data.List.unfoldr + name: "Use 'unfoldr' from Relude" + note: "'unfoldr' is already exported from Relude" + rhs: unfoldr +- warn: + lhs: Data.List.NonEmpty.NonEmpty + name: "Use 'NonEmpty' from Relude" + note: "'NonEmpty' is already exported from Relude" + rhs: NonEmpty +- warn: + lhs: "(Data.List.NonEmpty.:|)" + name: "Use ':|' from Relude" + note: "Operator '(:|)' is already exported from Relude" + rhs: "(:|)" +- warn: + lhs: Data.List.NonEmpty.nonEmpty + name: "Use 'nonEmpty' from Relude" + note: "'nonEmpty' is already exported from Relude" + rhs: nonEmpty +- warn: + lhs: Data.List.NonEmpty.head + name: "Use 'head' from Relude" + note: "'head' is already exported from Relude" + rhs: head +- warn: + lhs: Data.List.NonEmpty.init + name: "Use 'init' from Relude" + note: "'init' is already exported from Relude" + rhs: init +- warn: + lhs: Data.List.NonEmpty.last + name: "Use 'last' from Relude" + note: "'last' is already exported from Relude" + rhs: last +- warn: + lhs: Data.List.NonEmpty.tail + name: "Use 'tail' from Relude" + note: "'tail' is already exported from Relude" + rhs: tail +- warn: + lhs: GHC.Exts.sortWith + name: "Use 'sortWith' from Relude" + note: "'sortWith' is already exported from Relude" + rhs: sortWith +- warn: + lhs: Control.Monad.Except.ExceptT + name: "Use 'ExceptT' from Relude" + note: "'ExceptT' is already exported from Relude" + rhs: ExceptT +- warn: + lhs: Control.Monad.Except.runExceptT + name: "Use 'runExceptT' from Relude" + note: "'runExceptT' is already exported from Relude" + rhs: runExceptT +- warn: + lhs: Control.Monad.Reader.MonadReader + name: "Use 'MonadReader' from Relude" + note: "'MonadReader' is already exported from Relude" + rhs: MonadReader +- warn: + lhs: Control.Monad.Reader.Reader + name: "Use 'Reader' from Relude" + note: "'Reader' is already exported from Relude" + rhs: Reader +- warn: + lhs: Control.Monad.Reader.ReaderT + name: "Use 'ReaderT' from Relude" + note: "'ReaderT' is already exported from Relude" + rhs: ReaderT +- warn: + lhs: Control.Monad.Reader.runReaderT + name: "Use 'runReaderT' from Relude" + note: "'runReaderT' is already exported from Relude" + rhs: runReaderT +- warn: + lhs: Control.Monad.Reader.ask + name: "Use 'ask' from Relude" + note: "'ask' is already exported from Relude" + rhs: ask +- warn: + lhs: Control.Monad.Reader.asks + name: "Use 'asks' from Relude" + note: "'asks' is already exported from Relude" + rhs: asks +- warn: + lhs: Control.Monad.Reader.local + name: "Use 'local' from Relude" + note: "'local' is already exported from Relude" + rhs: local +- warn: + lhs: Control.Monad.Reader.reader + name: "Use 'reader' from Relude" + note: "'reader' is already exported from Relude" + rhs: reader +- warn: + lhs: Control.Monad.Reader.runReader + name: "Use 'runReader' from Relude" + note: "'runReader' is already exported from Relude" + rhs: runReader +- warn: + lhs: Control.Monad.Reader.withReader + name: "Use 'withReader' from Relude" + note: "'withReader' is already exported from Relude" + rhs: withReader +- warn: + lhs: Control.Monad.Reader.withReaderT + name: "Use 'withReaderT' from Relude" + note: "'withReaderT' is already exported from Relude" + rhs: withReaderT +- warn: + lhs: Control.Monad.State.Strict.MonadState + name: "Use 'MonadState' from Relude" + note: "'MonadState' is already exported from Relude" + rhs: MonadState +- warn: + lhs: Control.Monad.State.Strict.State + name: "Use 'State' from Relude" + note: "'State' is already exported from Relude" + rhs: State +- warn: + lhs: Control.Monad.State.Strict.StateT + name: "Use 'StateT' from Relude" + note: "'StateT' is already exported from Relude" + rhs: StateT +- warn: + lhs: Control.Monad.State.Strict.runStateT + name: "Use 'runStateT' from Relude" + note: "'runStateT' is already exported from Relude" + rhs: runStateT +- warn: + lhs: Control.Monad.State.Strict.evalState + name: "Use 'evalState' from Relude" + note: "'evalState' is already exported from Relude" + rhs: evalState +- warn: + lhs: Control.Monad.State.Strict.evalStateT + name: "Use 'evalStateT' from Relude" + note: "'evalStateT' is already exported from Relude" + rhs: evalStateT +- warn: + lhs: Control.Monad.State.Strict.execState + name: "Use 'execState' from Relude" + note: "'execState' is already exported from Relude" + rhs: execState +- warn: + lhs: Control.Monad.State.Strict.execStateT + name: "Use 'execStateT' from Relude" + note: "'execStateT' is already exported from Relude" + rhs: execStateT +- warn: + lhs: Control.Monad.State.Strict.get + name: "Use 'get' from Relude" + note: "'get' is already exported from Relude" + rhs: get +- warn: + lhs: Control.Monad.State.Strict.gets + name: "Use 'gets' from Relude" + note: "'gets' is already exported from Relude" + rhs: gets +- warn: + lhs: Control.Monad.State.Strict.modify + name: "Use 'modify' from Relude" + note: "'modify' is already exported from Relude" + rhs: modify +- warn: + lhs: "Control.Monad.State.Strict.modify'" + name: "Use 'modify'' from Relude" + note: "'modify'' is already exported from Relude" + rhs: "modify'" +- warn: + lhs: Control.Monad.State.Strict.put + name: "Use 'put' from Relude" + note: "'put' is already exported from Relude" + rhs: put +- warn: + lhs: Control.Monad.State.Strict.runState + name: "Use 'runState' from Relude" + note: "'runState' is already exported from Relude" + rhs: runState +- warn: + lhs: Control.Monad.State.Strict.state + name: "Use 'state' from Relude" + note: "'state' is already exported from Relude" + rhs: state +- warn: + lhs: Control.Monad.State.Strict.withState + name: "Use 'withState' from Relude" + note: "'withState' is already exported from Relude" + rhs: withState +- warn: + lhs: Control.Monad.Trans.MonadIO + name: "Use 'MonadIO' from Relude" + note: "'MonadIO' is already exported from Relude" + rhs: MonadIO +- warn: + lhs: Control.Monad.Trans.MonadTrans + name: "Use 'MonadTrans' from Relude" + note: "'MonadTrans' is already exported from Relude" + rhs: MonadTrans +- warn: + lhs: Control.Monad.Trans.lift + name: "Use 'lift' from Relude" + note: "'lift' is already exported from Relude" + rhs: lift +- warn: + lhs: Control.Monad.Trans.liftIO + name: "Use 'liftIO' from Relude" + note: "'liftIO' is already exported from Relude" + rhs: liftIO +- warn: + lhs: Control.Monad.Trans.Identity.IdentityT + name: "Use 'IdentityT' from Relude" + note: "'IdentityT' is already exported from Relude" + rhs: IdentityT +- warn: + lhs: Control.Monad.Trans.Identity.runIdentityT + name: "Use 'runIdentityT' from Relude" + note: "'runIdentityT' is already exported from Relude" + rhs: runIdentityT +- warn: + lhs: Control.Monad.Trans.Maybe.MaybeT + name: "Use 'MaybeT' from Relude" + note: "'MaybeT' is already exported from Relude" + rhs: MaybeT +- warn: + lhs: Control.Monad.Trans.Maybe.maybeToExceptT + name: "Use 'maybeToExceptT' from Relude" + note: "'maybeToExceptT' is already exported from Relude" + rhs: maybeToExceptT +- warn: + lhs: Control.Monad.Trans.Maybe.exceptToMaybeT + name: "Use 'exceptToMaybeT' from Relude" + note: "'exceptToMaybeT' is already exported from Relude" + rhs: exceptToMaybeT +- warn: + lhs: Control.Monad.MonadPlus + name: "Use 'MonadPlus' from Relude" + note: "'MonadPlus' is already exported from Relude" + rhs: MonadPlus +- warn: + lhs: Control.Monad.mzero + name: "Use 'mzero' from Relude" + note: "'mzero' is already exported from Relude" + rhs: mzero +- warn: + lhs: Control.Monad.mplus + name: "Use 'mplus' from Relude" + note: "'mplus' is already exported from Relude" + rhs: mplus +- warn: + lhs: Control.Monad.filterM + name: "Use 'filterM' from Relude" + note: "'filterM' is already exported from Relude" + rhs: filterM +- warn: + lhs: Control.Monad.forever + name: "Use 'forever' from Relude" + note: "'forever' is already exported from Relude" + rhs: forever +- warn: + lhs: Control.Monad.join + name: "Use 'join' from Relude" + note: "'join' is already exported from Relude" + rhs: join +- warn: + lhs: Control.Monad.mapAndUnzipM + name: "Use 'mapAndUnzipM' from Relude" + note: "'mapAndUnzipM' is already exported from Relude" + rhs: mapAndUnzipM +- warn: + lhs: Control.Monad.mfilter + name: "Use 'mfilter' from Relude" + note: "'mfilter' is already exported from Relude" + rhs: mfilter +- warn: + lhs: Control.Monad.replicateM + name: "Use 'replicateM' from Relude" + note: "'replicateM' is already exported from Relude" + rhs: replicateM +- warn: + lhs: Control.Monad.replicateM_ + name: "Use 'replicateM_' from Relude" + note: "'replicateM_' is already exported from Relude" + rhs: replicateM_ +- warn: + lhs: Control.Monad.zipWithM + name: "Use 'zipWithM' from Relude" + note: "'zipWithM' is already exported from Relude" + rhs: zipWithM +- warn: + lhs: Control.Monad.zipWithM_ + name: "Use 'zipWithM_' from Relude" + note: "'zipWithM_' is already exported from Relude" + rhs: zipWithM_ +- warn: + lhs: "(Control.Monad.<$!>)" + name: "Use '<$!>' from Relude" + note: "Operator '(<$!>)' is already exported from Relude" + rhs: "(<$!>)" +- warn: + lhs: "(Control.Monad.<=<)" + name: "Use '<=<' from Relude" + note: "Operator '(<=<)' is already exported from Relude" + rhs: "(<=<)" +- warn: + lhs: "(Control.Monad.=<<)" + name: "Use '=<<' from Relude" + note: "Operator '(=<<)' is already exported from Relude" + rhs: "(=<<)" +- warn: + lhs: "(Control.Monad.>=>)" + name: "Use '>=>' from Relude" + note: "Operator '(>=>)' is already exported from Relude" + rhs: "(>=>)" +- warn: + lhs: Control.Monad.Fail.MonadFail + name: "Use 'MonadFail' from Relude" + note: "'MonadFail' is already exported from Relude" + rhs: MonadFail +- warn: + lhs: Data.Maybe.catMaybes + name: "Use 'catMaybes' from Relude" + note: "'catMaybes' is already exported from Relude" + rhs: catMaybes +- warn: + lhs: Data.Maybe.fromMaybe + name: "Use 'fromMaybe' from Relude" + note: "'fromMaybe' is already exported from Relude" + rhs: fromMaybe +- warn: + lhs: Data.Maybe.isJust + name: "Use 'isJust' from Relude" + note: "'isJust' is already exported from Relude" + rhs: isJust +- warn: + lhs: Data.Maybe.isNothing + name: "Use 'isNothing' from Relude" + note: "'isNothing' is already exported from Relude" + rhs: isNothing +- warn: + lhs: Data.Maybe.listToMaybe + name: "Use 'listToMaybe' from Relude" + note: "'listToMaybe' is already exported from Relude" + rhs: listToMaybe +- warn: + lhs: Data.Maybe.mapMaybe + name: "Use 'mapMaybe' from Relude" + note: "'mapMaybe' is already exported from Relude" + rhs: mapMaybe +- warn: + lhs: Data.Maybe.maybeToList + name: "Use 'maybeToList' from Relude" + note: "'maybeToList' is already exported from Relude" + rhs: maybeToList +- warn: + lhs: Data.Either.isLeft + name: "Use 'isLeft' from Relude" + note: "'isLeft' is already exported from Relude" + rhs: isLeft +- warn: + lhs: Data.Either.isRight + name: "Use 'isRight' from Relude" + note: "'isRight' is already exported from Relude" + rhs: isRight +- warn: + lhs: Data.Either.lefts + name: "Use 'lefts' from Relude" + note: "'lefts' is already exported from Relude" + rhs: lefts +- warn: + lhs: Data.Either.partitionEithers + name: "Use 'partitionEithers' from Relude" + note: "'partitionEithers' is already exported from Relude" + rhs: partitionEithers +- warn: + lhs: Data.Either.rights + name: "Use 'rights' from Relude" + note: "'rights' is already exported from Relude" + rhs: rights +- warn: + lhs: Data.Monoid.All + name: "Use 'All' from Relude" + note: "'All' is already exported from Relude" + rhs: All +- warn: + lhs: Data.Monoid.getAll + name: "Use 'getAll' from Relude" + note: "'getAll' is already exported from Relude" + rhs: getAll +- warn: + lhs: Data.Monoid.Alt + name: "Use 'Alt' from Relude" + note: "'Alt' is already exported from Relude" + rhs: Alt +- warn: + lhs: Data.Monoid.getAlt + name: "Use 'getAlt' from Relude" + note: "'getAlt' is already exported from Relude" + rhs: getAlt +- warn: + lhs: Data.Monoid.Any + name: "Use 'Any' from Relude" + note: "'Any' is already exported from Relude" + rhs: Any +- warn: + lhs: Data.Monoid.getAny + name: "Use 'getAny' from Relude" + note: "'getAny' is already exported from Relude" + rhs: getAny +- warn: + lhs: Data.Monoid.Ap + name: "Use 'Ap' from Relude" + note: "'Ap' is already exported from Relude" + rhs: Ap +- warn: + lhs: Data.Monoid.getAp + name: "Use 'getAp' from Relude" + note: "'getAp' is already exported from Relude" + rhs: getAp +- warn: + lhs: Data.Monoid.Dual + name: "Use 'Dual' from Relude" + note: "'Dual' is already exported from Relude" + rhs: Dual +- warn: + lhs: Data.Monoid.getDual + name: "Use 'getDual' from Relude" + note: "'getDual' is already exported from Relude" + rhs: getDual +- warn: + lhs: Data.Monoid.Endo + name: "Use 'Endo' from Relude" + note: "'Endo' is already exported from Relude" + rhs: Endo +- warn: + lhs: Data.Monoid.appEndo + name: "Use 'appEndo' from Relude" + note: "'appEndo' is already exported from Relude" + rhs: appEndo +- warn: + lhs: Data.Monoid.First + name: "Use 'First' from Relude" + note: "'First' is already exported from Relude" + rhs: First +- warn: + lhs: Data.Monoid.getFirst + name: "Use 'getFirst' from Relude" + note: "'getFirst' is already exported from Relude" + rhs: getFirst +- warn: + lhs: Data.Monoid.Last + name: "Use 'Last' from Relude" + note: "'Last' is already exported from Relude" + rhs: Last +- warn: + lhs: Data.Monoid.getLast + name: "Use 'getLast' from Relude" + note: "'getLast' is already exported from Relude" + rhs: getLast +- warn: + lhs: Data.Monoid.Product + name: "Use 'Product' from Relude" + note: "'Product' is already exported from Relude" + rhs: Product +- warn: + lhs: Data.Monoid.getProduct + name: "Use 'getProduct' from Relude" + note: "'getProduct' is already exported from Relude" + rhs: getProduct +- warn: + lhs: Data.Monoid.Sum + name: "Use 'Sum' from Relude" + note: "'Sum' is already exported from Relude" + rhs: Sum +- warn: + lhs: Data.Monoid.getSum + name: "Use 'getSum' from Relude" + note: "'getSum' is already exported from Relude" + rhs: getSum +- warn: + lhs: Data.Semigroup.Option + name: "Use 'Option' from Relude" + note: "'Option' is already exported from Relude" + rhs: Option +- warn: + lhs: Data.Semigroup.getOption + name: "Use 'getOption' from Relude" + note: "'getOption' is already exported from Relude" + rhs: getOption +- warn: + lhs: Data.Semigroup.Semigroup + name: "Use 'Semigroup' from Relude" + note: "'Semigroup' is already exported from Relude" + rhs: Semigroup +- warn: + lhs: Data.Semigroup.sconcat + name: "Use 'sconcat' from Relude" + note: "'sconcat' is already exported from Relude" + rhs: sconcat +- warn: + lhs: Data.Semigroup.stimes + name: "Use 'stimes' from Relude" + note: "'stimes' is already exported from Relude" + rhs: stimes +- warn: + lhs: "(Data.Semigroup.<>)" + name: "Use '<>' from Relude" + note: "Operator '(<>)' is already exported from Relude" + rhs: "(<>)" +- warn: + lhs: Data.Semigroup.WrappedMonoid + name: "Use 'WrappedMonoid' from Relude" + note: "'WrappedMonoid' is already exported from Relude" + rhs: WrappedMonoid +- warn: + lhs: Data.Semigroup.cycle1 + name: "Use 'cycle1' from Relude" + note: "'cycle1' is already exported from Relude" + rhs: cycle1 +- warn: + lhs: Data.Semigroup.mtimesDefault + name: "Use 'mtimesDefault' from Relude" + note: "'mtimesDefault' is already exported from Relude" + rhs: mtimesDefault +- warn: + lhs: Data.Semigroup.stimesIdempotent + name: "Use 'stimesIdempotent' from Relude" + note: "'stimesIdempotent' is already exported from Relude" + rhs: stimesIdempotent +- warn: + lhs: Data.Semigroup.stimesIdempotentMonoid + name: "Use 'stimesIdempotentMonoid' from Relude" + note: "'stimesIdempotentMonoid' is already exported from Relude" + rhs: stimesIdempotentMonoid +- warn: + lhs: Data.Semigroup.stimesMonoid + name: "Use 'stimesMonoid' from Relude" + note: "'stimesMonoid' is already exported from Relude" + rhs: stimesMonoid +- warn: + lhs: Data.ByteString.ByteString + name: "Use 'ByteString' from Relude" + note: "'ByteString' is already exported from Relude" + rhs: ByteString +- warn: + lhs: Data.ByteString.Short.ShortByteString + name: "Use 'ShortByteString' from Relude" + note: "'ShortByteString' is already exported from Relude" + rhs: ShortByteString +- warn: + lhs: Data.ByteString.Short.toShort + name: "Use 'toShort' from Relude" + note: "'toShort' is already exported from Relude" + rhs: toShort +- warn: + lhs: Data.ByteString.Short.fromShort + name: "Use 'fromShort' from Relude" + note: "'fromShort' is already exported from Relude" + rhs: fromShort +- warn: + lhs: Data.String.IsString + name: "Use 'IsString' from Relude" + note: "'IsString' is already exported from Relude" + rhs: IsString +- warn: + lhs: Data.String.fromString + name: "Use 'fromString' from Relude" + note: "'fromString' is already exported from Relude" + rhs: fromString +- warn: + lhs: Data.Text.Text + name: "Use 'Text' from Relude" + note: "'Text' is already exported from Relude" + rhs: Text +- warn: + lhs: Data.Text.lines + name: "Use 'lines' from Relude" + note: "'lines' is already exported from Relude" + rhs: lines +- warn: + lhs: Data.Text.unlines + name: "Use 'unlines' from Relude" + note: "'unlines' is already exported from Relude" + rhs: unlines +- warn: + lhs: Data.Text.words + name: "Use 'words' from Relude" + note: "'words' is already exported from Relude" + rhs: words +- warn: + lhs: Data.Text.unwords + name: "Use 'unwords' from Relude" + note: "'unwords' is already exported from Relude" + rhs: unwords +- warn: + lhs: "Data.Text.Encoding.decodeUtf8'" + name: "Use 'decodeUtf8'' from Relude" + note: "'decodeUtf8'' is already exported from Relude" + rhs: "decodeUtf8'" +- warn: + lhs: Data.Text.Encoding.decodeUtf8With + name: "Use 'decodeUtf8With' from Relude" + note: "'decodeUtf8With' is already exported from Relude" + rhs: decodeUtf8With +- warn: + lhs: Data.Text.Encoding.Error.OnDecodeError + name: "Use 'OnDecodeError' from Relude" + note: "'OnDecodeError' is already exported from Relude" + rhs: OnDecodeError +- warn: + lhs: Data.Text.Encoding.Error.OnError + name: "Use 'OnError' from Relude" + note: "'OnError' is already exported from Relude" + rhs: OnError +- warn: + lhs: Data.Text.Encoding.Error.UnicodeException + name: "Use 'UnicodeException' from Relude" + note: "'UnicodeException' is already exported from Relude" + rhs: UnicodeException +- warn: + lhs: Data.Text.Encoding.Error.lenientDecode + name: "Use 'lenientDecode' from Relude" + note: "'lenientDecode' is already exported from Relude" + rhs: lenientDecode +- warn: + lhs: Data.Text.Encoding.Error.strictDecode + name: "Use 'strictDecode' from Relude" + note: "'strictDecode' is already exported from Relude" + rhs: strictDecode +- warn: + lhs: Text.Read.Read + name: "Use 'Read' from Relude" + note: "'Read' is already exported from Relude" + rhs: Read +- warn: + lhs: Text.Read.readMaybe + name: "Use 'readMaybe' from Relude" + note: "'readMaybe' is already exported from Relude" + rhs: readMaybe +- warn: + lhs: "(liftIO (newEmptyMVar ))" + name: "'liftIO' is not needed" + note: "If you import 'newEmptyMVar' from Relude, it's already lifted" + rhs: newEmptyMVar +- warn: + lhs: "(liftIO (newMVar x))" + name: "'liftIO' is not needed" + note: "If you import 'newMVar' from Relude, it's already lifted" + rhs: newMVar +- warn: + lhs: "(liftIO (putMVar x y))" + name: "'liftIO' is not needed" + note: "If you import 'putMVar' from Relude, it's already lifted" + rhs: putMVar +- warn: + lhs: "(liftIO (readMVar x))" + name: "'liftIO' is not needed" + note: "If you import 'readMVar' from Relude, it's already lifted" + rhs: readMVar +- warn: + lhs: "(liftIO (swapMVar x y))" + name: "'liftIO' is not needed" + note: "If you import 'swapMVar' from Relude, it's already lifted" + rhs: swapMVar +- warn: + lhs: "(liftIO (takeMVar x))" + name: "'liftIO' is not needed" + note: "If you import 'takeMVar' from Relude, it's already lifted" + rhs: takeMVar +- warn: + lhs: "(liftIO (tryPutMVar x y))" + name: "'liftIO' is not needed" + note: "If you import 'tryPutMVar' from Relude, it's already lifted" + rhs: tryPutMVar +- warn: + lhs: "(liftIO (tryReadMVar x))" + name: "'liftIO' is not needed" + note: "If you import 'tryReadMVar' from Relude, it's already lifted" + rhs: tryReadMVar +- warn: + lhs: "(liftIO (tryTakeMVar x))" + name: "'liftIO' is not needed" + note: "If you import 'tryTakeMVar' from Relude, it's already lifted" + rhs: tryTakeMVar +- warn: + lhs: "(liftIO (atomically x))" + name: "'liftIO' is not needed" + note: "If you import 'atomically' from Relude, it's already lifted" + rhs: atomically +- warn: + lhs: "(liftIO (newTVarIO x))" + name: "'liftIO' is not needed" + note: "If you import 'newTVarIO' from Relude, it's already lifted" + rhs: newTVarIO +- warn: + lhs: "(liftIO (readTVarIO x))" + name: "'liftIO' is not needed" + note: "If you import 'readTVarIO' from Relude, it's already lifted" + rhs: readTVarIO +- warn: + lhs: "(liftIO (newTMVarIO x))" + name: "'liftIO' is not needed" + note: "If you import 'newTMVarIO' from Relude, it's already lifted" + rhs: newTMVarIO +- warn: + lhs: "(liftIO (newEmptyTMVarIO ))" + name: "'liftIO' is not needed" + note: "If you import 'newEmptyTMVarIO' from Relude, it's already lifted" + rhs: newEmptyTMVarIO +- warn: + lhs: "(liftIO (exitWith x))" + name: "'liftIO' is not needed" + note: "If you import 'exitWith' from Relude, it's already lifted" + rhs: exitWith +- warn: + lhs: "(liftIO (exitFailure ))" + name: "'liftIO' is not needed" + note: "If you import 'exitFailure' from Relude, it's already lifted" + rhs: exitFailure +- warn: + lhs: "(liftIO (exitSuccess ))" + name: "'liftIO' is not needed" + note: "If you import 'exitSuccess' from Relude, it's already lifted" + rhs: exitSuccess +- warn: + lhs: "(liftIO (die x))" + name: "'liftIO' is not needed" + note: "If you import 'die' from Relude, it's already lifted" + rhs: die +- warn: + lhs: "(liftIO (readFile x))" + name: "'liftIO' is not needed" + note: "If you import 'readFile' from Relude, it's already lifted" + rhs: readFile +- warn: + lhs: "(liftIO (writeFile x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeFile' from Relude, it's already lifted" + rhs: writeFile +- warn: + lhs: "(liftIO (appendFile x y))" + name: "'liftIO' is not needed" + note: "If you import 'appendFile' from Relude, it's already lifted" + rhs: appendFile +- warn: + lhs: "(liftIO (readFileText x))" + name: "'liftIO' is not needed" + note: "If you import 'readFileText' from Relude, it's already lifted" + rhs: readFileText +- warn: + lhs: "(liftIO (writeFileText x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeFileText' from Relude, it's already lifted" + rhs: writeFileText +- warn: + lhs: "(liftIO (appendFileText x y))" + name: "'liftIO' is not needed" + note: "If you import 'appendFileText' from Relude, it's already lifted" + rhs: appendFileText +- warn: + lhs: "(liftIO (readFileLText x))" + name: "'liftIO' is not needed" + note: "If you import 'readFileLText' from Relude, it's already lifted" + rhs: readFileLText +- warn: + lhs: "(liftIO (writeFileLText x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeFileLText' from Relude, it's already lifted" + rhs: writeFileLText +- warn: + lhs: "(liftIO (appendFileLText x y))" + name: "'liftIO' is not needed" + note: "If you import 'appendFileLText' from Relude, it's already lifted" + rhs: appendFileLText +- warn: + lhs: "(liftIO (readFileBS x))" + name: "'liftIO' is not needed" + note: "If you import 'readFileBS' from Relude, it's already lifted" + rhs: readFileBS +- warn: + lhs: "(liftIO (writeFileBS x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeFileBS' from Relude, it's already lifted" + rhs: writeFileBS +- warn: + lhs: "(liftIO (appendFileBS x y))" + name: "'liftIO' is not needed" + note: "If you import 'appendFileBS' from Relude, it's already lifted" + rhs: appendFileBS +- warn: + lhs: "(liftIO (readFileLBS x))" + name: "'liftIO' is not needed" + note: "If you import 'readFileLBS' from Relude, it's already lifted" + rhs: readFileLBS +- warn: + lhs: "(liftIO (writeFileLBS x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeFileLBS' from Relude, it's already lifted" + rhs: writeFileLBS +- warn: + lhs: "(liftIO (appendFileLBS x y))" + name: "'liftIO' is not needed" + note: "If you import 'appendFileLBS' from Relude, it's already lifted" + rhs: appendFileLBS +- warn: + lhs: "(liftIO (newIORef x))" + name: "'liftIO' is not needed" + note: "If you import 'newIORef' from Relude, it's already lifted" + rhs: newIORef +- warn: + lhs: "(liftIO (readIORef x))" + name: "'liftIO' is not needed" + note: "If you import 'readIORef' from Relude, it's already lifted" + rhs: readIORef +- warn: + lhs: "(liftIO (writeIORef x y))" + name: "'liftIO' is not needed" + note: "If you import 'writeIORef' from Relude, it's already lifted" + rhs: writeIORef +- warn: + lhs: "(liftIO (modifyIORef x y))" + name: "'liftIO' is not needed" + note: "If you import 'modifyIORef' from Relude, it's already lifted" + rhs: modifyIORef +- warn: + lhs: "(liftIO (modifyIORef' x y))" + name: "'liftIO' is not needed" + note: "If you import 'modifyIORef'' from Relude, it's already lifted" + rhs: "modifyIORef'" +- warn: + lhs: "(liftIO (atomicModifyIORef x y))" + name: "'liftIO' is not needed" + note: "If you import 'atomicModifyIORef' from Relude, it's already lifted" + rhs: atomicModifyIORef +- warn: + lhs: "(liftIO (atomicModifyIORef' x y))" + name: "'liftIO' is not needed" + note: "If you import 'atomicModifyIORef'' from Relude, it's already lifted" + rhs: "atomicModifyIORef'" +- warn: + lhs: "(liftIO (atomicWriteIORef x y))" + name: "'liftIO' is not needed" + note: "If you import 'atomicWriteIORef' from Relude, it's already lifted" + rhs: atomicWriteIORef +- warn: + lhs: "(liftIO (getLine ))" + name: "'liftIO' is not needed" + note: "If you import 'getLine' from Relude, it's already lifted" + rhs: getLine +- warn: + lhs: "(liftIO (print x))" + name: "'liftIO' is not needed" + note: "If you import 'print' from Relude, it's already lifted" + rhs: print +- warn: + lhs: "(liftIO (putStr x))" + name: "'liftIO' is not needed" + note: "If you import 'putStr' from Relude, it's already lifted" + rhs: putStr +- warn: + lhs: "(liftIO (putStrLn x))" + name: "'liftIO' is not needed" + note: "If you import 'putStrLn' from Relude, it's already lifted" + rhs: putStrLn +- warn: + lhs: "(liftIO (putText x))" + name: "'liftIO' is not needed" + note: "If you import 'putText' from Relude, it's already lifted" + rhs: putText +- warn: + lhs: "(liftIO (putTextLn x))" + name: "'liftIO' is not needed" + note: "If you import 'putTextLn' from Relude, it's already lifted" + rhs: putTextLn +- warn: + lhs: "(liftIO (putLText x))" + name: "'liftIO' is not needed" + note: "If you import 'putLText' from Relude, it's already lifted" + rhs: putLText +- warn: + lhs: "(liftIO (putLTextLn x))" + name: "'liftIO' is not needed" + note: "If you import 'putLTextLn' from Relude, it's already lifted" + rhs: putLTextLn +- warn: + lhs: "(liftIO (putBS x))" + name: "'liftIO' is not needed" + note: "If you import 'putBS' from Relude, it's already lifted" + rhs: putBS +- warn: + lhs: "(liftIO (putBSLn x))" + name: "'liftIO' is not needed" + note: "If you import 'putBSLn' from Relude, it's already lifted" + rhs: putBSLn +- warn: + lhs: "(liftIO (putLBS x))" + name: "'liftIO' is not needed" + note: "If you import 'putLBS' from Relude, it's already lifted" + rhs: putLBS +- warn: + lhs: "(liftIO (putLBSLn x))" + name: "'liftIO' is not needed" + note: "If you import 'putLBSLn' from Relude, it's already lifted" + rhs: putLBSLn +- warn: + lhs: "(liftIO (hFlush x))" + name: "'liftIO' is not needed" + note: "If you import 'hFlush' from Relude, it's already lifted" + rhs: hFlush +- warn: + lhs: "(liftIO (hIsEOF x))" + name: "'liftIO' is not needed" + note: "If you import 'hIsEOF' from Relude, it's already lifted" + rhs: hIsEOF +- warn: + lhs: "(liftIO (hSetBuffering x y))" + name: "'liftIO' is not needed" + note: "If you import 'hSetBuffering' from Relude, it's already lifted" + rhs: hSetBuffering +- warn: + lhs: "(liftIO (hGetBuffering x))" + name: "'liftIO' is not needed" + note: "If you import 'hGetBuffering' from Relude, it's already lifted" + rhs: hGetBuffering +- warn: + lhs: "(liftIO (getArgs ))" + name: "'liftIO' is not needed" + note: "If you import 'getArgs' from Relude, it's already lifted" + rhs: getArgs +- warn: + lhs: "(liftIO (lookupEnv x))" + name: "'liftIO' is not needed" + note: "If you import 'lookupEnv' from Relude, it's already lifted" + rhs: lookupEnv +- hint: + lhs: "fmap (bimap f g)" + note: "Use `bimapF` from `Relude.Extra.Bifunctor`" + rhs: bimapF f g +- hint: + lhs: "bimap f g <$> x" + note: "Use `bimapF` from `Relude.Extra.Bifunctor`" + rhs: bimapF f g x +- hint: + lhs: "fmap (first f)" + note: "Use `firstF` from `Relude.Extra.Bifunctor`" + rhs: firstF f +- hint: + lhs: fmap . first + note: "Use `firstF` from `Relude.Extra.Bifunctor`" + rhs: firstF +- hint: + lhs: "fmap (second f)" + note: "Use `secondF` from `Relude.Extra.Bifunctor`" + rhs: secondF f +- hint: + lhs: fmap . second + note: "Use `secondF` from `Relude.Extra.Bifunctor`" + rhs: secondF +- hint: + lhs: "[minBound .. maxBound]" + note: "Use `universe` from `Relude.Extra.Enum`" + rhs: universe +- hint: + lhs: succ + note: "`succ` from `Prelude` is a pure function but it may throw exception. Consider using `next` from `Relude.Extra.Enum` instead." + rhs: next +- hint: + lhs: pred + note: "`pred` from `Prelude` is a pure function but it may throw exception. Consider using `prev` from `Relude.Extra.Enum` instead." + rhs: prev +- hint: + lhs: toEnum + note: "`toEnum` from `Prelude` is a pure function but it may throw exception. Consider using `safeToEnum` from `Relude.Extra.Enum` instead." + rhs: safeToEnum +- hint: + lhs: "\\a -> (a, a)" + note: "Use `dup` from `Relude.Extra.Tuple`" + rhs: dup +- hint: + lhs: "\\a -> (f a, a)" + note: "Use `toFst` from `Relude.Extra.Tuple`" + rhs: toFst f +- hint: + lhs: "\\a -> (a, f a)" + note: "Use `toSnd` from `Relude.Extra.Tuple`" + rhs: toSnd f +- hint: + lhs: fmap . toFst + note: "Use `fmapToFst` from `Relude.Extra.Tuple`" + rhs: fmapToFst +- hint: + lhs: "fmap (toFst f)" + note: "Use `fmapToFst` from `Relude.Extra.Tuple`" + rhs: fmapToFst f +- hint: + lhs: fmap . toSnd + note: "Use `fmapToSnd` from `Relude.Extra.Tuple`" + rhs: fmapToSnd +- hint: + lhs: "fmap (toSnd f)" + note: "Use `fmapToSnd` from `Relude.Extra.Tuple`" + rhs: fmapToSnd f +- hint: + lhs: map . toFst + note: "Use `fmapToFst` from `Relude.Extra.Tuple`" + rhs: fmapToFst +- hint: + lhs: "map (toFst f)" + note: "Use `fmapToFst` from `Relude.Extra.Tuple`" + rhs: fmapToFst f +- hint: + lhs: map . toSnd + note: "Use `fmapToSnd` from `Relude.Extra.Tuple`" + rhs: fmapToSnd +- hint: + lhs: "map (toSnd f)" + note: "Use `fmapToSnd` from `Relude.Extra.Tuple`" + rhs: fmapToSnd f +- hint: + lhs: "fmap (,a) (f a)" + note: "Use `traverseToFst` from `Relude.Extra.Tuple`" + rhs: traverseToFst f a +- hint: + lhs: "fmap (flip (,) a) (f a)" + note: "Use `traverseToFst` from `Relude.Extra.Tuple`" + rhs: traverseToFst f a +- hint: + lhs: "(,a) <$> f a" + note: "Use `traverseToFst` from `Relude.Extra.Tuple`" + rhs: traverseToFst f a +- hint: + lhs: "flip (,) a <$> f a" + note: "Use `traverseToFst` from `Relude.Extra.Tuple`" + rhs: traverseToFst f a +- hint: + lhs: "fmap (a,) (f a)" + note: "Use `traverseToSnd` from `Relude.Extra.Tuple`" + rhs: traverseToSnd f a +- hint: + lhs: "fmap ((,) a) (f a)" + note: "Use `traverseToSnd` from `Relude.Extra.Tuple`" + rhs: traverseToSnd f a +- hint: + lhs: "(a,) <$> f a" + note: "Use `traverseToSnd` from `Relude.Extra.Tuple`" + rhs: traverseToSnd f a +- hint: + lhs: "(,) a <$> f a" + note: "Use `traverseToSnd` from `Relude.Extra.Tuple`" + rhs: traverseToSnd f a diff --git a/.travis.yml b/.travis.yml index 6af01ba..48fa201 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,72 +1,32 @@ -# Use new container infrastructure to enable caching -sudo: false +sudo: true +language: haskell +git: + depth: 5 -# Do not choose a language; we provide our own build tools. -language: generic +cabal: "2.4" - -# Caching so the next build will be fast too. cache: directories: - - $HOME/.stack - - $HOME/.stack-work - - -# Ensure necessary system libraries are present -addons: - apt: - sources: - - google-chrome - packages: - - google-chrome-stable - - libgmp-dev - - unzip - - xvfb - - -before_install: -- mkdir -p ~/.local/bin -- export PATH=$HOME/.local/bin:$PATH -- -- # install stack -- travis_retry curl -L https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' -- -- # install geckodriver -- travis_retry curl -L https://github.com/mozilla/geckodriver/releases/download/v0.29.0/geckodriver-v0.29.0-linux64.tar.gz | tar xz -C ~/.local/bin -- -- # install chromedriver -- wget -O /tmp/chromedriver.zip https://chromedriver.storage.googleapis.com/89.0.4389.23/chromedriver_linux64.zip -- unzip /tmp/chromedriver.zip chromedriver -d ~/.local/bin -- -- # install firefox -- wget -L -O firefox.tar.bz2 'https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64&lang=en-US' -- tar xvf firefox.tar.bz2 -C ~/.local/bin -- export PATH=$HOME/.local/bin/firefox:$PATH -- -- # start remote ends -- export MOZ_HEADLESS=1 -- export WD_STRESS_TEST_NUM_TESTS="50" -- xvfb-run -a geckodriver --binary $HOME/.local/bin/firefox/firefox --marionette-port 2828 --port 4444 --log error >/dev/null 2>/dev/null & -- xvfb-run -a geckodriver --binary $HOME/.local/bin/firefox/firefox --marionette-port 2829 --port 4445 --log error >/dev/null 2>/dev/null & -- xvfb-run -a geckodriver --binary $HOME/.local/bin/firefox/firefox --marionette-port 2830 --port 4446 --log error >/dev/null 2>/dev/null & -- xvfb-run -a geckodriver --binary $HOME/.local/bin/firefox/firefox --marionette-port 2831 --port 4447 --log error >/dev/null 2>/dev/null & -- xvfb-run -a geckodriver --binary $HOME/.local/bin/firefox/firefox --marionette-port 2832 --port 4448 --log error >/dev/null 2>/dev/null & -- chromedriver --port=9515 & -- chromedriver --port=9516 & -- sleep 2 + - "$HOME/.cabal/store" + +matrix: + include: + - ghc: 8.10.7 + + install: -- # stack --no-terminal --install-ghc test --only-dependencies - - -before_script: -- # root needs permission to run chrome-sandbox; else chromedriver will error out -- sudo chown root /opt/google/chrome/chrome-sandbox -- sudo chmod 4755 /opt/google/chrome/chrome-sandbox + # HLint check + - curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint . + - cabal v2-update + - cabal v2-configure --enable-tests --enable-benchmarks --test-show-details=direct + - cabal v2-build --enable-tests --enable-benchmarks script: -- stack --no-terminal test --haddock --no-haddock-deps -- stack --no-terminal install webdriver-w3c:wd-parallel-stress-test && stack --no-terminal exec -- wd-parallel-stress-test --wd-remote-ends 'geckodriver https://localhost:4444 https://localhost:4445 https://localhost:4446 https://localhost:4447 https://localhost:4448' --num-threads 5 + - cabal v2-test --enable-tests + +notifications: + email: false diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..a30d20e --- /dev/null +++ b/cabal.project @@ -0,0 +1,2 @@ +packages : . +index-state : 2021-11-05T00:00:00Z diff --git a/cabal.project.local b/cabal.project.local new file mode 100644 index 0000000..e69de29 diff --git a/ci/common/common.sh b/ci/common/common.sh new file mode 100644 index 0000000..9e659df --- /dev/null +++ b/ci/common/common.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +guard_bash_error () { + set -Eeuo pipefail +} + +# Log levels +INFO=0 +WARN=1 +ERROR=2 +FATAL=3 +DEBUG=4 +DEFAULT_LOG_LEVEL=${ERROR} + +my_exit () { + echo "EXIT: - [HOST:$(hostname)]: - $(date +"%Y-%m-%d %H:%M:%S") - $1" + exit "$2" +} + +msg () { + if [ $1 -le ${DEFAULT_LOG_LEVEL} ]; then + echo "[HOST:$(hostname)]: - $(date +"%Y-%m-%d %H:%M:%S") - $2" + fi +} + +info () { + msg ${INFO} "INFO: - $1" +} + +warn () { + msg ${WARN} "WARNING: - $1" +} + +error () { + msg ${ERROR} "ERROR: - $1" +} + +fatal () { + msg ${FATAL} "FATAL: - $1" +} + +debug () { + msg ${DEBUG} "DEBUG: - $1" +} + +begin_banner () { + info "$1 - $2 phase - begin" +} + +done_banner () { + info "$1 - $2 phase - done" +} + +### turn path within script into absolute path +### must pass the calling string of the script as the first parameter +### e.g., ./path_to_script/script.sh +### or, /root/path_to_script/script.sh +### return the absolute path to the script with "echo" command +turn_to_absolute_path () { + local SCRIPT_ABS_PATH_RAW="$(dirname "$1")" + # turn SCRIPT_ABS_PATH into absolute path + case ${SCRIPT_ABS_PATH_RAW} in + /*) echo "${SCRIPT_ABS_PATH_RAW}" ;; + \.\.*) echo "$PWD/${SCRIPT_ABS_PATH_RAW}" ;; + \.*) echo "$PWD/${SCRIPT_ABS_PATH_RAW}" ;; + *) echo "$PWD" ;; + esac +} + +### change CD to up to the project root directory +### must pass the absolute path to the script as the first parameter +change_CD_to_project_root () { + cd "$1" + local up_level=.. + local my_loop=10 # guard not to loop forever + until ls "${up_level}"|grep -w "cook.sh" > /dev/null 2>&1 && [ ${my_loop} -gt 0 ] + do + up_level=${up_level}/.. + my_loop=$(expr ${my_loop} - 1) + done + if [ ${my_loop} -eq 0 ]; then + my_exit "Too many level up within the searching for DevOps directory,abort." 1 + fi + cd "$1/${up_level}" +} + +### check OS and distribution +### return the OS distribution and ID with "echo" command +check_dist_or_OS () { + local MY_THE_DISTRIBUTION_ID="" + local MY_THE_DISTRIBUTION_VERSION="" + if [ -e /etc/os-release ]; then + MY_THE_DISTRIBUTION_ID=$(grep -w "ID" /etc/os-release |awk -F"=" '{print $NF}'|sed 's/"//g') + if [ "${MY_THE_DISTRIBUTION_ID}" == "ubuntu" ]; then + MY_THE_DISTRIBUTION_VERSION=$(grep -w "VERSION_ID" /etc/os-release |awk -F"=" '{print $NF}'|sed 's/"//g') + else + MY_THE_DISTRIBUTION_VERSION=$(grep -w "VERSION_ID" /etc/os-release |awk -F"=" '{print $NF}'|awk -F"." '{print $1}'|sed 's/"//g') + fi + echo "${MY_THE_DISTRIBUTION_ID} ${MY_THE_DISTRIBUTION_VERSION}" + else if type uname > /dev/null 2>&1; then + MY_THE_DISTRIBUTION_ID=$(uname -s) + MY_THE_DISTRIBUTION_VERSION=$(uname -r) + echo "${MY_THE_DISTRIBUTION_ID} ${MY_THE_DISTRIBUTION_VERSION}" + else + echo "" + fi + fi +} + +### guard that the caller of the script must be root or has sudo right +guard_root_or_sudo () { + if [[ $EUID > 0 ]] && ! sudo -v >/dev/null 2>&1; then + return 1 + else + return 0 + fi +} + +### init script with check if root or sudo +init_with_root_or_sudo () { + guard_bash_error + + if ! guard_root_or_sudo; then + my_exit "You must be root or you must be sudoer to prepare the env for CI/CD." 1 + fi + + SCRIPT_ABS_PATH=$(turn_to_absolute_path $0) + + # change_CD_to_project_root ${SCRIPT_ABS_PATH} + + THE_DISTRIBUTION_ID_VERSION=$(check_dist_or_OS) + THE_DISTRIBUTION_ID=$(echo ${THE_DISTRIBUTION_ID_VERSION}|awk '{print $1}') + THE_DISTRIBUTION_VERSION=$(echo ${THE_DISTRIBUTION_ID_VERSION}|awk '{print $2}') +} + +### init script without check if root or sudo +init_without_root_or_sudo () { + guard_bash_error + + SCRIPT_ABS_PATH=$(turn_to_absolute_path $0) + + # change_CD_to_project_root ${SCRIPT_ABS_PATH} + + THE_DISTRIBUTION_ID_VERSION=$(check_dist_or_OS) + THE_DISTRIBUTION_ID=$(echo ${THE_DISTRIBUTION_ID_VERSION}|awk '{print $1}') + THE_DISTRIBUTION_VERSION=$(echo ${THE_DISTRIBUTION_ID_VERSION}|awk '{print $2}') +} + +get_last_stable_nix_channel () { + local MY_CHANNEL_NAME_REGEX="" + case ${THE_DISTRIBUTION_ID} in + debian|ubuntu|rhel|centos) MY_CHANNEL_NAME_REGEX='s/.*\(nixos-[0-9][0-9].[0-9][0-9]\).*/\1/p' ;; + Darwin) MY_CHANNEL_NAME_REGEX='s/.*\(nixpkgs-[0-9][0-9].[0-9][0-9]-darwin\).*/\1/p' ;; + *) ;; + esac + local MY_LAST_NIX_STABLE_CHANNEL=$(git ls-remote --heads https://github.com/NixOS/nixpkgs | awk '{print $NF}' | awk -F"/" '{print $NF}' | grep -v "\-unstable" | grep -v "\-small" | sed -n ${MY_CHANNEL_NAME_REGEX} | sort | tail -1) + echo ${MY_LAST_NIX_STABLE_CHANNEL} +} + +switch_to_last_stable_nix_channel () { + nix-channel --remove nixpkgs + nix-channel --add "https://nixos.org/channels/$(get_last_stable_nix_channel)" nixpkgs + nix-channel --update +} + diff --git a/ci/pack-tarball/do.sh b/ci/pack-tarball/do.sh new file mode 100644 index 0000000..0093cf1 --- /dev/null +++ b/ci/pack-tarball/do.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +if ! type dirname > /dev/null 2>&1; then + echo "Not even a linux or macOS, Windoze? We don't support it. Abort." + exit 1 +fi + +. "$(dirname "$0")"/../common/common.sh + +init_with_root_or_sudo "$0" + +SCRIPT_ABS_PATH=$(turn_to_absolute_path $0) + +begin_banner "Top level" "project deploy - packing" + +set +u +[[ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]] && . $HOME/.nix-profile/etc/profile.d/nix.sh +set -u + +if [ -e ${SCRIPT_ABS_PATH}/../../result ]; then + [[ -e ${SCRIPT_ABS_PATH}/../../webdriver_w3c.tar.gz ]] && rm -fr ${CRIPT_ABS_PATH}/../../webdriver_w3c.tar.gz + tar zPcf ${SCRIPT_ABS_PATH}/../../webdriver_w3c.tar.gz $(nix-store --query --requisites ${SCRIPT_ABS_PATH}/../../result) +else + info "No ${SCRIPT_ABS_PATH}/../../result, can't pack tarball" +fi + +done_banner "Top level" "project deploy - packing" diff --git a/ci/pack-tarball/undo.sh b/ci/pack-tarball/undo.sh new file mode 100644 index 0000000..d116640 --- /dev/null +++ b/ci/pack-tarball/undo.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +if ! type dirname > /dev/null 2>&1; then + echo "Not even a linux or macOS, Windoze? We don't support it. Abort." + exit 1 +fi + +. "$(dirname "$0")"/../common/common.sh + +init_with_root_or_sudo "$0" + +begin_banner "Top level" "project deploy - packing" + +set +u +[[ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]] && . $HOME/.nix-profile/etc/profile.d/nix.sh +set -u + +done_banner "Top level" "project deploy - packing" diff --git a/ci/prepare-env/do.sh b/ci/prepare-env/do.sh new file mode 100644 index 0000000..7101543 --- /dev/null +++ b/ci/prepare-env/do.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +if ! type dirname > /dev/null 2>&1; then + echo "Not even a linux or macOS, Windoze? We don't support it. Abort." + exit 1 +fi + +. "$(dirname "$0")"/../common/common.sh + +init_with_root_or_sudo "$0" + +begin_banner "Top level" "project env prepare" + +set +u +[[ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]] && . $HOME/.nix-profile/etc/profile.d/nix.sh +set -u + +if ! type nix-build >/dev/null 2>&1; then + info "no nix-build found, trying to install it" + case ${THE_DISTRIBUTION_ID} in + debian) + [[ -e /proc/sys/kernel/unprivileged_userns_clone ]] && sudo sysctl kernel.unprivileged_userns_clone=1 + curl -L https://nixos.org/nix/install | sh + ;; + ubuntu) + [[ -e /proc/sys/kernel/unprivileged_userns_clone ]] && sudo sysctl kernel.unprivileged_userns_clone=1 + curl -L https://nixos.org/nix/install | sh + ;; + Darwin) + curl -L https://nixos.org/nix/install | sh + ;; + rhel|centos) + curl -L https://nixos.org/releases/nix/nix-2.1.3/install | sh + ;; + *) ;; + esac + set +u + . $HOME/.nix-profile/etc/profile.d/nix.sh + set -u + switch_to_last_stable_nix_channel +fi + +# add iohk binary cache +if ! [ -f ~/.config/nix/nix.conf ] || ! grep "hydra.iohk.io" ~/.config/nix/nix.conf > /dev/null 2>&1 ; then + mkdir -p ~/.config/nix + echo "trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" >> ~/.config/nix/nix.conf + echo "substituters = https://hydra.iohk.io" >> ~/.config/nix/nix.conf +fi + +# in Chian, use the TUNA mirror for Nix binary cache +#if ! [ -f ~/.config/nix/nix.conf ] || ! grep "mirrors.tuna.tsinghua.edu.cn" ~/.config/nix/nix.conf > /dev/null 2>&1 ; then +# mkdir -p ~/.config/nix +# echo "substituters = https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store https://cache.nixos.org/" >> ~/.config/nix/nix.conf +#fi + +#if ! type patchelf >/dev/null 2>&1; then +# info "no patchelf found, trying to install it" +# nix-env --install patchelf +#fi + +#if ! type cabal2nix >/dev/null 2>&1; then +# info "no cabal2nix found, trying to install it" +# +# nix-env --install cabal2nix +#fi + +#if ! type nix-prefetch-git >/dev/null 2>&1; then +# info "no nix-prefetch-git found, trying to install it" +# nix-env --install nix-prefetch-git +#fi + +#if ! type cabal >/dev/null 2>&1; then +# info "no cabal-install found, trying to install it" +# nix-env --install cabal-install +#fi + +#if ! type nodejs >/dev/null 2>&1 && ! type node >/dev/null 2>&1; then +# info "no nodejs found, trying to install it" +# case ${THE_DISTRIBUTION_ID} in +# debian) +# curl -sL https://deb.nodesource.com/setup_10.x | sudo bash - +# sudo apt-get update +# sudo apt-get install -y nodejs +# ;; +# ubuntu) +# curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - +# sudo apt-get update +# sudo apt-get install -y nodejs +# ;; +# Darwin) +# if type brew > /dev/null 2>&1; then +# brew install node@10 +# else +# curl "https://nodejs.org/dist/latest-v10.x/node-${VERSION:-$(wget -qO- https://nodejs.org/dist/latest-v10.x/ | sed -nE 's|.*>node-(.*)\.pkg.*|\1|p')}.pkg" > "$HOME/Downloads/node-latest-v10.x.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest-v10.x.pkg" -target "/" +# fi +# ;; +# rhel|centos) +# curl -sL https://rpm.nodesource.com/setup_10.x | sudo bash - +# sudo yum install -y nodejs +# ;; +# *) ;; +# esac +#fi + +done_banner "Top level" "project env prepare" diff --git a/ci/prepare-env/undo.sh b/ci/prepare-env/undo.sh new file mode 100644 index 0000000..e569f6a --- /dev/null +++ b/ci/prepare-env/undo.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +if ! type dirname > /dev/null 2>&1; then + echo "Not even a linux or macOS, Windoze? We don't support it. Abort." + exit 1 +fi + +. "$(dirname "$0")"/../common/common.sh + +init_with_root_or_sudo "$0" + +begin_banner "Top level" "project env unprepare" + +set +u +[[ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]] && . $HOME/.nix-profile/etc/profile.d/nix.sh +set -u + +if type nix-build >/dev/null 2>&1; then + info "nix-build found, trying to uninstall it" + if [ "${THE_DISTRIBUTION_ID}" == "debian" ] || [ "${THE_DISTRIBUTION_ID}" == "ubuntu" ]; then + [[ -e /proc/sys/kernel/unprivileged_userns_clone ]] && sudo sysctl kernel.unprivileged_userns_clone=1 + fi + set +e + sudo rm -fr /nix > /dev/null 2>&1 + set -e + sudo rm -fr $HOME/.nix-channels + sudo rm -fr $HOME/.nix-defexpr + sudo rm -fr $HOME/.nix-profile + [[ -f $HOME/.config/nix/nix.conf ]] && sudo rm -fr $HOME/.config/nix/nix.conf + [[ -d $HOME/.cache/nix ]] && sudo rm -fr $HOME/.cache/nix + [[ -f $HOME/.profile ]] && sed -i.nix.uninstall.bak '/.nix-profile/d' $HOME/.profile + [[ -f $HOME/.bash_profile ]] && sed -i.nix.uninstall.bak '/.nix-profile/d' $HOME/.bash_profile + [[ -f $HOME/.bashrc ]] && sed -i.nix.uninstall.bak '/.nix-profile/d' $HOME/.bashrc +fi + +#if type nodejs >/dev/null 2>&1 || type node >/dev/null 2>&1; then +# info "nodejs found, trying to uninstall it" +# case ${THE_DISTRIBUTION_ID} in +# debian|ubuntu) +# sudo apt-get purge -y nodejs +# ;; +# Darwin) +# if type brew > /dev/null 2>&1; then +# brew uninstall node@10 +# brew unlink node@10 +# else +# info "Use your macOS finder to uninstall the nodejs package." +# fi +# ;; +# rhel|centos) +# sudo yum erase -y nodejs +# ;; +# *) ;; +# esac +#fi + +info "Now, you should logout and log back in to let the uninstall take effect." + +done_banner "Top level" "project env unprepare" diff --git a/cross-build.nix b/cross-build.nix new file mode 100644 index 0000000..0cdd379 --- /dev/null +++ b/cross-build.nix @@ -0,0 +1,31 @@ +{ defaultPlatformProject ? import ./default.nix {} , +toBuild ? import ./nix/cross-build/systems.nix defaultPlatformProject.pkgs +} : +# map through the system list +defaultPlatformProject.pkgs.lib.mapAttrs (_: pkgs: rec { + # nativePkgs.lib.recurseIntoAttrs, just a bit more explicilty. + recurseForDerivations = true; + + webdriver-w3c = import ./default.nix { nativePkgs = pkgs; + customModules = [ { packages.webdriver-w3c.dontStrip = false; } ] ++ (if pkgs.stdenv.hostPlatform.isMusl then + [ + # following customization is to build fully static binary for project using postgresql-libpq + { packages.postgresql-libpq.flags.use-pkg-config = true; } + # The order of -lssl and -lcrypto is important here + { packages.webdriver-w3c.configureFlags = + [ + "--ghc-option=-optl=-lssl" + "--ghc-option=-optl=-lcrypto" + "--ghc-option=-optl=-L${pkgs.openssl.out}/lib" + ]; + } + { packages.script-monad.patches = [ ./script-monad-http-client-remove-Eq.patch ] ; } + ] + else + []); + }; + + inherit pkgs; + +}) toBuild + diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..da144fd --- /dev/null +++ b/default.nix @@ -0,0 +1,45 @@ +let + sources = import ./nix/sources.nix; + # Fetch the latest haskell.nix and import its default.nix + haskellNix = import sources."haskell.nix" {}; + # haskell.nix provides access to the nixpkgs pins which are used by our CI, hence + # you will be more likely to get cache hits when using these. + # But you can also just use your own, e.g. '' + #nixpkgsSrc = if haskellNix.pkgs.stdenv.hostPlatform.isDarwin then sources.nixpkgs-darwin else haskellNix.sources.nixpkgs-2111; + # no need to check platform now + nixpkgsSrc = haskellNix.sources.nixpkgs-2111; + # haskell.nix provides some arguments to be passed to nixpkgs, including some patches + # and also the haskell.nix functionality itself as an overlay. + nixpkgsArgs = haskellNix.nixpkgsArgs; +in +{ nativePkgs ? import nixpkgsSrc (nixpkgsArgs // { overlays = nixpkgsArgs.overlays ++ [(import ./nix/overlay)]; }) +, haskellCompiler ? "ghc8107" +, customModules ? [] +}: +let pkgs = nativePkgs; +in +# 'cabalProject' generates a package set based on a cabal.project (and the corresponding .cabal files) +rec { + # inherit the pkgs package set so that others importing this function can use it + inherit pkgs; + + # nativePkgs.lib.recurseIntoAttrs, just a bit more explicilty. + recurseForDerivations = true; + + webdriver-w3c = (pkgs.haskell-nix.project { + src = pkgs.haskell-nix.haskellLib.cleanGit { + name = "webdriver-w3c"; + src = ./.; + }; + index-state = pkgs.haskell-nix.internalHackageIndexState; + compiler-nix-name = haskellCompiler; + modules = customModules; + }); + + repldemo-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-repl-demo; + tastydemo-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-tasty-demo; + parallelstresstest-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-parallel-stress-test; + #webdriver-w3c-test = webdriver-w3c.webdriver-w3c.components.tests.webdriver-w3c-test; + +} + diff --git a/docker.nix b/docker.nix new file mode 100644 index 0000000..e180472 --- /dev/null +++ b/docker.nix @@ -0,0 +1,20 @@ +{ nativePkgs ? (import ./default.nix {}).pkgs, +crossBuildProject ? import ./cross-build.nix {} }: +nativePkgs.lib.mapAttrs (_: prj: +with prj.webdriver-w3c; +let + executable = webdriver-w3c.webdriver-w3c.components.exes.webdriver-w3c; + binOnly = prj.pkgs.runCommand "webdriver-w3c-bin" { } '' + mkdir -p $out/bin + cp ${executable}/bin/webdriver-w3c $out/bin + ${nativePkgs.nukeReferences}/bin/nuke-refs $out/bin/webdriver-w3c + ''; +in { + webdriver-w3c-image = prj.pkgs.dockerTools.buildImage { + name = "webdriver-w3c"; + tag = executable.version; + contents = [ binOnly prj.pkgs.cacert prj.pkgs.iana-etc ]; + config.Entrypoint = "webdriver-w3c"; + config.Cmd = "--help"; + }; +}) crossBuildProject diff --git a/hie.yaml b/hie.yaml new file mode 100644 index 0000000..9ae3e69 --- /dev/null +++ b/hie.yaml @@ -0,0 +1,10 @@ +cradle: + cabal: + - path: "./src" + component: "lib:webdriver-w3c" + + - path: "./app" + component: "webdriver-w3c:exe:webdriver-w3c" + + - path: "./test" + component: "webdriver-w3c:test:webdriver-w3c-test" diff --git a/nix/cross-build/systems.nix b/nix/cross-build/systems.nix new file mode 100644 index 0000000..6f5cd5a --- /dev/null +++ b/nix/cross-build/systems.nix @@ -0,0 +1,17 @@ +nativePkgs: +with nativePkgs.pkgsCross; { + # x86-gnu32 = gnu32; + #x86-gnu64 = nativePkgs; #gnu64; # should be == nativePkgs + # x86-musl32 = musl32; + x86-musl64 = musl64; + #x86-win64 = mingwW64; + #rpi1-gnu = raspberryPi; + #rpi1-musl = muslpi; + #rpi32-gnu = armv7l-hf-multiplatform; + # sadly this one is missing from the nixpkgs system examples + #rpi32-musl = import nixpkgsSrc (nativePkgs.lib.recursiveUpdate nixpkgsArgs + #{ crossSystem = nativePkgs.lib.systems.examples.armv7l-hf-multiplatform + #// { config = "armv7l-unknown-linux-musleabihf"; }; }); + #rpi64-gnu = aarch64-multiplatform; + #rpi64-musl = aarch64-multiplatform-musl; +} diff --git a/nix/overlay/default.nix b/nix/overlay/default.nix new file mode 100644 index 0000000..ab57fa6 --- /dev/null +++ b/nix/overlay/default.nix @@ -0,0 +1,2 @@ +self: prev: +with prev.lib; mapAttrs' (attr: _: { name = removeSuffix ".nix" attr; value = ./. + "/${attr}"; }) (filterAttrs (attr: _: attr != "default.nix" ) (builtins.readDir ./.)) diff --git a/nix/sources.json b/nix/sources.json new file mode 100644 index 0000000..4cad7a7 --- /dev/null +++ b/nix/sources.json @@ -0,0 +1,50 @@ +{ + "haskell.nix": { + "branch": "master", + "description": "Alternative Haskell Infrastructure for Nixpkgs", + "homepage": "https://input-output-hk.github.io/haskell.nix", + "owner": "input-output-hk", + "repo": "haskell.nix", + "rev": "83bf601595baeaf1b0789fb44572f59143e28034", + "sha256": "0zg68ivzsh54zb8w33w7grhw6c3y4lw8nh8njjk0b6m85wvlwv9d", + "type": "tarball", + "url": "https://github.com/input-output-hk/haskell.nix/archive/83bf601595baeaf1b0789fb44572f59143e28034.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, + "niv": { + "branch": "master", + "description": "Easy dependency management for Nix projects", + "homepage": "https://github.com/nmattia/niv", + "owner": "nmattia", + "repo": "niv", + "rev": "2998a663d03ef9521585f67cf8fdfa8dac348462", + "sha256": "17wgdcqysj5dah6h8dydvvnraff70nh2sgz059mw92qzm9i5gv6g", + "type": "tarball", + "url": "https://github.com/nmattia/niv/archive/2998a663d03ef9521585f67cf8fdfa8dac348462.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, + "nixpkgs": { + "branch": "nixos-21.11", + "description": "Nix Packages collection", + "homepage": "", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7a3f9d626c8a88141077ab99d8352469aa6feeb7", + "sha256": "1w8yv4sar5sk4dw3jdmxdzd4dc1ngs10m6mdq1ill55rwivsy0ks", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/7a3f9d626c8a88141077ab99d8352469aa6feeb7.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, + "nixpkgs-darwin": { + "branch": "nixpkgs-21.11-darwin", + "description": "Nix Packages collection", + "homepage": "", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ba93b1d8253ed4b359b9e81d10e02c106d3f8b11", + "sha256": "0pzlw69mbq0aa1c158ap4fyzhmcbpzgbdpapcxcwylzhs21xjf55", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/ba93b1d8253ed4b359b9e81d10e02c106d3f8b11.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + } +} diff --git a/nix/sources.nix b/nix/sources.nix new file mode 100644 index 0000000..1938409 --- /dev/null +++ b/nix/sources.nix @@ -0,0 +1,174 @@ +# This file has been generated by Niv. + +let + + # + # The fetchers. fetch_ fetches specs of type . + # + + fetch_file = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchurl { inherit (spec) url sha256; name = name'; } + else + pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; + + fetch_tarball = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchTarball { name = name'; inherit (spec) url sha256; } + else + pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; + + fetch_git = name: spec: + let + ref = + if spec ? ref then spec.ref else + if spec ? branch then "refs/heads/${spec.branch}" else + if spec ? tag then "refs/tags/${spec.tag}" else + abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; + in + builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; + + fetch_local = spec: spec.path; + + fetch_builtin-tarball = name: throw + ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=tarball -a builtin=true''; + + fetch_builtin-url = name: throw + ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=file -a builtin=true''; + + # + # Various helpers + # + + # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 + sanitizeName = name: + ( + concatMapStrings (s: if builtins.isList s then "-" else s) + ( + builtins.split "[^[:alnum:]+._?=-]+" + ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) + ) + ); + + # The set of packages used when specs are fetched using non-builtins. + mkPkgs = sources: system: + let + sourcesNixpkgs = + import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; + hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; + hasThisAsNixpkgsPath = == ./.; + in + if builtins.hasAttr "nixpkgs" sources + then sourcesNixpkgs + else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then + import {} + else + abort + '' + Please specify either (through -I or NIX_PATH=nixpkgs=...) or + add a package called "nixpkgs" to your sources.json. + ''; + + # The actual fetching function. + fetch = pkgs: name: spec: + + if ! builtins.hasAttr "type" spec then + abort "ERROR: niv spec ${name} does not have a 'type' attribute" + else if spec.type == "file" then fetch_file pkgs name spec + else if spec.type == "tarball" then fetch_tarball pkgs name spec + else if spec.type == "git" then fetch_git name spec + else if spec.type == "local" then fetch_local spec + else if spec.type == "builtin-tarball" then fetch_builtin-tarball name + else if spec.type == "builtin-url" then fetch_builtin-url name + else + abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; + + # If the environment variable NIV_OVERRIDE_${name} is set, then use + # the path directly as opposed to the fetched source. + replace = name: drv: + let + saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; + ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; + in + if ersatz == "" then drv else + # this turns the string into an actual Nix path (for both absolute and + # relative paths) + if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; + + # Ports of functions for older nix versions + + # a Nix version of mapAttrs if the built-in doesn't exist + mapAttrs = builtins.mapAttrs or ( + f: set: with builtins; + listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) + ); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 + range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 + stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 + stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); + concatMapStrings = f: list: concatStrings (map f list); + concatStrings = builtins.concatStringsSep ""; + + # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 + optionalAttrs = cond: as: if cond then as else {}; + + # fetchTarball version that is compatible between all the versions of Nix + builtins_fetchTarball = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchTarball; + in + if lessThan nixVersion "1.12" then + fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchTarball attrs; + + # fetchurl version that is compatible between all the versions of Nix + builtins_fetchurl = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchurl; + in + if lessThan nixVersion "1.12" then + fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchurl attrs; + + # Create the final "sources" from the config + mkSources = config: + mapAttrs ( + name: spec: + if builtins.hasAttr "outPath" spec + then abort + "The values in sources.json should not have an 'outPath' attribute" + else + spec // { outPath = replace name (fetch config.pkgs name spec); } + ) config.sources; + + # The "config" used by the fetchers + mkConfig = + { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null + , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) + , system ? builtins.currentSystem + , pkgs ? mkPkgs sources system + }: rec { + # The sources, i.e. the attribute set of spec name to spec + inherit sources; + + # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers + inherit pkgs; + }; + +in +mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/script-monad-http-client-remove-Eq.patch b/script-monad-http-client-remove-Eq.patch new file mode 100644 index 0000000..7c555a7 --- /dev/null +++ b/script-monad-http-client-remove-Eq.patch @@ -0,0 +1,52 @@ +diff --git a/src/Control/Monad/Script/Http.hs b/src/Control/Monad/Script/Http.hs +index 2602ba3..993801e 100644 +--- a/src/Control/Monad/Script/Http.hs ++++ b/src/Control/Monad/Script/Http.hs +@@ -133,6 +133,8 @@ import Data.Aeson.Encode.Pretty + ( encodePretty ) + import Data.Aeson.Lens + ( _Value ) ++import Data.Aeson.KeyMap ++ ( toHashMapText ) + import Data.ByteString.Lazy + ( ByteString, fromStrict, readFile, writeFile ) + import Data.ByteString.Lazy.Char8 +@@ -1153,7 +1155,7 @@ lookupKeyJson + -> Value -- ^ JSON object + -> HttpTT e r w s p t eff Value + lookupKeyJson key v = case v of +- Object obj -> case lookup key obj of ++ Object obj -> case lookup key (toHashMapText obj) of + Nothing -> throwJsonError $ JsonKeyDoesNotExist key (Object obj) + Just value -> return value + _ -> throwJsonError $ JsonKeyLookupOffObject key v +diff --git a/src/Network/HTTP/Client/Extras.hs b/src/Network/HTTP/Client/Extras.hs +index 22c47a9..787612e 100644 +--- a/src/Network/HTTP/Client/Extras.hs ++++ b/src/Network/HTTP/Client/Extras.hs +@@ -40,6 +40,7 @@ import Network.HTTP.Client + , responseHeaders, responseVersion, responseStatus ) + import Network.HTTP.Types + import Data.Aeson (Value(..), object, (.=)) ++import qualified Data.Aeson.Key as AK ( fromText ) + import qualified Data.Text as T (Text, pack) + + +@@ -53,7 +54,7 @@ data HttpResponse = HttpResponse + , _responseHeaders :: ResponseHeaders + , _responseBody :: ByteString + , _responseCookieJar :: CookieJar +- } deriving (Eq, Show) ++ } deriving (Show) + + -- | Convert an opaque `Response ByteString` into an `HttpResponse`. + readHttpResponse :: Response ByteString -> HttpResponse +@@ -72,7 +73,7 @@ jsonResponseHeaders :: ResponseHeaders -> Value + jsonResponseHeaders = + Array . fromList . map (\(k,v) -> object [ (key k) .= (val v) ]) + where +- key = T.pack . concatMap esc . show ++ key = AK.fromText . T.pack . concatMap esc . show + val = T.pack . concatMap esc . show + + esc c = case c of diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..7068d8a --- /dev/null +++ b/shell.nix @@ -0,0 +1,32 @@ +{ defaultPlatformProject ? import ./default.nix {} }: + + defaultPlatformProject.webdriver-w3c.shellFor { + # Include only the *local* packages of your project. + packages = ps: with ps; [ + webdriver-w3c + #pkgb + ]; + + # Builds a Hoogle documentation index of all dependencies, + # and provides a "hoogle" command to search the index. + withHoogle = true; + + # You might want some extra tools in the shell (optional). + + # Some common tools can be added with the `tools` argument + #tools = { cabal = "latest"; hlint = "latest"; }; + tools = { cabal = defaultPlatformProject.pkgs.haskell-nix.cabal-install.ghc8107.version; + hasktags = "latest"; + haskell-language-server = "latest"; + }; + # See overlays/tools.nix for more details + + # Some you may need to get some other way. + buildInputs = with defaultPlatformProject.pkgs; + #[ ghcid lorri niv ]; + [ ]; + + # Prevents cabal from choosing alternate plans, so that + # *all* dependencies are provided by Nix. + exactDeps = true; +} diff --git a/stack.yaml b/stack.yaml deleted file mode 100644 index 997937c..0000000 --- a/stack.yaml +++ /dev/null @@ -1,7 +0,0 @@ -resolver: lts-16.0 - -packages: -- . - -extra-deps: -- script-monad-0.0.3 diff --git a/start-dev b/start-dev new file mode 100755 index 0000000..471ecc6 --- /dev/null +++ b/start-dev @@ -0,0 +1 @@ +nix-shell --run "[[ -f ~/.doom.d/config.el ]] && sed -i '/enable-local-variables/d' ~/.doom.d/config.el && echo '(setq-default enable-local-variables t)' >> ~/.doom.d/config.el ; [[ -x ~/.emacs.d/bin/doom ]] && ~/.emacs.d/bin/doom env ; emacs -nw" diff --git a/tarball.nix b/tarball.nix new file mode 100644 index 0000000..d26055e --- /dev/null +++ b/tarball.nix @@ -0,0 +1,27 @@ +{ nativePkgs ? (import ./default.nix {}).pkgs, +crossBuildProject ? import ./cross-build.nix {} }: +nativePkgs.lib.mapAttrs (_: prj: +with prj.webdriver-w3c; +let + executable = webdriver-w3c.webdriver-w3c.components.exes.webdriver-w3c; + binOnly = prj.pkgs.runCommand "webdriver-w3c-bin" { } '' + mkdir -p $out/bin + cp -R ${executable}/bin/* $out/bin/ + ${nativePkgs.nukeReferences}/bin/nuke-refs $out/bin/webdriver-w3c + ''; + + tarball = nativePkgs.stdenv.mkDerivation { + name = "webdriver-w3c-tarball"; + buildInputs = with nativePkgs; [ zip ]; + + phases = [ "installPhase" ]; + + installPhase = '' + mkdir -p $out/ + zip -r -9 $out/webdriver-w3c-tarball.zip ${binOnly} + ''; + }; +in { + webdriver-w3c-tarball = tarball; +} +) crossBuildProject From 25b0b64002050d4581c879090c0d6683d54f4297 Mon Sep 17 00:00:00 2001 From: Hugh JF Chen Date: Thu, 31 Mar 2022 02:54:31 -0500 Subject: [PATCH 3/4] add GetResourcePerformanceData. --- app/GetResourcePerformanceData.hs | 151 ++++++++++++++++++++++++++++++ cross-build.nix | 1 - default.nix | 2 +- webdriver-w3c.cabal | 12 +++ 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 app/GetResourcePerformanceData.hs diff --git a/app/GetResourcePerformanceData.hs b/app/GetResourcePerformanceData.hs new file mode 100644 index 0000000..7525ba7 --- /dev/null +++ b/app/GetResourcePerformanceData.hs @@ -0,0 +1,151 @@ +module Main where + +import Control.Monad.IO.Class +import Data.List (isInfixOf) + +import Web.Api.WebDriver + +{- +Utilities for demoing webdriver sessions. I mostly use this for testing +and development. To run an individual demo, first make sure your remote end +(chromedriver or geckodriver) is running on the correct port, then from ghci +say something like: + + withChromedriver normalChrome demoNewWindow + +The executable runs all the demos, although this is less useful. +-} + +main :: IO () +main = do + + withChromedriver headlessChrome getResourcePerformanceChrome + + + + + +withChromedriver :: Capabilities -> WebDriverT IO () -> IO () +withChromedriver caps acts = do + execWebDriverT chromeConfig $ + runIsolated_ caps acts + return () + +chromeConfig :: WebDriverConfig IO +chromeConfig = defaultWebDriverConfig + { _environment = defaultWebDriverEnvironment + { _env = defaultWDEnv + { _remotePort = 9515 + , _responseFormat = SpecFormat + } + } + } + +normalChrome :: Capabilities +normalChrome = defaultChromeCapabilities + +headlessChrome :: Capabilities +headlessChrome = defaultChromeCapabilities + { _chromeOptions = Just $ defaultChromeOptions + { _chromeArgs = Just ["--headless"] + } + } + +consoleLogChrome :: Capabilities +consoleLogChrome = defaultChromeCapabilities + { _chromeOptions = Just $ defaultChromeOptions + { _chromeArgs = Just ["--user-data-dir=datadir", "--enable-logging", "--v=1"] + } + } + + + +demoNewWindow :: WebDriverT IO () +demoNewWindow = do + -- open google.com in the current tab + navigateTo "https://www.google.com" + + -- open a new tab; but do not switch to it + (handle, _) <- newWindow TabContext + + -- switch to the new tab + switchToWindow handle + + -- open bing.com in this tab + navigateTo "https://www.bing.com" + + wait 5000000 + return () + + + +demoGetComputedRole :: WebDriverT IO () +demoGetComputedRole = do + -- open google.com + navigateTo "https://www.google.com" + + -- get the ARIA role of whatever element is active on page load + role <- getActiveElement >>= getComputedRole + + comment $ "Computed role is '" <> role <> "'" + wait 5000000 + return () + + + +demoGetComputedLabel :: WebDriverT IO () +demoGetComputedLabel = do + -- open google.com + navigateTo "https://www.google.com" + + -- get the ARIA label of whatever element is active on page load + role <- getActiveElement >>= getComputedLabel + + comment $ "Computed label is '" <> role <> "'" + wait 5000000 + return () + + + +demoPrintPage :: WebDriverT IO () +demoPrintPage = do + -- open google.com + navigateTo "https://www.google.com" + + -- print + pdf <- printPage defaultPrintOptions + writeBase64EncodedPdf "testprint.pdf" pdf + + wait 5000000 + return () + + + +-- use this to demonstrate getting the JS console log +-- withChromedriver consoleLogChrome demoConsoleLogChrome +demoConsoleLogChrome :: WebDriverT IO () +demoConsoleLogChrome = do + -- open google.com + navigateTo "https://www.google.com" + + executeScript "console.error('HEYOOOO')" [] + + -- logLines <- fmap lines $ liftIO $ readFile "~/datadir/chrome_debug.log" -- you'll need to expand ~ here + -- let lines = filter ("CONSOLE" `isInfixOf`) logLines + -- liftIO $ print lines + + wait 5000000 + return () + +-- get the performance data of the page and extract the network requests +getResourcePerformanceChrome :: WebDriverT IO () +getResourcePerformanceChrome = do + -- open the SQ trading condition page + navigateTo "https://en.swissquote.com/forex/pricing/trading-conditions" + + resPerf <- executeScript "performance.getEntriesByType('resource');" [] + print resPerf + + wait 5000000 + + return () diff --git a/cross-build.nix b/cross-build.nix index 0cdd379..40c6f2b 100644 --- a/cross-build.nix +++ b/cross-build.nix @@ -19,7 +19,6 @@ defaultPlatformProject.pkgs.lib.mapAttrs (_: pkgs: rec { "--ghc-option=-optl=-L${pkgs.openssl.out}/lib" ]; } - { packages.script-monad.patches = [ ./script-monad-http-client-remove-Eq.patch ] ; } ] else []); diff --git a/default.nix b/default.nix index da144fd..46ad947 100644 --- a/default.nix +++ b/default.nix @@ -33,7 +33,7 @@ rec { }; index-state = pkgs.haskell-nix.internalHackageIndexState; compiler-nix-name = haskellCompiler; - modules = customModules; + modules = [ { packages.script-monad.patches = [ ./script-monad-http-client-remove-Eq.patch ] ; } ] ++ customModules; }); repldemo-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-repl-demo; diff --git a/webdriver-w3c.cabal b/webdriver-w3c.cabal index 5dc6959..3b56c80 100644 --- a/webdriver-w3c.cabal +++ b/webdriver-w3c.cabal @@ -130,6 +130,18 @@ executable wd-repl-demo , tasty >=1.0.1.1 +executable get-resource-performance-data + default-language: Haskell2010 + main-is: GetResourcePerformanceData.hs + hs-source-dirs: app + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + webdriver-w3c + , base >=4.7 && <5 + + , tasty >=1.0.1.1 + + test-suite webdriver-w3c-test default-language: Haskell2010 From f6dcfc454d91a40c5306e1f272ef36fd0a47e2ad Mon Sep 17 00:00:00 2001 From: Hugh JF Chen Date: Thu, 31 Mar 2022 08:06:04 -0500 Subject: [PATCH 4/4] add GetResourcePerformanceData.sh --- app/GetResourcePerformanceData.hs | 14 ++++++++++---- default.nix | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/GetResourcePerformanceData.hs b/app/GetResourcePerformanceData.hs index 7525ba7..20d8de7 100644 --- a/app/GetResourcePerformanceData.hs +++ b/app/GetResourcePerformanceData.hs @@ -19,7 +19,8 @@ The executable runs all the demos, although this is less useful. main :: IO () main = do - withChromedriver headlessChrome getResourcePerformanceChrome + -- withChromedriver headlessChrome getResourcePerformanceChrome + withChromedriver consoleLogChrome getResourcePerformanceChrome @@ -54,7 +55,7 @@ headlessChrome = defaultChromeCapabilities consoleLogChrome :: Capabilities consoleLogChrome = defaultChromeCapabilities { _chromeOptions = Just $ defaultChromeOptions - { _chromeArgs = Just ["--user-data-dir=datadir", "--enable-logging", "--v=1"] + { _chromeArgs = Just ["--user-data-dir=datadir1", "--enable-logging", "--v=1"] } } @@ -143,8 +144,13 @@ getResourcePerformanceChrome = do -- open the SQ trading condition page navigateTo "https://en.swissquote.com/forex/pricing/trading-conditions" - resPerf <- executeScript "performance.getEntriesByType('resource');" [] - print resPerf + -- wait for 5 secs to make sure the page load swap and spread. + wait 5000000 + resPerf <- executeScript "console.error(JSON.stringify(performance.getEntriesByType('resource')));" [] + -- liftIO $ print resPerf + logLines <- fmap lines $ liftIO $ readFile "/home/chenjf/projects/datadir1/chrome_debug.log" -- you'll need to expand ~ here + let lines = filter ("CONSOLE" `isInfixOf`) logLines + liftIO $ print lines wait 5000000 diff --git a/default.nix b/default.nix index 46ad947..2974c9c 100644 --- a/default.nix +++ b/default.nix @@ -39,6 +39,7 @@ rec { repldemo-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-repl-demo; tastydemo-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-tasty-demo; parallelstresstest-exe = webdriver-w3c.webdriver-w3c.components.exes.wd-parallel-stress-test; + getresourceperformancedata-exe = webdriver-w3c.webdriver-w3c.components.exes.get-resource-performance-data; #webdriver-w3c-test = webdriver-w3c.webdriver-w3c.components.tests.webdriver-w3c-test; }