diff --git a/.circleci/config.yml b/.circleci/config.yml index e82daad..1b38118 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ defaults: &defaults working_directory: ~/aws-lambda-haskell-runtime docker: - - image: fpco/stack-build:lts-13.0 + - image: fpco/stack-build:lts-13.25 version: 2 jobs: diff --git a/.hlint.yaml b/.hlint.yaml index 11bb23b..fd49751 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -1,2986 +1,76 @@ +# HLint configuration file +# https://github.com/ndmitchell/hlint +########################## + +# This file contains a template configuration file, which is typically +# placed as .hlint.yaml in the root of your project + + +# Specify additional command line arguments +# - 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 - note: ! '''Alternative'' is already exported from Relude' - name: Use 'Alternative' from Relude - rhs: Alternative -- warn: - lhs: Control.Applicative.empty - note: ! '''empty'' is already exported from Relude' - name: Use 'empty' from Relude - rhs: empty -- warn: - lhs: (Control.Applicative.<|>) - note: Operator '(<|>)' is already exported from Relude - name: Use '<|>' from Relude - rhs: (<|>) -- warn: - lhs: Control.Applicative.some - note: ! '''some'' is already exported from Relude' - name: Use 'some' from Relude - rhs: some -- warn: - lhs: Control.Applicative.many - note: ! '''many'' is already exported from Relude' - name: Use 'many' from Relude - rhs: many -- warn: - lhs: Control.Applicative.Const - note: ! '''Const'' is already exported from Relude' - name: Use 'Const' from Relude - rhs: Const -- warn: - lhs: Control.Applicative.getConst - note: ! '''getConst'' is already exported from Relude' - name: Use 'getConst' from Relude - rhs: getConst -- warn: - lhs: Control.Applicative.ZipList - note: ! '''ZipList'' is already exported from Relude' - name: Use 'ZipList' from Relude - rhs: ZipList -- warn: - lhs: Control.Applicative.getZipList - note: ! '''getZipList'' is already exported from Relude' - name: Use 'getZipList' from Relude - rhs: getZipList -- warn: - lhs: Control.Applicative.liftA2 - note: ! '''liftA2'' is already exported from Relude' - name: Use 'liftA2' from Relude - rhs: liftA2 -- warn: - lhs: Control.Applicative.liftA3 - note: ! '''liftA3'' is already exported from Relude' - name: Use 'liftA3' from Relude - rhs: liftA3 -- warn: - lhs: Control.Applicative.optional - note: ! '''optional'' is already exported from Relude' - name: Use 'optional' from Relude - rhs: optional -- warn: - lhs: (Control.Applicative.<**>) - note: Operator '(<**>)' is already exported from Relude - name: Use '<**>' from Relude - rhs: (<**>) -- warn: - lhs: Data.Bits.xor - note: ! '''xor'' is already exported from Relude' - name: Use 'xor' from Relude - rhs: xor -- warn: - lhs: Data.Char.chr - note: ! '''chr'' is already exported from Relude' - name: Use 'chr' from Relude - rhs: chr -- warn: - lhs: Data.Int.Int8 - note: ! '''Int8'' is already exported from Relude' - name: Use 'Int8' from Relude - rhs: Int8 -- warn: - lhs: Data.Int.Int16 - note: ! '''Int16'' is already exported from Relude' - name: Use 'Int16' from Relude - rhs: Int16 -- warn: - lhs: Data.Int.Int32 - note: ! '''Int32'' is already exported from Relude' - name: Use 'Int32' from Relude - rhs: Int32 -- warn: - lhs: Data.Int.Int64 - note: ! '''Int64'' is already exported from Relude' - name: Use 'Int64' from Relude - rhs: Int64 -- warn: - lhs: Data.Word.Word8 - note: ! '''Word8'' is already exported from Relude' - name: Use 'Word8' from Relude - rhs: Word8 -- warn: - lhs: Data.Word.Word16 - note: ! '''Word16'' is already exported from Relude' - name: Use 'Word16' from Relude - rhs: Word16 -- warn: - lhs: Data.Word.Word32 - note: ! '''Word32'' is already exported from Relude' - name: Use 'Word32' from Relude - rhs: Word32 -- warn: - lhs: Data.Word.Word64 - note: ! '''Word64'' is already exported from Relude' - name: Use 'Word64' from Relude - rhs: Word64 -- warn: - lhs: Data.Word.byteSwap16 - note: ! '''byteSwap16'' is already exported from Relude' - name: Use 'byteSwap16' from Relude - rhs: byteSwap16 -- warn: - lhs: Data.Word.byteSwap32 - note: ! '''byteSwap32'' is already exported from Relude' - name: Use 'byteSwap32' from Relude - rhs: byteSwap32 -- warn: - lhs: Data.Word.byteSwap64 - note: ! '''byteSwap64'' is already exported from Relude' - name: Use 'byteSwap64' from Relude - rhs: byteSwap64 -- warn: - lhs: Numeric.Natural.Natural - note: ! '''Natural'' is already exported from Relude' - name: Use 'Natural' from Relude - rhs: Natural -- warn: - lhs: System.IO.Handle - note: ! '''Handle'' is already exported from Relude' - name: Use 'Handle' from Relude - rhs: Handle -- warn: - lhs: System.IO.IOMode - note: ! '''IOMode'' is already exported from Relude' - name: Use 'IOMode' from Relude - rhs: IOMode -- warn: - lhs: System.IO.ReadMode - note: ! '''ReadMode'' is already exported from Relude' - name: Use 'ReadMode' from Relude - rhs: ReadMode -- warn: - lhs: System.IO.WriteMode - note: ! '''WriteMode'' is already exported from Relude' - name: Use 'WriteMode' from Relude - rhs: WriteMode -- warn: - lhs: System.IO.AppendMode - note: ! '''AppendMode'' is already exported from Relude' - name: Use 'AppendMode' from Relude - rhs: AppendMode -- warn: - lhs: System.IO.ReadWriteMode - note: ! '''ReadWriteMode'' is already exported from Relude' - name: Use 'ReadWriteMode' from Relude - rhs: ReadWriteMode -- warn: - lhs: System.IO.stderr - note: ! '''stderr'' is already exported from Relude' - name: Use 'stderr' from Relude - rhs: stderr -- warn: - lhs: System.IO.stdin - note: ! '''stdin'' is already exported from Relude' - name: Use 'stdin' from Relude - rhs: stdin -- warn: - lhs: System.IO.stdout - note: ! '''stdout'' is already exported from Relude' - name: Use 'stdout' from Relude - rhs: stdout -- warn: - lhs: System.IO.withFile - note: ! '''withFile'' is already exported from Relude' - name: Use 'withFile' from Relude - rhs: withFile -- warn: - lhs: Data.Ord.Down - note: ! '''Down'' is already exported from Relude' - name: Use 'Down' from Relude - rhs: Down -- warn: - lhs: Data.Ord.comparing - note: ! '''comparing'' is already exported from Relude' - name: Use 'comparing' from Relude - rhs: comparing -- warn: - lhs: Data.Coerce.Coercible - note: ! '''Coercible'' is already exported from Relude' - name: Use 'Coercible' from Relude - rhs: Coercible -- warn: - lhs: Data.Coerce.coerce - note: ! '''coerce'' is already exported from Relude' - name: Use 'coerce' from Relude - rhs: coerce -- warn: - lhs: Data.Kind.Constraint - note: ! '''Constraint'' is already exported from Relude' - name: Use 'Constraint' from Relude - rhs: Constraint -- warn: - lhs: Data.Kind.Type - note: ! '''Type'' is already exported from Relude' - name: Use 'Type' from Relude - rhs: Type -- warn: - lhs: Data.Typeable.Typeable - note: ! '''Typeable'' is already exported from Relude' - name: Use 'Typeable' from Relude - rhs: Typeable -- warn: - lhs: Data.Proxy.Proxy - note: ! '''Proxy'' is already exported from Relude' - name: Use 'Proxy' from Relude - rhs: Proxy -- warn: - lhs: Data.Typeable.Typeable - note: ! '''Typeable'' is already exported from Relude' - name: Use 'Typeable' from Relude - rhs: Typeable -- warn: - lhs: Data.Void.Void - note: ! '''Void'' is already exported from Relude' - name: Use 'Void' from Relude - rhs: Void -- warn: - lhs: Data.Void.absurd - note: ! '''absurd'' is already exported from Relude' - name: Use 'absurd' from Relude - rhs: absurd -- warn: - lhs: Data.Void.vacuous - note: ! '''vacuous'' is already exported from Relude' - name: Use 'vacuous' from Relude - rhs: vacuous -- warn: - lhs: Data.Base.maxInt - note: ! '''maxInt'' is already exported from Relude' - name: Use 'maxInt' from Relude - rhs: maxInt -- warn: - lhs: Data.Base.minInt - note: ! '''minInt'' is already exported from Relude' - name: Use 'minInt' from Relude - rhs: minInt -- warn: - lhs: Data.Base.ord - note: ! '''ord'' is already exported from Relude' - name: Use 'ord' from Relude - rhs: ord -- warn: - lhs: GHC.Enum.boundedEnumFrom - note: ! '''boundedEnumFrom'' is already exported from Relude' - name: Use 'boundedEnumFrom' from Relude - rhs: boundedEnumFrom -- warn: - lhs: GHC.Enum.boundedEnumFromThen - note: ! '''boundedEnumFromThen'' is already exported from Relude' - name: Use 'boundedEnumFromThen' from Relude - rhs: boundedEnumFromThen -- warn: - lhs: GHC.Generics.Generic - note: ! '''Generic'' is already exported from Relude' - name: Use 'Generic' from Relude - rhs: Generic -- warn: - lhs: GHC.Real.Ratio - note: ! '''Ratio'' is already exported from Relude' - name: Use 'Ratio' from Relude - rhs: Ratio -- warn: - lhs: GHC.Real.Rational - note: ! '''Rational'' is already exported from Relude' - name: Use 'Rational' from Relude - rhs: Rational -- warn: - lhs: GHC.Real.denominator - note: ! '''denominator'' is already exported from Relude' - name: Use 'denominator' from Relude - rhs: denominator -- warn: - lhs: GHC.Real.numerator - note: ! '''numerator'' is already exported from Relude' - name: Use 'numerator' from Relude - rhs: numerator -- warn: - lhs: GHC.TypeNats.CmpNat - note: ! '''CmpNat'' is already exported from Relude' - name: Use 'CmpNat' from Relude - rhs: CmpNat -- warn: - lhs: GHC.TypeNats.KnownNat - note: ! '''KnownNat'' is already exported from Relude' - name: Use 'KnownNat' from Relude - rhs: KnownNat -- warn: - lhs: GHC.TypeNats.Nat - note: ! '''Nat'' is already exported from Relude' - name: Use 'Nat' from Relude - rhs: Nat -- warn: - lhs: GHC.TypeNats.SomeNat - note: ! '''SomeNat'' is already exported from Relude' - name: Use 'SomeNat' from Relude - rhs: SomeNat -- warn: - lhs: GHC.TypeNats.natVal - note: ! '''natVal'' is already exported from Relude' - name: Use 'natVal' from Relude - rhs: natVal -- warn: - lhs: GHC.TypeNats.someNatVal - note: ! '''someNatVal'' is already exported from Relude' - name: Use 'someNatVal' from Relude - rhs: someNatVal -- warn: - lhs: GHC.TypeLits.CmpNat - note: ! '''CmpNat'' is already exported from Relude' - name: Use 'CmpNat' from Relude - rhs: CmpNat -- warn: - lhs: GHC.TypeLits.KnownNat - note: ! '''KnownNat'' is already exported from Relude' - name: Use 'KnownNat' from Relude - rhs: KnownNat -- warn: - lhs: GHC.TypeLits.Nat - note: ! '''Nat'' is already exported from Relude' - name: Use 'Nat' from Relude - rhs: Nat -- warn: - lhs: GHC.TypeLits.SomeNat - note: ! '''SomeNat'' is already exported from Relude' - name: Use 'SomeNat' from Relude - rhs: SomeNat -- warn: - lhs: GHC.TypeLits.natVal - note: ! '''natVal'' is already exported from Relude' - name: Use 'natVal' from Relude - rhs: natVal -- warn: - lhs: GHC.TypeLits.someNatVal - note: ! '''someNatVal'' is already exported from Relude' - name: Use 'someNatVal' from Relude - rhs: someNatVal -- warn: - lhs: GHC.ExecutionStack.getStackTrace - note: ! '''getStackTrace'' is already exported from Relude' - name: Use 'getStackTrace' from Relude - rhs: getStackTrace -- warn: - lhs: GHC.ExecutionStack.showStackTrace - note: ! '''showStackTrace'' is already exported from Relude' - name: Use 'showStackTrace' from Relude - rhs: showStackTrace -- warn: - lhs: GHC.OverloadedLabels.IsLabel - note: ! '''IsLabel'' is already exported from Relude' - name: Use 'IsLabel' from Relude - rhs: IsLabel -- warn: - lhs: GHC.OverloadedLabels.fromLabel - note: ! '''fromLabel'' is already exported from Relude' - name: Use 'fromLabel' from Relude - rhs: fromLabel -- warn: - lhs: GHC.Stack.CallStack - note: ! '''CallStack'' is already exported from Relude' - name: Use 'CallStack' from Relude - rhs: CallStack -- warn: - lhs: GHC.Stack.HasCallStack - note: ! '''HasCallStack'' is already exported from Relude' - name: Use 'HasCallStack' from Relude - rhs: HasCallStack -- warn: - lhs: GHC.Stack.callStack - note: ! '''callStack'' is already exported from Relude' - name: Use 'callStack' from Relude - rhs: callStack -- warn: - lhs: GHC.Stack.currentCallStack - note: ! '''currentCallStack'' is already exported from Relude' - name: Use 'currentCallStack' from Relude - rhs: currentCallStack -- warn: - lhs: GHC.Stack.getCallStack - note: ! '''getCallStack'' is already exported from Relude' - name: Use 'getCallStack' from Relude - rhs: getCallStack -- warn: - lhs: GHC.Stack.prettyCallStack - note: ! '''prettyCallStack'' is already exported from Relude' - name: Use 'prettyCallStack' from Relude - rhs: prettyCallStack -- warn: - lhs: GHC.Stack.prettySrcLoc - note: ! '''prettySrcLoc'' is already exported from Relude' - name: Use 'prettySrcLoc' from Relude - rhs: prettySrcLoc -- warn: - lhs: GHC.Stack.withFrozenCallStack - note: ! '''withFrozenCallStack'' is already exported from Relude' - name: Use 'withFrozenCallStack' from Relude - rhs: withFrozenCallStack -- warn: - lhs: Data.Bifoldable.Bifoldable - note: ! '''Bifoldable'' is already exported from Relude' - name: Use 'Bifoldable' from Relude - rhs: Bifoldable -- warn: - lhs: Data.Bifoldable.bifold - note: ! '''bifold'' is already exported from Relude' - name: Use 'bifold' from Relude - rhs: bifold -- warn: - lhs: Data.Bifoldable.bifoldMap - note: ! '''bifoldMap'' is already exported from Relude' - name: Use 'bifoldMap' from Relude - rhs: bifoldMap -- warn: - lhs: Data.Bifoldable.bifoldr - note: ! '''bifoldr'' is already exported from Relude' - name: Use 'bifoldr' from Relude - rhs: bifoldr -- warn: - lhs: Data.Bifoldable.bifoldl - note: ! '''bifoldl'' is already exported from Relude' - name: Use 'bifoldl' from Relude - rhs: bifoldl -- warn: - lhs: Data.Bifoldable.bifoldl' - note: ! '''bifoldl'''' is already exported from Relude' - name: Use 'bifoldl'' from Relude - rhs: bifoldl' -- warn: - lhs: Data.Bifoldable.bifoldlM - note: ! '''bifoldlM'' is already exported from Relude' - name: Use 'bifoldlM' from Relude - rhs: bifoldlM -- warn: - lhs: Data.Bifoldable.bifoldr' - note: ! '''bifoldr'''' is already exported from Relude' - name: Use 'bifoldr'' from Relude - rhs: bifoldr' -- warn: - lhs: Data.Bifoldable.bifoldrM - note: ! '''bifoldrM'' is already exported from Relude' - name: Use 'bifoldrM' from Relude - rhs: bifoldrM -- warn: - lhs: Data.Bifoldable.bitraverse_ - note: ! '''bitraverse_'' is already exported from Relude' - name: Use 'bitraverse_' from Relude - rhs: bitraverse_ -- warn: - lhs: Data.Bifoldable.bifor_ - note: ! '''bifor_'' is already exported from Relude' - name: Use 'bifor_' from Relude - rhs: bifor_ -- warn: - lhs: Data.Bifoldable.biasum - note: ! '''biasum'' is already exported from Relude' - name: Use 'biasum' from Relude - rhs: biasum -- warn: - lhs: Data.Bifoldable.bisequence_ - note: ! '''bisequence_'' is already exported from Relude' - name: Use 'bisequence_' from Relude - rhs: bisequence_ -- warn: - lhs: Data.Bifoldable.biList - note: ! '''biList'' is already exported from Relude' - name: Use 'biList' from Relude - rhs: biList -- warn: - lhs: Data.Bifoldable.binull - note: ! '''binull'' is already exported from Relude' - name: Use 'binull' from Relude - rhs: binull -- warn: - lhs: Data.Bifoldable.bilength - note: ! '''bilength'' is already exported from Relude' - name: Use 'bilength' from Relude - rhs: bilength -- warn: - lhs: Data.Bifoldable.bielem - note: ! '''bielem'' is already exported from Relude' - name: Use 'bielem' from Relude - rhs: bielem -- warn: - lhs: Data.Bifoldable.biand - note: ! '''biand'' is already exported from Relude' - name: Use 'biand' from Relude - rhs: biand -- warn: - lhs: Data.Bifoldable.bior - note: ! '''bior'' is already exported from Relude' - name: Use 'bior' from Relude - rhs: bior -- warn: - lhs: Data.Bifoldable.biany - note: ! '''biany'' is already exported from Relude' - name: Use 'biany' from Relude - rhs: biany -- warn: - lhs: Data.Bifoldable.biall - note: ! '''biall'' is already exported from Relude' - name: Use 'biall' from Relude - rhs: biall -- warn: - lhs: Data.Bifoldable.bifind - note: ! '''bifind'' is already exported from Relude' - name: Use 'bifind' from Relude - rhs: bifind -- warn: - lhs: Data.Bitraversable.Bitraversable - note: ! '''Bitraversable'' is already exported from Relude' - name: Use 'Bitraversable' from Relude - rhs: Bitraversable -- warn: - lhs: Data.Bitraversable.bitraverse - note: ! '''bitraverse'' is already exported from Relude' - name: Use 'bitraverse' from Relude - rhs: bitraverse -- warn: - lhs: Data.Bitraversable.bisequence - note: ! '''bisequence'' is already exported from Relude' - name: Use 'bisequence' from Relude - rhs: bisequence -- warn: - lhs: Data.Bitraversable.bifor - note: ! '''bifor'' is already exported from Relude' - name: Use 'bifor' from Relude - rhs: bifor -- warn: - lhs: Data.Bitraversable.bimapDefault - note: ! '''bimapDefault'' is already exported from Relude' - name: Use 'bimapDefault' from Relude - rhs: bimapDefault -- warn: - lhs: Data.Bitraversable.bifoldMapDefault - note: ! '''bifoldMapDefault'' is already exported from Relude' - name: Use 'bifoldMapDefault' from Relude - rhs: bifoldMapDefault -- warn: - lhs: Control.Monad.guard - note: ! '''guard'' is already exported from Relude' - name: Use 'guard' from Relude - rhs: guard -- warn: - lhs: Control.Monad.unless - note: ! '''unless'' is already exported from Relude' - name: Use 'unless' from Relude - rhs: unless -- warn: - lhs: Control.Monad.when - note: ! '''when'' is already exported from Relude' - name: Use 'when' from Relude - rhs: when -- warn: - lhs: Data.Bool.bool - note: ! '''bool'' is already exported from Relude' - name: Use 'bool' from Relude - rhs: bool -- warn: - lhs: Data.Hashable.Hashable - note: ! '''Hashable'' is already exported from Relude' - name: Use 'Hashable' from Relude - rhs: Hashable -- warn: - lhs: Data.Hashable.hashWithSalt - note: ! '''hashWithSalt'' is already exported from Relude' - name: Use 'hashWithSalt' from Relude - rhs: hashWithSalt -- warn: - lhs: Data.HashMap.Strict.HashMap - note: ! '''HashMap'' is already exported from Relude' - name: Use 'HashMap' from Relude - rhs: HashMap -- warn: - lhs: Data.HashSet.HashSet - note: ! '''HashSet'' is already exported from Relude' - name: Use 'HashSet' from Relude - rhs: HashSet -- warn: - lhs: Data.IntMap.Strict.IntMap - note: ! '''IntMap'' is already exported from Relude' - name: Use 'IntMap' from Relude - rhs: IntMap -- warn: - lhs: Data.IntSet.IntSet - note: ! '''IntSet'' is already exported from Relude' - name: Use 'IntSet' from Relude - rhs: IntSet -- warn: - lhs: Data.Map.Strict.Map - note: ! '''Map'' is already exported from Relude' - name: Use 'Map' from Relude - rhs: Map -- warn: - lhs: Data.Sequence.Sequence - note: ! '''Sequence'' is already exported from Relude' - name: Use 'Sequence' from Relude - rhs: Sequence -- warn: - lhs: Data.Set.Set - note: ! '''Set'' is already exported from Relude' - name: Use 'Set' from Relude - rhs: Set -- warn: - lhs: Data.Tuple.swap - note: ! '''swap'' is already exported from Relude' - name: Use 'swap' from Relude - rhs: swap -- warn: - lhs: Data.Vector.Vector - note: ! '''Vector'' is already exported from Relude' - name: Use 'Vector' from Relude - rhs: Vector -- warn: - lhs: GHC.Exts.IsList - note: ! '''IsList'' is already exported from Relude' - name: Use 'IsList' from Relude - rhs: IsList -- warn: - lhs: GHC.Exts.fromList - note: ! '''fromList'' is already exported from Relude' - name: Use 'fromList' from Relude - rhs: fromList -- warn: - lhs: GHC.Exts.fromListN - note: ! '''fromListN'' is already exported from Relude' - name: Use 'fromListN' from Relude - rhs: fromListN -- warn: - lhs: Debug.Trace.trace - note: ! '''trace'' is already exported from Relude' - name: Use 'trace' from Relude - rhs: trace -- warn: - lhs: Debug.Trace.traceShow - note: ! '''traceShow'' is already exported from Relude' - name: Use 'traceShow' from Relude - rhs: traceShow -- warn: - lhs: Debug.Trace.traceShowId - note: ! '''traceShowId'' is already exported from Relude' - name: Use 'traceShowId' from Relude - rhs: traceShowId -- warn: - lhs: Debug.Trace.traceShowM - note: ! '''traceShowM'' is already exported from Relude' - name: Use 'traceShowM' from Relude - rhs: traceShowM -- warn: - lhs: Debug.Trace.traceM - note: ! '''traceM'' is already exported from Relude' - name: Use 'traceM' from Relude - rhs: traceM -- warn: - lhs: Debug.Trace.traceId - note: ! '''traceId'' is already exported from Relude' - name: Use 'traceId' from Relude - rhs: traceId -- warn: - lhs: Control.DeepSeq.NFData - note: ! '''NFData'' is already exported from Relude' - name: Use 'NFData' from Relude - rhs: NFData -- warn: - lhs: Control.DeepSeq.rnf - note: ! '''rnf'' is already exported from Relude' - name: Use 'rnf' from Relude - rhs: rnf -- warn: - lhs: Control.DeepSeq.deepseq - note: ! '''deepseq'' is already exported from Relude' - name: Use 'deepseq' from Relude - rhs: deepseq -- warn: - lhs: Control.DeepSeq.force - note: ! '''force'' is already exported from Relude' - name: Use 'force' from Relude - rhs: force -- warn: - lhs: (Control.DeepSeq.$!!) - note: Operator '($!!)' is already exported from Relude - name: Use '$!!' from Relude - rhs: ($!!) -- warn: - lhs: Control.Exception.Exception - note: ! '''Exception'' is already exported from Relude' - name: Use 'Exception' from Relude - rhs: Exception -- warn: - lhs: Control.Exception.SomeException - note: ! '''SomeException'' is already exported from Relude' - name: Use 'SomeException' from Relude - rhs: SomeException -- warn: - lhs: Control.Exception.toException - note: ! '''toException'' is already exported from Relude' - name: Use 'toException' from Relude - rhs: toException -- warn: - lhs: Control.Exception.fromException - note: ! '''fromException'' is already exported from Relude' - name: Use 'fromException' from Relude - rhs: fromException -- warn: - lhs: Control.Exception.displayException - note: ! '''displayException'' is already exported from Relude' - name: Use 'displayException' from Relude - rhs: displayException -- warn: - lhs: Data.Foldable.asum - note: ! '''asum'' is already exported from Relude' - name: Use 'asum' from Relude - rhs: asum -- warn: - lhs: Data.Foldable.find - note: ! '''find'' is already exported from Relude' - name: Use 'find' from Relude - rhs: find -- warn: - lhs: Data.Foldable.find - note: ! '''find'' is already exported from Relude' - name: Use 'find' from Relude - rhs: find -- warn: - lhs: Data.Foldable.fold - note: ! '''fold'' is already exported from Relude' - name: Use 'fold' from Relude - rhs: fold -- warn: - lhs: Data.Foldable.foldl' - note: ! '''foldl'''' is already exported from Relude' - name: Use 'foldl'' from Relude - rhs: foldl' -- warn: - lhs: Data.Foldable.foldrM - note: ! '''foldrM'' is already exported from Relude' - name: Use 'foldrM' from Relude - rhs: foldrM -- warn: - lhs: Data.Foldable.forM_ - note: ! '''forM_'' is already exported from Relude' - name: Use 'forM_' from Relude - rhs: forM_ -- warn: - lhs: Data.Foldable.for_ - note: ! '''for_'' is already exported from Relude' - name: Use 'for_' from Relude - rhs: for_ -- warn: - lhs: Data.Foldable.sequenceA_ - note: ! '''sequenceA_'' is already exported from Relude' - name: Use 'sequenceA_' from Relude - rhs: sequenceA_ -- warn: - lhs: Data.Foldable.toList - note: ! '''toList'' is already exported from Relude' - name: Use 'toList' from Relude - rhs: toList -- warn: - lhs: Data.Foldable.traverse_ - note: ! '''traverse_'' is already exported from Relude' - name: Use 'traverse_' from Relude - rhs: traverse_ -- warn: - lhs: Data.Traversable.forM - note: ! '''forM'' is already exported from Relude' - name: Use 'forM' from Relude - rhs: forM -- warn: - lhs: Data.Traversable.mapAccumL - note: ! '''mapAccumL'' is already exported from Relude' - name: Use 'mapAccumL' from Relude - rhs: mapAccumL -- warn: - lhs: Data.Traversable.mapAccumR - note: ! '''mapAccumR'' is already exported from Relude' - name: Use 'mapAccumR' from Relude - rhs: mapAccumR -- warn: - lhs: (Control.Arrow.&&&) - note: Operator '(&&&)' is already exported from Relude - name: Use '&&&' from Relude - rhs: (&&&) -- warn: - lhs: (Control.Category.>>>) - note: Operator '(>>>)' is already exported from Relude - name: Use '>>>' from Relude - rhs: (>>>) -- warn: - lhs: (Control.Category.<<<) - note: Operator '(<<<)' is already exported from Relude - name: Use '<<<' from Relude - rhs: (<<<) -- warn: - lhs: Data.Function.fix - note: ! '''fix'' is already exported from Relude' - name: Use 'fix' from Relude - rhs: fix -- warn: - lhs: Data.Function.on - note: ! '''on'' is already exported from Relude' - name: Use 'on' from Relude - rhs: 'on' -- warn: - lhs: Data.Bifunctor.Bifunctor - note: ! '''Bifunctor'' is already exported from Relude' - name: Use 'Bifunctor' from Relude - rhs: Bifunctor -- warn: - lhs: Data.Bifunctor.bimap - note: ! '''bimap'' is already exported from Relude' - name: Use 'bimap' from Relude - rhs: bimap -- warn: - lhs: Data.Bifunctor.first - note: ! '''first'' is already exported from Relude' - name: Use 'first' from Relude - rhs: first -- warn: - lhs: Data.Bifunctor.second - note: ! '''second'' is already exported from Relude' - name: Use 'second' from Relude - rhs: second -- warn: - lhs: Data.Functor.void - note: ! '''void'' is already exported from Relude' - name: Use 'void' from Relude - rhs: void -- warn: - lhs: (Data.Functor.$>) - note: Operator '($>)' is already exported from Relude - name: Use '$>' from Relude - rhs: ($>) -- warn: - lhs: (Data.Functor.<&>) - note: Operator '(<&>)' is already exported from Relude - name: Use '<&>' from Relude - rhs: (<&>) -- warn: - lhs: Data.Functor.Compose.Compose - note: ! '''Compose'' is already exported from Relude' - name: Use 'Compose' from Relude - rhs: Compose -- warn: - lhs: Data.Functor.Compose.getCompose - note: ! '''getCompose'' is already exported from Relude' - name: Use 'getCompose' from Relude - rhs: getCompose -- warn: - lhs: Data.Functor.Identity.Identity - note: ! '''Identity'' is already exported from Relude' - name: Use 'Identity' from Relude - rhs: Identity -- warn: - lhs: Data.Functor.Identity.runIdentity - note: ! '''runIdentity'' is already exported from Relude' - name: Use 'runIdentity' from Relude - rhs: runIdentity -- warn: - lhs: Control.Concurrent.MVar.MVar - note: ! '''MVar'' is already exported from Relude' - name: Use 'MVar' from Relude - rhs: MVar -- warn: - lhs: Control.Concurrent.MVar.newEmptyMVar - note: ! '''newEmptyMVar'' is already exported from Relude' - name: Use 'newEmptyMVar' from Relude - rhs: newEmptyMVar -- warn: - lhs: Control.Concurrent.MVar.newMVar - note: ! '''newMVar'' is already exported from Relude' - name: Use 'newMVar' from Relude - rhs: newMVar -- warn: - lhs: Control.Concurrent.MVar.putMVar - note: ! '''putMVar'' is already exported from Relude' - name: Use 'putMVar' from Relude - rhs: putMVar -- warn: - lhs: Control.Concurrent.MVar.readMVar - note: ! '''readMVar'' is already exported from Relude' - name: Use 'readMVar' from Relude - rhs: readMVar -- warn: - lhs: Control.Concurrent.MVar.swapMVar - note: ! '''swapMVar'' is already exported from Relude' - name: Use 'swapMVar' from Relude - rhs: swapMVar -- warn: - lhs: Control.Concurrent.MVar.takeMVar - note: ! '''takeMVar'' is already exported from Relude' - name: Use 'takeMVar' from Relude - rhs: takeMVar -- warn: - lhs: Control.Concurrent.MVar.tryPutMVar - note: ! '''tryPutMVar'' is already exported from Relude' - name: Use 'tryPutMVar' from Relude - rhs: tryPutMVar -- warn: - lhs: Control.Concurrent.MVar.tryReadMVar - note: ! '''tryReadMVar'' is already exported from Relude' - name: Use 'tryReadMVar' from Relude - rhs: tryReadMVar -- warn: - lhs: Control.Concurrent.MVar.tryTakeMVar - note: ! '''tryTakeMVar'' is already exported from Relude' - name: Use 'tryTakeMVar' from Relude - rhs: tryTakeMVar -- warn: - lhs: Control.Monad.STM.STM - note: ! '''STM'' is already exported from Relude' - name: Use 'STM' from Relude - rhs: STM -- warn: - lhs: Control.Monad.STM.atomically - note: ! '''atomically'' is already exported from Relude' - name: Use 'atomically' from Relude - rhs: atomically -- warn: - lhs: Control.Concurrent.STM.TVar.TVar - note: ! '''TVar'' is already exported from Relude' - name: Use 'TVar' from Relude - rhs: TVar -- warn: - lhs: Control.Concurrent.STM.TVar.newTVarIO - note: ! '''newTVarIO'' is already exported from Relude' - name: Use 'newTVarIO' from Relude - rhs: newTVarIO -- warn: - lhs: Control.Concurrent.STM.TVar.readTVarIO - note: ! '''readTVarIO'' is already exported from Relude' - name: Use 'readTVarIO' from Relude - rhs: readTVarIO -- warn: - lhs: Control.Concurrent.STM.TVar.modifyTVar' - note: ! '''modifyTVar'''' is already exported from Relude' - name: Use 'modifyTVar'' from Relude - rhs: modifyTVar' -- warn: - lhs: Control.Concurrent.STM.TVar.newTVar - note: ! '''newTVar'' is already exported from Relude' - name: Use 'newTVar' from Relude - rhs: newTVar -- warn: - lhs: Control.Concurrent.STM.TVar.readTVar - note: ! '''readTVar'' is already exported from Relude' - name: Use 'readTVar' from Relude - rhs: readTVar -- warn: - lhs: Control.Concurrent.STM.TVar.writeTVar - note: ! '''writeTVar'' is already exported from Relude' - name: Use 'writeTVar' from Relude - rhs: writeTVar -- warn: - lhs: Data.IORef.IORef - note: ! '''IORef'' is already exported from Relude' - name: Use 'IORef' from Relude - rhs: IORef -- warn: - lhs: Data.IORef.atomicModifyIORef - note: ! '''atomicModifyIORef'' is already exported from Relude' - name: Use 'atomicModifyIORef' from Relude - rhs: atomicModifyIORef -- warn: - lhs: Data.IORef.atomicModifyIORef' - note: ! '''atomicModifyIORef'''' is already exported from Relude' - name: Use 'atomicModifyIORef'' from Relude - rhs: atomicModifyIORef' -- warn: - lhs: Data.IORef.atomicWriteIORef - note: ! '''atomicWriteIORef'' is already exported from Relude' - name: Use 'atomicWriteIORef' from Relude - rhs: atomicWriteIORef -- warn: - lhs: Data.IORef.modifyIORef - note: ! '''modifyIORef'' is already exported from Relude' - name: Use 'modifyIORef' from Relude - rhs: modifyIORef -- warn: - lhs: Data.IORef.modifyIORef' - note: ! '''modifyIORef'''' is already exported from Relude' - name: Use 'modifyIORef'' from Relude - rhs: modifyIORef' -- warn: - lhs: Data.IORef.newIORef - note: ! '''newIORef'' is already exported from Relude' - name: Use 'newIORef' from Relude - rhs: newIORef -- warn: - lhs: Data.IORef.readIORef - note: ! '''readIORef'' is already exported from Relude' - name: Use 'readIORef' from Relude - rhs: readIORef -- warn: - lhs: Data.IORef.writeIORef - note: ! '''writeIORef'' is already exported from Relude' - name: Use 'writeIORef' from Relude - rhs: writeIORef -- warn: - lhs: Data.Text.IO.getLine - note: ! '''getLine'' is already exported from Relude' - name: Use 'getLine' from Relude - rhs: getLine -- warn: - lhs: Data.List.genericDrop - note: ! '''genericDrop'' is already exported from Relude' - name: Use 'genericDrop' from Relude - rhs: genericDrop -- warn: - lhs: Data.List.genericLength - note: ! '''genericLength'' is already exported from Relude' - name: Use 'genericLength' from Relude - rhs: genericLength -- warn: - lhs: Data.List.genericReplicate - note: ! '''genericReplicate'' is already exported from Relude' - name: Use 'genericReplicate' from Relude - rhs: genericReplicate -- warn: - lhs: Data.List.genericSplitAt - note: ! '''genericSplitAt'' is already exported from Relude' - name: Use 'genericSplitAt' from Relude - rhs: genericSplitAt -- warn: - lhs: Data.List.genericTake - note: ! '''genericTake'' is already exported from Relude' - name: Use 'genericTake' from Relude - rhs: genericTake -- warn: - lhs: Data.List.group - note: ! '''group'' is already exported from Relude' - name: Use 'group' from Relude - rhs: group -- warn: - lhs: Data.List.inits - note: ! '''inits'' is already exported from Relude' - name: Use 'inits' from Relude - rhs: inits -- warn: - lhs: Data.List.intercalate - note: ! '''intercalate'' is already exported from Relude' - name: Use 'intercalate' from Relude - rhs: intercalate -- warn: - lhs: Data.List.intersperse - note: ! '''intersperse'' is already exported from Relude' - name: Use 'intersperse' from Relude - rhs: intersperse -- warn: - lhs: Data.List.isPrefixOf - note: ! '''isPrefixOf'' is already exported from Relude' - name: Use 'isPrefixOf' from Relude - rhs: isPrefixOf -- warn: - lhs: Data.List.permutations - note: ! '''permutations'' is already exported from Relude' - name: Use 'permutations' from Relude - rhs: permutations -- warn: - lhs: Data.List.sort - note: ! '''sort'' is already exported from Relude' - name: Use 'sort' from Relude - rhs: sort -- warn: - lhs: Data.List.sortBy - note: ! '''sortBy'' is already exported from Relude' - name: Use 'sortBy' from Relude - rhs: sortBy -- warn: - lhs: Data.List.sortOn - note: ! '''sortOn'' is already exported from Relude' - name: Use 'sortOn' from Relude - rhs: sortOn -- warn: - lhs: Data.List.subsequences - note: ! '''subsequences'' is already exported from Relude' - name: Use 'subsequences' from Relude - rhs: subsequences -- warn: - lhs: Data.List.tails - note: ! '''tails'' is already exported from Relude' - name: Use 'tails' from Relude - rhs: tails -- warn: - lhs: Data.List.transpose - note: ! '''transpose'' is already exported from Relude' - name: Use 'transpose' from Relude - rhs: transpose -- warn: - lhs: Data.List.uncons - note: ! '''uncons'' is already exported from Relude' - name: Use 'uncons' from Relude - rhs: uncons -- warn: - lhs: Data.List.unfoldr - note: ! '''unfoldr'' is already exported from Relude' - name: Use 'unfoldr' from Relude - rhs: unfoldr -- warn: - lhs: Data.NonEmpty.NonEmpty - note: ! '''NonEmpty'' is already exported from Relude' - name: Use 'NonEmpty' from Relude - rhs: NonEmpty -- warn: - lhs: (Data.NonEmpty.:|) - note: Operator '(:|)' is already exported from Relude - name: Use ':|' from Relude - rhs: (:|) -- warn: - lhs: Data.NonEmpty.nonEmpty - note: ! '''nonEmpty'' is already exported from Relude' - name: Use 'nonEmpty' from Relude - rhs: nonEmpty -- warn: - lhs: Data.NonEmpty.head - note: ! '''head'' is already exported from Relude' - name: Use 'head' from Relude - rhs: head -- warn: - lhs: Data.NonEmpty.init - note: ! '''init'' is already exported from Relude' - name: Use 'init' from Relude - rhs: init -- warn: - lhs: Data.NonEmpty.last - note: ! '''last'' is already exported from Relude' - name: Use 'last' from Relude - rhs: last -- warn: - lhs: Data.NonEmpty.tail - note: ! '''tail'' is already exported from Relude' - name: Use 'tail' from Relude - rhs: tail -- warn: - lhs: GHC.Exts.sortWith - note: ! '''sortWith'' is already exported from Relude' - name: Use 'sortWith' from Relude - rhs: sortWith -- warn: - lhs: Control.Monad.Except.ExceptT - note: ! '''ExceptT'' is already exported from Relude' - name: Use 'ExceptT' from Relude - rhs: ExceptT -- warn: - lhs: Control.Monad.Except.runExceptT - note: ! '''runExceptT'' is already exported from Relude' - name: Use 'runExceptT' from Relude - rhs: runExceptT -- warn: - lhs: Control.Monad.Reader.MonadReader - note: ! '''MonadReader'' is already exported from Relude' - name: Use 'MonadReader' from Relude - rhs: MonadReader -- warn: - lhs: Control.Monad.Reader.Reader - note: ! '''Reader'' is already exported from Relude' - name: Use 'Reader' from Relude - rhs: Reader -- warn: - lhs: Control.Monad.Reader.ReaderT - note: ! '''ReaderT'' is already exported from Relude' - name: Use 'ReaderT' from Relude - rhs: ReaderT -- warn: - lhs: Control.Monad.Reader.runReaderT - note: ! '''runReaderT'' is already exported from Relude' - name: Use 'runReaderT' from Relude - rhs: runReaderT -- warn: - lhs: Control.Monad.Reader.ask - note: ! '''ask'' is already exported from Relude' - name: Use 'ask' from Relude - rhs: ask -- warn: - lhs: Control.Monad.Reader.asks - note: ! '''asks'' is already exported from Relude' - name: Use 'asks' from Relude - rhs: asks -- warn: - lhs: Control.Monad.Reader.local - note: ! '''local'' is already exported from Relude' - name: Use 'local' from Relude - rhs: local -- warn: - lhs: Control.Monad.Reader.reader - note: ! '''reader'' is already exported from Relude' - name: Use 'reader' from Relude - rhs: reader -- warn: - lhs: Control.Monad.Reader.runReader - note: ! '''runReader'' is already exported from Relude' - name: Use 'runReader' from Relude - rhs: runReader -- warn: - lhs: Control.Monad.Reader.withReader - note: ! '''withReader'' is already exported from Relude' - name: Use 'withReader' from Relude - rhs: withReader -- warn: - lhs: Control.Monad.Reader.withReaderT - note: ! '''withReaderT'' is already exported from Relude' - name: Use 'withReaderT' from Relude - rhs: withReaderT -- warn: - lhs: Control.Monad.State.Strict.MonadState - note: ! '''MonadState'' is already exported from Relude' - name: Use 'MonadState' from Relude - rhs: MonadState -- warn: - lhs: Control.Monad.State.Strict.State - note: ! '''State'' is already exported from Relude' - name: Use 'State' from Relude - rhs: State -- warn: - lhs: Control.Monad.State.Strict.StateT - note: ! '''StateT'' is already exported from Relude' - name: Use 'StateT' from Relude - rhs: StateT -- warn: - lhs: Control.Monad.State.Strict.runStateT - note: ! '''runStateT'' is already exported from Relude' - name: Use 'runStateT' from Relude - rhs: runStateT -- warn: - lhs: Control.Monad.State.Strict.evalState - note: ! '''evalState'' is already exported from Relude' - name: Use 'evalState' from Relude - rhs: evalState -- warn: - lhs: Control.Monad.State.Strict.evalStateT - note: ! '''evalStateT'' is already exported from Relude' - name: Use 'evalStateT' from Relude - rhs: evalStateT -- warn: - lhs: Control.Monad.State.Strict.execState - note: ! '''execState'' is already exported from Relude' - name: Use 'execState' from Relude - rhs: execState -- warn: - lhs: Control.Monad.State.Strict.execStateT - note: ! '''execStateT'' is already exported from Relude' - name: Use 'execStateT' from Relude - rhs: execStateT -- warn: - lhs: Control.Monad.State.Strict.get - note: ! '''get'' is already exported from Relude' - name: Use 'get' from Relude - rhs: get -- warn: - lhs: Control.Monad.State.Strict.gets - note: ! '''gets'' is already exported from Relude' - name: Use 'gets' from Relude - rhs: gets -- warn: - lhs: Control.Monad.State.Strict.modify - note: ! '''modify'' is already exported from Relude' - name: Use 'modify' from Relude - rhs: modify -- warn: - lhs: Control.Monad.State.Strict.modify' - note: ! '''modify'''' is already exported from Relude' - name: Use 'modify'' from Relude - rhs: modify' -- warn: - lhs: Control.Monad.State.Strict.put - note: ! '''put'' is already exported from Relude' - name: Use 'put' from Relude - rhs: put -- warn: - lhs: Control.Monad.State.Strict.runState - note: ! '''runState'' is already exported from Relude' - name: Use 'runState' from Relude - rhs: runState -- warn: - lhs: Control.Monad.State.Strict.state - note: ! '''state'' is already exported from Relude' - name: Use 'state' from Relude - rhs: state -- warn: - lhs: Control.Monad.State.Strict.withState - note: ! '''withState'' is already exported from Relude' - name: Use 'withState' from Relude - rhs: withState -- warn: - lhs: Control.Monad.Trans.MonadIO - note: ! '''MonadIO'' is already exported from Relude' - name: Use 'MonadIO' from Relude - rhs: MonadIO -- warn: - lhs: Control.Monad.Trans.MonadTrans - note: ! '''MonadTrans'' is already exported from Relude' - name: Use 'MonadTrans' from Relude - rhs: MonadTrans -- warn: - lhs: Control.Monad.Trans.lift - note: ! '''lift'' is already exported from Relude' - name: Use 'lift' from Relude - rhs: lift -- warn: - lhs: Control.Monad.Trans.liftIO - note: ! '''liftIO'' is already exported from Relude' - name: Use 'liftIO' from Relude - rhs: liftIO -- warn: - lhs: Control.Monad.Trans.Identity.IdentityT - note: ! '''IdentityT'' is already exported from Relude' - name: Use 'IdentityT' from Relude - rhs: IdentityT -- warn: - lhs: Control.Monad.Trans.Identity.runIdentityT - note: ! '''runIdentityT'' is already exported from Relude' - name: Use 'runIdentityT' from Relude - rhs: runIdentityT -- warn: - lhs: Control.Monad.Trans.Maybe.MaybeT - note: ! '''MaybeT'' is already exported from Relude' - name: Use 'MaybeT' from Relude - rhs: MaybeT -- warn: - lhs: Control.Monad.Trans.Maybe.maybeToExceptT - note: ! '''maybeToExceptT'' is already exported from Relude' - name: Use 'maybeToExceptT' from Relude - rhs: maybeToExceptT -- warn: - lhs: Control.Monad.Trans.Maybe.exceptToMaybeT - note: ! '''exceptToMaybeT'' is already exported from Relude' - name: Use 'exceptToMaybeT' from Relude - rhs: exceptToMaybeT -- warn: - lhs: Control.Monad.MonadPlus - note: ! '''MonadPlus'' is already exported from Relude' - name: Use 'MonadPlus' from Relude - rhs: MonadPlus -- warn: - lhs: Control.Monad.mzero - note: ! '''mzero'' is already exported from Relude' - name: Use 'mzero' from Relude - rhs: mzero -- warn: - lhs: Control.Monad.mplus - note: ! '''mplus'' is already exported from Relude' - name: Use 'mplus' from Relude - rhs: mplus -- warn: - lhs: Control.Monad.filterM - note: ! '''filterM'' is already exported from Relude' - name: Use 'filterM' from Relude - rhs: filterM -- warn: - lhs: Control.Monad.forever - note: ! '''forever'' is already exported from Relude' - name: Use 'forever' from Relude - rhs: forever -- warn: - lhs: Control.Monad.join - note: ! '''join'' is already exported from Relude' - name: Use 'join' from Relude - rhs: join -- warn: - lhs: Control.Monad.mapAndUnzipM - note: ! '''mapAndUnzipM'' is already exported from Relude' - name: Use 'mapAndUnzipM' from Relude - rhs: mapAndUnzipM -- warn: - lhs: Control.Monad.mfilter - note: ! '''mfilter'' is already exported from Relude' - name: Use 'mfilter' from Relude - rhs: mfilter -- warn: - lhs: Control.Monad.replicateM - note: ! '''replicateM'' is already exported from Relude' - name: Use 'replicateM' from Relude - rhs: replicateM -- warn: - lhs: Control.Monad.replicateM_ - note: ! '''replicateM_'' is already exported from Relude' - name: Use 'replicateM_' from Relude - rhs: replicateM_ -- warn: - lhs: Control.Monad.zipWithM - note: ! '''zipWithM'' is already exported from Relude' - name: Use 'zipWithM' from Relude - rhs: zipWithM -- warn: - lhs: Control.Monad.zipWithM_ - note: ! '''zipWithM_'' is already exported from Relude' - name: Use 'zipWithM_' from Relude - rhs: zipWithM_ -- warn: - lhs: (Control.Monad.<$!>) - note: Operator '(<$!>)' is already exported from Relude - name: Use '<$!>' from Relude - rhs: (<$!>) -- warn: - lhs: (Control.Monad.<=<) - note: Operator '(<=<)' is already exported from Relude - name: Use '<=<' from Relude - rhs: (<=<) -- warn: - lhs: (Control.Monad.=<<) - note: Operator '(=<<)' is already exported from Relude - name: Use '=<<' from Relude - rhs: (=<<) -- warn: - lhs: (Control.Monad.>=>) - note: Operator '(>=>)' is already exported from Relude - name: Use '>=>' from Relude - rhs: (>=>) -- warn: - lhs: Control.Monad.Fail.MonadFail - note: ! '''MonadFail'' is already exported from Relude' - name: Use 'MonadFail' from Relude - rhs: MonadFail -- warn: - lhs: Data.Maybe.catMaybes - note: ! '''catMaybes'' is already exported from Relude' - name: Use 'catMaybes' from Relude - rhs: catMaybes -- warn: - lhs: Data.Maybe.fromMaybe - note: ! '''fromMaybe'' is already exported from Relude' - name: Use 'fromMaybe' from Relude - rhs: fromMaybe -- warn: - lhs: Data.Maybe.isJust - note: ! '''isJust'' is already exported from Relude' - name: Use 'isJust' from Relude - rhs: isJust -- warn: - lhs: Data.Maybe.isNothing - note: ! '''isNothing'' is already exported from Relude' - name: Use 'isNothing' from Relude - rhs: isNothing -- warn: - lhs: Data.Maybe.listToMaybe - note: ! '''listToMaybe'' is already exported from Relude' - name: Use 'listToMaybe' from Relude - rhs: listToMaybe -- warn: - lhs: Data.Maybe.mapMaybe - note: ! '''mapMaybe'' is already exported from Relude' - name: Use 'mapMaybe' from Relude - rhs: mapMaybe -- warn: - lhs: Data.Maybe.maybeToList - note: ! '''maybeToList'' is already exported from Relude' - name: Use 'maybeToList' from Relude - rhs: maybeToList -- warn: - lhs: Data.Either.isLeft - note: ! '''isLeft'' is already exported from Relude' - name: Use 'isLeft' from Relude - rhs: isLeft -- warn: - lhs: Data.Either.isRight - note: ! '''isRight'' is already exported from Relude' - name: Use 'isRight' from Relude - rhs: isRight -- warn: - lhs: Data.Either.lefts - note: ! '''lefts'' is already exported from Relude' - name: Use 'lefts' from Relude - rhs: lefts -- warn: - lhs: Data.Either.partitionEithers - note: ! '''partitionEithers'' is already exported from Relude' - name: Use 'partitionEithers' from Relude - rhs: partitionEithers -- warn: - lhs: Data.Either.rights - note: ! '''rights'' is already exported from Relude' - name: Use 'rights' from Relude - rhs: rights -- warn: - lhs: Data.Monoid.All - note: ! '''All'' is already exported from Relude' - name: Use 'All' from Relude - rhs: All -- warn: - lhs: Data.Monoid.getAll - note: ! '''getAll'' is already exported from Relude' - name: Use 'getAll' from Relude - rhs: getAll -- warn: - lhs: Data.Monoid.Alt - note: ! '''Alt'' is already exported from Relude' - name: Use 'Alt' from Relude - rhs: Alt -- warn: - lhs: Data.Monoid.getAlt - note: ! '''getAlt'' is already exported from Relude' - name: Use 'getAlt' from Relude - rhs: getAlt -- warn: - lhs: Data.Monoid.Any - note: ! '''Any'' is already exported from Relude' - name: Use 'Any' from Relude - rhs: Any -- warn: - lhs: Data.Monoid.getAny - note: ! '''getAny'' is already exported from Relude' - name: Use 'getAny' from Relude - rhs: getAny -- warn: - lhs: Data.Monoid.Dual - note: ! '''Dual'' is already exported from Relude' - name: Use 'Dual' from Relude - rhs: Dual -- warn: - lhs: Data.Monoid.getDual - note: ! '''getDual'' is already exported from Relude' - name: Use 'getDual' from Relude - rhs: getDual -- warn: - lhs: Data.Monoid.Endo - note: ! '''Endo'' is already exported from Relude' - name: Use 'Endo' from Relude - rhs: Endo -- warn: - lhs: Data.Monoid.appEndo - note: ! '''appEndo'' is already exported from Relude' - name: Use 'appEndo' from Relude - rhs: appEndo -- warn: - lhs: Data.Monoid.First - note: ! '''First'' is already exported from Relude' - name: Use 'First' from Relude - rhs: First -- warn: - lhs: Data.Monoid.getFirst - note: ! '''getFirst'' is already exported from Relude' - name: Use 'getFirst' from Relude - rhs: getFirst -- warn: - lhs: Data.Monoid.Last - note: ! '''Last'' is already exported from Relude' - name: Use 'Last' from Relude - rhs: Last -- warn: - lhs: Data.Monoid.getLast - note: ! '''getLast'' is already exported from Relude' - name: Use 'getLast' from Relude - rhs: getLast -- warn: - lhs: Data.Monoid.Product - note: ! '''Product'' is already exported from Relude' - name: Use 'Product' from Relude - rhs: Product -- warn: - lhs: Data.Monoid.getProduct - note: ! '''getProduct'' is already exported from Relude' - name: Use 'getProduct' from Relude - rhs: getProduct -- warn: - lhs: Data.Monoid.Sum - note: ! '''Sum'' is already exported from Relude' - name: Use 'Sum' from Relude - rhs: Sum -- warn: - lhs: Data.Monoid.getSum - note: ! '''getSum'' is already exported from Relude' - name: Use 'getSum' from Relude - rhs: getSum -- warn: - lhs: Data.Semigroup.Option - note: ! '''Option'' is already exported from Relude' - name: Use 'Option' from Relude - rhs: Option -- warn: - lhs: Data.Semigroup.getOption - note: ! '''getOption'' is already exported from Relude' - name: Use 'getOption' from Relude - rhs: getOption -- warn: - lhs: Data.Semigroup.Semigroup - note: ! '''Semigroup'' is already exported from Relude' - name: Use 'Semigroup' from Relude - rhs: Semigroup -- warn: - lhs: Data.Semigroup.sconcat - note: ! '''sconcat'' is already exported from Relude' - name: Use 'sconcat' from Relude - rhs: sconcat -- warn: - lhs: Data.Semigroup.stimes - note: ! '''stimes'' is already exported from Relude' - name: Use 'stimes' from Relude - rhs: stimes -- warn: - lhs: (Data.Semigroup.<>) - note: Operator '(<>)' is already exported from Relude - name: Use '<>' from Relude - rhs: (<>) -- warn: - lhs: Data.Semigroup.WrappedMonoid - note: ! '''WrappedMonoid'' is already exported from Relude' - name: Use 'WrappedMonoid' from Relude - rhs: WrappedMonoid -- warn: - lhs: Data.Semigroup.cycle1 - note: ! '''cycle1'' is already exported from Relude' - name: Use 'cycle1' from Relude - rhs: cycle1 -- warn: - lhs: Data.Semigroup.mtimesDefault - note: ! '''mtimesDefault'' is already exported from Relude' - name: Use 'mtimesDefault' from Relude - rhs: mtimesDefault -- warn: - lhs: Data.Semigroup.stimesIdempotent - note: ! '''stimesIdempotent'' is already exported from Relude' - name: Use 'stimesIdempotent' from Relude - rhs: stimesIdempotent -- warn: - lhs: Data.Semigroup.stimesIdempotentMonoid - note: ! '''stimesIdempotentMonoid'' is already exported from Relude' - name: Use 'stimesIdempotentMonoid' from Relude - rhs: stimesIdempotentMonoid -- warn: - lhs: Data.Semigroup.stimesMonoid - note: ! '''stimesMonoid'' is already exported from Relude' - name: Use 'stimesMonoid' from Relude - rhs: stimesMonoid -- warn: - lhs: Data.ByteString.ByteString - note: ! '''ByteString'' is already exported from Relude' - name: Use 'ByteString' from Relude - rhs: ByteString -- warn: - lhs: Data.String.IsString - note: ! '''IsString'' is already exported from Relude' - name: Use 'IsString' from Relude - rhs: IsString -- warn: - lhs: Data.String.fromString - note: ! '''fromString'' is already exported from Relude' - name: Use 'fromString' from Relude - rhs: fromString -- warn: - lhs: Data.Text.Text - note: ! '''Text'' is already exported from Relude' - name: Use 'Text' from Relude - rhs: Text -- warn: - lhs: Data.Text.lines - note: ! '''lines'' is already exported from Relude' - name: Use 'lines' from Relude - rhs: lines -- warn: - lhs: Data.Text.unlines - note: ! '''unlines'' is already exported from Relude' - name: Use 'unlines' from Relude - rhs: unlines -- warn: - lhs: Data.Text.words - note: ! '''words'' is already exported from Relude' - name: Use 'words' from Relude - rhs: words -- warn: - lhs: Data.Text.unwords - note: ! '''unwords'' is already exported from Relude' - name: Use 'unwords' from Relude - rhs: unwords -- warn: - lhs: Data.Text.Encoding.decodeUtf8' - note: ! '''decodeUtf8'''' is already exported from Relude' - name: Use 'decodeUtf8'' from Relude - rhs: decodeUtf8' -- warn: - lhs: Data.Text.Encoding.decodeUtf8With - note: ! '''decodeUtf8With'' is already exported from Relude' - name: Use 'decodeUtf8With' from Relude - rhs: decodeUtf8With -- warn: - lhs: Data.Text.Encoding.Error.OnDecodeError - note: ! '''OnDecodeError'' is already exported from Relude' - name: Use 'OnDecodeError' from Relude - rhs: OnDecodeError -- warn: - lhs: Data.Text.Encoding.Error.OnError - note: ! '''OnError'' is already exported from Relude' - name: Use 'OnError' from Relude - rhs: OnError -- warn: - lhs: Data.Text.Encoding.Error.UnicodeException - note: ! '''UnicodeException'' is already exported from Relude' - name: Use 'UnicodeException' from Relude - rhs: UnicodeException -- warn: - lhs: Data.Text.Encoding.Error.lenientDecode - note: ! '''lenientDecode'' is already exported from Relude' - name: Use 'lenientDecode' from Relude - rhs: lenientDecode -- warn: - lhs: Data.Text.Encoding.Error.strictDecode - note: ! '''strictDecode'' is already exported from Relude' - name: Use 'strictDecode' from Relude - rhs: strictDecode -- warn: - lhs: Text.Read.Read - note: ! '''Read'' is already exported from Relude' - name: Use 'Read' from Relude - rhs: Read -- warn: - lhs: Text.Read.readMaybe - note: ! '''readMaybe'' is already exported from Relude' - name: Use 'readMaybe' from Relude - rhs: readMaybe -- warn: - lhs: (liftIO (newEmptyMVar )) - note: If you import 'newEmptyMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: newEmptyMVar -- warn: - lhs: (liftIO (newMVar x)) - note: If you import 'newMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: newMVar -- warn: - lhs: (liftIO (putMVar x y)) - note: If you import 'putMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putMVar -- warn: - lhs: (liftIO (readMVar x)) - note: If you import 'readMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readMVar -- warn: - lhs: (liftIO (swapMVar x y)) - note: If you import 'swapMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: swapMVar -- warn: - lhs: (liftIO (takeMVar x)) - note: If you import 'takeMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: takeMVar -- warn: - lhs: (liftIO (tryPutMVar x y)) - note: If you import 'tryPutMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: tryPutMVar -- warn: - lhs: (liftIO (tryReadMVar x)) - note: If you import 'tryReadMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: tryReadMVar -- warn: - lhs: (liftIO (tryTakeMVar x)) - note: If you import 'tryTakeMVar' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: tryTakeMVar -- warn: - lhs: (liftIO (atomically x)) - note: If you import 'atomically' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: atomically -- warn: - lhs: (liftIO (newTVarIO x)) - note: If you import 'newTVarIO' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: newTVarIO -- warn: - lhs: (liftIO (readTVarIO x)) - note: If you import 'readTVarIO' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readTVarIO -- warn: - lhs: (liftIO (exitWith x)) - note: If you import 'exitWith' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: exitWith -- warn: - lhs: (liftIO (exitFailure )) - note: If you import 'exitFailure' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: exitFailure -- warn: - lhs: (liftIO (exitSuccess )) - note: If you import 'exitSuccess' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: exitSuccess -- warn: - lhs: (liftIO (die x)) - note: If you import 'die' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: die -- warn: - lhs: (liftIO (readFile x)) - note: If you import 'readFile' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readFile -- warn: - lhs: (liftIO (writeFile x y)) - note: If you import 'writeFile' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeFile -- warn: - lhs: (liftIO (appendFile x y)) - note: If you import 'appendFile' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: appendFile -- warn: - lhs: (liftIO (readFileText x)) - note: If you import 'readFileText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readFileText -- warn: - lhs: (liftIO (writeFileText x y)) - note: If you import 'writeFileText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeFileText -- warn: - lhs: (liftIO (appendFileText x y)) - note: If you import 'appendFileText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: appendFileText -- warn: - lhs: (liftIO (readFileLText x)) - note: If you import 'readFileLText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readFileLText -- warn: - lhs: (liftIO (writeFileLText x y)) - note: If you import 'writeFileLText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeFileLText -- warn: - lhs: (liftIO (appendFileLText x y)) - note: If you import 'appendFileLText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: appendFileLText -- warn: - lhs: (liftIO (readFileBS x)) - note: If you import 'readFileBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readFileBS -- warn: - lhs: (liftIO (writeFileBS x y)) - note: If you import 'writeFileBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeFileBS -- warn: - lhs: (liftIO (appendFileBS x y)) - note: If you import 'appendFileBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: appendFileBS -- warn: - lhs: (liftIO (readFileLBS x)) - note: If you import 'readFileLBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readFileLBS -- warn: - lhs: (liftIO (writeFileLBS x y)) - note: If you import 'writeFileLBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeFileLBS -- warn: - lhs: (liftIO (appendFileLBS x y)) - note: If you import 'appendFileLBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: appendFileLBS -- warn: - lhs: (liftIO (newIORef x)) - note: If you import 'newIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: newIORef -- warn: - lhs: (liftIO (readIORef x)) - note: If you import 'readIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: readIORef -- warn: - lhs: (liftIO (writeIORef x y)) - note: If you import 'writeIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: writeIORef -- warn: - lhs: (liftIO (modifyIORef x y)) - note: If you import 'modifyIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: modifyIORef -- warn: - lhs: (liftIO (modifyIORef' x y)) - note: If you import 'modifyIORef'' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: modifyIORef' -- warn: - lhs: (liftIO (atomicModifyIORef x y)) - note: If you import 'atomicModifyIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: atomicModifyIORef -- warn: - lhs: (liftIO (atomicModifyIORef' x y)) - note: If you import 'atomicModifyIORef'' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: atomicModifyIORef' -- warn: - lhs: (liftIO (atomicWriteIORef x y)) - note: If you import 'atomicWriteIORef' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: atomicWriteIORef -- warn: - lhs: (liftIO (getLine )) - note: If you import 'getLine' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: getLine -- warn: - lhs: (liftIO (print x)) - note: If you import 'print' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: print -- warn: - lhs: (liftIO (putStr x)) - note: If you import 'putStr' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putStr -- warn: - lhs: (liftIO (putStrLn x)) - note: If you import 'putStrLn' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putStrLn -- warn: - lhs: (liftIO (putText x)) - note: If you import 'putText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putText -- warn: - lhs: (liftIO (putTextLn x)) - note: If you import 'putTextLn' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putTextLn -- warn: - lhs: (liftIO (putLText x)) - note: If you import 'putLText' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putLText -- warn: - lhs: (liftIO (putLTextLn x)) - note: If you import 'putLTextLn' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putLTextLn -- warn: - lhs: (liftIO (putBS x)) - note: If you import 'putBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putBS -- warn: - lhs: (liftIO (putBSLn x)) - note: If you import 'putBSLn' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putBSLn -- warn: - lhs: (liftIO (putLBS x)) - note: If you import 'putLBS' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putLBS -- warn: - lhs: (liftIO (putLBSLn x)) - note: If you import 'putLBSLn' from Relude, it's already lifted - name: ! '''liftIO'' is not needed' - rhs: putLBSLn + - TemplateHaskell + - OverloadedStrings + - RecordWildCards + - ScopedTypeVariables + - DeriveGeneric + - TypeApplications + - FlexibleContexts + - DeriveAnyClass + - QuasiQuotes + + +# Control which extensions/flags/modules/functions can be used +# +# - extensions: +# - name: +# - TemplateHaskell +# - OverloadedStrings +# - RecordWildCards +# - ScopedTypeVariables +# - DeriveGeneric +# - TypeApplications +# - FlexibleContexts +# - DeriveAnyClass +# - QuasiQuotes +# +# - flags: +# - {name: -w, within: []} # -w is allowed nowhere +# +# - modules: +# - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set' +# - {name: Control.Arrow, within: []} # Certain modules are banned entirely +# +# - functions: +# - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules + + +# Add custom hints for this project +# +# Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar" +# - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x} + + +# Turn on hints that are off by default +# +# Ban "module X(module X) where", to require a real export list +# - warn: {name: Use explicit module export list} +# +# Replace a $ b $ c with a . b $ c +# - group: {name: dollar, enabled: true} +# +# Generalise map to fmap, ++ to <> +# - group: {name: generalise, enabled: true} + + +# Ignore some builtin hints +# - ignore: {name: Use let} +# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules + + +# Define some custom infix operators +# - fixity: infixr 3 ~^#^~ + + +# To generate a suitable file for HLint do: +# $ hlint --default > .hlint.yaml diff --git a/app/Main.hs b/app/Main.hs index 283f9d5..cb0a53f 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,13 +1,21 @@ -module Main where +-- | Main entry point for the layer +module Main + ( main + ) where -import Control.Monad -import Control.Monad.Except import Aws.Lambda.Runtime +import Control.Monad +import qualified Network.HTTP.Client as Http + +httpManagerSettings :: Http.ManagerSettings +httpManagerSettings = + -- We set the timeout to none, as AWS Lambda freezes the containers. + Http.defaultManagerSettings + { Http.managerResponseTimeout = Http.responseTimeoutNone + } main :: IO () -main = forever $ do - res <- runExceptT lambdaRunner - case res of - Right _ -> return () - Left err -> putStrLn $ show err +main = do + manager <- Http.newManager httpManagerSettings + forever (runLambda manager) diff --git a/default.nix b/default.nix deleted file mode 100644 index cfa3328..0000000 --- a/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ compiler ? "ghc863", pkgs ? import {} }: - -let - - haskellPackages = pkgs.haskell.packages.${compiler}.extend ( self: super: { - }); - - runtime = haskellPackages.callCabal2nix "aws-lambda-haskell-runtime" ./. {}; - -in -{ - aws-lambda-haskell-runtime = runtime; - aws-lambda-haskell-runtime-shell = haskellPackages.shellFor { - packages = p: [runtime]; - buildInputs = with pkgs; [ - cabal-install - hlint - stack - haskellPackages.ghcid - haskellPackages.weeder - zlib - ]; - }; -} diff --git a/package.yaml b/package.yaml index aba0679..40b87df 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aws-lambda-haskell-runtime -version: 1.0.10 +version: 1.1.0 github: "theam/aws-lambda-haskell-runtime" license: Apache-2.0 author: Nikita Tchayka @@ -16,28 +16,25 @@ description: Please see the README on GitHub at = 4.7 && < 5 - - mtl library: dependencies: - aeson - bytestring - - case-insensitive - - conduit - - directory - - filepath - http-client - - microlens-platform + - http-types - optparse-generic - process - template-haskell - text - uuid - - wreq + - safe-exceptions-checked + - path + - path-io source-dirs: src exposed-modules: + - Aws.Lambda - Aws.Lambda.Runtime - - Aws.Lambda.Configuration executables: bootstrap: @@ -45,6 +42,7 @@ executables: main: Main.hs dependencies: - aws-lambda-haskell-runtime + - http-client tests: aws-lambda-haskell-runtime-test: @@ -64,7 +62,21 @@ default-extensions: - ScopedTypeVariables - DeriveGeneric - TypeApplications + - FlexibleContexts + - DeriveAnyClass + - QuasiQuotes ghc-options: - -Wall + - -fno-warn-orphans - -optP-Wno-nonportable-include-path + - -Wincomplete-uni-patterns + - -Wincomplete-record-updates + - -Wcompat + - -Widentities + - -Wredundant-constraints + - -Wmissing-export-lists + - -Wpartial-fields + - -fhide-source-paths + - -freverse-errors + - -O2 diff --git a/shell.nix b/shell.nix deleted file mode 100644 index f3d8316..0000000 --- a/shell.nix +++ /dev/null @@ -1 +0,0 @@ -(import ./. {}).aws-lambda-haskell-runtime-shell diff --git a/src/Aws/Lambda.hs b/src/Aws/Lambda.hs new file mode 100644 index 0000000..3b6b9e6 --- /dev/null +++ b/src/Aws/Lambda.hs @@ -0,0 +1,6 @@ +module Aws.Lambda + ( module Reexported + ) where + +import Aws.Lambda.Configuration as Reexported +import Aws.Lambda.Runtime.Context as Reexported diff --git a/src/Aws/Lambda/Configuration.hs b/src/Aws/Lambda/Configuration.hs index 201376d..38bf4d1 100644 --- a/src/Aws/Lambda/Configuration.hs +++ b/src/Aws/Lambda/Configuration.hs @@ -1,184 +1,27 @@ {-# OPTIONS_GHC -fno-warn-unused-pattern-binds #-} module Aws.Lambda.Configuration - ( LambdaOptions (..) + ( Main.LambdaOptions(..) + , Main.generate + , Main.getRecord , configureLambda - , returnAndFail - , returnAndSucceed - , decodeObj - , Options.getRecord + , IPC.returnAndFail + , IPC.returnAndSucceed + , Dispatch.decodeObj ) where -import Data.Aeson +import qualified Language.Haskell.TH as Meta -import qualified Data.Text as Text -import Data.Text (Text) -import GHC.Generics -import Data.Function ((&)) -import Language.Haskell.TH -import qualified Options.Generic as Options -import qualified Data.Conduit as Conduit -import qualified System.Directory as Directory -import System.FilePath (()) -import System.IO.Error -import System.IO (hFlush, stdout, stderr) -import System.Exit (exitSuccess, exitFailure) -import Control.Monad.Trans -import Control.Monad -import qualified Data.Text.Encoding as Encoding -import qualified Data.ByteString.Lazy as LazyByteString -import Data.Void -import Data.Monoid +import qualified Aws.Lambda.Meta.Dispatch as Dispatch +import qualified Aws.Lambda.Meta.Main as Main +import qualified Aws.Lambda.Meta.Run as Run +import qualified Aws.Lambda.Runtime.IPC as IPC - - -import Aws.Lambda.ThHelpers - -putTextLn :: Text -> IO () -putTextLn = putStrLn . Text.unpack - -data LambdaOptions = LambdaOptions - { eventObject :: Text - , contextObject :: Text - , functionHandler :: Text - , executionUuid :: Text - } deriving (Generic) -instance Options.ParseRecord LambdaOptions - - --- This function is the reason why we disable the warning on top of the module -mkMain :: Q [Dec] -mkMain = [d| - $(pName "main") = getRecord "" >>= run - |] - -mkRun :: Q Dec -mkRun = do - handlers <- runIO getHandlers - clause' <- recordQ "LambdaOptions" ["functionHandler", "contextObject", "eventObject", "executionUuid"] - body <- dispatcherCaseQ handlers - pure $ FunD (mkName "run") [Clause [clause'] (NormalB body) []] - - -dispatcherCaseQ :: [Text] -> Q Exp -dispatcherCaseQ fileNames = do - caseExp <- eName "functionHandler" - matches <- traverse handlerCaseQ fileNames - unmatched <- unmatchedCaseQ - pure $ CaseE caseExp (matches <> [unmatched]) - - -handlerCaseQ :: Text -> Q Match -handlerCaseQ lambdaHandler = do - let pattern = LitP (StringL $ Text.unpack lambdaHandler) - body <- [e|do - result <- $(eName qualifiedName) (decodeObj $(eName "eventObject")) (decodeObj $(eName "contextObject")) - either (returnAndFail $(eName "executionUuid")) (returnAndSucceed $(eName "executionUuid")) result - |] - pure $ Match pattern (NormalB body) [] - where - qualifiedName = - lambdaHandler - & Text.dropWhile (/= '/') - & Text.drop 1 - & Text.replace "/" "." - - -unmatchedCaseQ :: Q Match -unmatchedCaseQ = do - let pattern = WildP - body <- [e| - returnAndFail $(eName "executionUuid") ("Handler " <> $(eName "functionHandler") <> " does not exist on project") - |] - pure $ Match pattern (NormalB body) [] - -configureLambda :: Q [Dec] +{-| Generates a @main@ function to be used with the +AWS Lambda layer. +-} +configureLambda :: Meta.DecsQ configureLambda = do - main <- mkMain - run <- mkRun - return $ main <> [run] - - -returnAndFail :: ToJSON a => Text -> a -> IO () -returnAndFail uuid v = do - hFlush stdout - putTextLn uuid - hFlush stdout - putTextLn (Encoding.decodeUtf8 $ LazyByteString.toStrict $ encode v) - hFlush stdout - hFlush stderr - exitFailure - -returnAndSucceed :: ToJSON a => Text -> a -> IO () -returnAndSucceed uuid v = do - hFlush stdout - putTextLn uuid - hFlush stdout - putTextLn (Encoding.decodeUtf8 $ LazyByteString.toStrict $ encode v) - hFlush stdout - exitSuccess - -decodeObj :: FromJSON a => Text -> a -decodeObj x = - case (eitherDecode $ LazyByteString.fromStrict $ Encoding.encodeUtf8 x) of - Left e -> error e - Right v -> v - - -data DirContent = DirList [FilePath] [FilePath] - | DirError IOError -data DirData = DirData FilePath DirContent - - --- Produces directory data -walk :: FilePath -> Conduit.ConduitM () DirData IO () -walk path = do - result <- lift $ tryIOError listdir - case result of - Right dl@(DirList subdirs _) -> do - Conduit.yield (DirData path dl) - forM_ subdirs (walk . (path )) - Right e -> Conduit.yield (DirData path e) - Left e -> Conduit.yield (DirData path (DirError e)) - where - listdir = do - entries <- filterHidden <$> Directory.getDirectoryContents path - subdirs <- filterM isDir entries - files <- filterM isFile entries - return $ DirList subdirs files - where - isFile entry = Directory.doesFileExist (path entry) - isDir entry = Directory.doesDirectoryExist (path entry) - filterHidden paths = filter (\p -> head p /= '.' && p /= "node_modules") paths - - --- Consume directories -myVisitor :: Conduit.ConduitM DirData Void IO [FilePath] -myVisitor = loop [] - where - loop n = do - r <- Conduit.await - case r of - Nothing -> return n - Just result -> loop (process result <> n) - process (DirData _ (DirError _)) = [] - process (DirData dir (DirList _ files)) = map (\f -> dir <> "/" <> f) files - - -getHandlers :: IO [Text] -getHandlers = do - files <- Conduit.runConduit $ walk "." Conduit..| myVisitor - handlerFiles <- files - & fmap Text.pack - & filter (Text.isSuffixOf ".hs") - & filterM containsHandler - & fmap (fmap $ Text.dropEnd 3) - & fmap (fmap $ Text.drop 2) - & fmap (fmap (<> ".handler")) - return handlerFiles - - -containsHandler :: Text -> IO Bool -containsHandler file = do - fileContents <- readFile $ Text.unpack file - return $ "handler :: " `Text.isInfixOf` Text.pack fileContents \ No newline at end of file + main <- Main.generate + run <- Run.generate + return (main <> [run]) diff --git a/src/Aws/Lambda/ThHelpers.hs b/src/Aws/Lambda/Meta/Common.hs similarity index 57% rename from src/Aws/Lambda/ThHelpers.hs rename to src/Aws/Lambda/Meta/Common.hs index df20427..bae9c3f 100644 --- a/src/Aws/Lambda/ThHelpers.hs +++ b/src/Aws/Lambda/Meta/Common.hs @@ -1,22 +1,23 @@ -module Aws.Lambda.ThHelpers - ( pName - , eName - , recordQ +{-| Helper functions to make code generation easier -} +module Aws.Lambda.Meta.Common + ( declarationName + , expressionName + , getFieldsFrom ) where -import Language.Haskell.TH -import qualified Data.Text as Text import Data.Text (Text) +import qualified Data.Text as Text +import Language.Haskell.TH -- | Helper for defining names in declarations -- think of @myValue@ in @myValue = 2@ -pName :: Text -> Q Pat -pName = pure . VarP . mkName . Text.unpack +declarationName :: Text -> Q Pat +declarationName = pure . VarP . mkName . Text.unpack -- | Helper for defining names in expressions -- think of @myFunction@ in @quux = myFunction 3@ -eName :: Text -> Q Exp -eName = pure . VarE . mkName . Text.unpack +expressionName :: Text -> Q Exp +expressionName = pure . VarE . mkName . Text.unpack -- | Helper for extracting fields of a specified record @@ -24,12 +25,12 @@ eName = pure . VarE . mkName . Text.unpack -- and the list of fields to bring into scope as second -- think of @Person@, and @personAge@, @personName@ in -- @myFunction Person { personAge, personName } = ...@ -recordQ :: Text -> [Text] -> Q Pat -recordQ name fields = do - extractedFields <- traverse fName fields +getFieldsFrom :: Text -> [Text] -> Q Pat +getFieldsFrom name fields = do + extractedFields <- traverse extractField fields pure $ RecP (mkName $ Text.unpack name) extractedFields where -- | Helper for extracting fields of records -- think of @personAge@ in @myFunction Person { personAge = personAge } = ...@ - fName :: Text -> Q FieldPat - fName n = pure (mkName $ Text.unpack n, VarP $ mkName $ Text.unpack n) + extractField :: Text -> Q FieldPat + extractField n = pure (mkName $ Text.unpack n, VarP $ mkName $ Text.unpack n) diff --git a/src/Aws/Lambda/Meta/Discover.hs b/src/Aws/Lambda/Meta/Discover.hs new file mode 100644 index 0000000..1cd8b44 --- /dev/null +++ b/src/Aws/Lambda/Meta/Discover.hs @@ -0,0 +1,51 @@ +{-| Discovery of AWS Lambda handlers +A handler is basically a function that has a type definition that +starts with "handler :: ". + -} +module Aws.Lambda.Meta.Discover + ( handlers + ) where + +import qualified Control.Monad as Monad +import Data.Function ((&)) +import qualified Data.Maybe as Maybe +import Data.Text (Text) +import qualified Data.Text as Text + +import Path +import qualified Path.IO as PathIO + +{-| Returns a list of handler paths that look like + +@src/Foo/Bar/Quux.handler@ + +It is the path to the source file, but changing the +extension for ".handler" +-} +handlers :: IO [Text] +handlers = do + (_, files) <- PathIO.listDirRecurRel [reldir|.|] + handlerFiles <- modulesWithHandler files + pure (handlerNames handlerFiles) + +modulesWithHandler :: [Path Rel File] -> IO [Path Rel File] +modulesWithHandler files = + filter isHaskellModule files + & Monad.filterM containsHandler + where + isHaskellModule file = + fileExtension file == ".hs" + +handlerNames :: [Path Rel File] -> [Text] +handlerNames modules = + fmap changeExtensionToHandler modules + & fmap (Text.pack . toFilePath) + where + changeExtensionToHandler file = + setFileExtension ".handler" file + & Maybe.fromJust -- The path will be always parsable, as we just replace the extension + +containsHandler :: Path Rel File -> IO Bool +containsHandler file = do + fileContents <- readFile $ toFilePath file + pure $ "handler :: " `Text.isInfixOf` Text.pack fileContents diff --git a/src/Aws/Lambda/Meta/Dispatch.hs b/src/Aws/Lambda/Meta/Dispatch.hs new file mode 100644 index 0000000..d69a1a1 --- /dev/null +++ b/src/Aws/Lambda/Meta/Dispatch.hs @@ -0,0 +1,61 @@ +{-| Dispatcher generation -} +module Aws.Lambda.Meta.Dispatch + ( generate + , decodeObj + ) where + +import Data.Function ((&)) +import Data.Text (Text) +import qualified Data.Text as Text + +import Data.Aeson +import qualified Data.ByteString.Lazy.Char8 as LazyByteString +import qualified Language.Haskell.TH as Meta + +import Aws.Lambda.Meta.Common + +{-| Helper function that the dispatcher will use to +decode the JSON that comes as an AWS Lambda event into the +appropriate type expected by the handler. +-} +decodeObj :: FromJSON a => String -> a +decodeObj x = + case (eitherDecode $ LazyByteString.pack x) of + Left e -> error e + Right v -> v + +{-| Generates the dispatcher out of a list of +handler names in the form @src/Foo/Bar.handler@ + +This dispatcher has a case for each of the handlers that calls +the appropriate qualified function. In the case of the example above, +the dispatcher will call @Foo.Bar.handler@. +-} +generate :: [Text] -> Meta.ExpQ +generate handlerNames = do + caseExp <- expressionName "functionHandler" + matches <- traverse handlerCase handlerNames + unmatched <- unmatchedCase + pure $ Meta.CaseE caseExp (matches <> [unmatched]) + +handlerCase :: Text -> Meta.MatchQ +handlerCase lambdaHandler = do + let pat = Meta.LitP (Meta.StringL $ Text.unpack lambdaHandler) + body <- [e|do + result <- $(expressionName qualifiedName) (decodeObj $(expressionName "eventObject")) (decodeObj $(expressionName "contextObject")) + either (returnAndFail $(expressionName "executionUuid")) (returnAndSucceed $(expressionName "executionUuid")) result |] + pure $ Meta.Match pat (Meta.NormalB body) [] + where + qualifiedName = + lambdaHandler + & Text.dropWhile (/= '/') + & Text.drop 1 + & Text.replace "/" "." + +unmatchedCase :: Meta.MatchQ +unmatchedCase = do + let pattern = Meta.WildP + body <- [e| + returnAndFail $(expressionName "executionUuid") ("Handler " <> $(expressionName "functionHandler") <> " does not exist on project") + |] + pure $ Meta.Match pattern (Meta.NormalB body) [] diff --git a/src/Aws/Lambda/Meta/Main.hs b/src/Aws/Lambda/Meta/Main.hs new file mode 100644 index 0000000..7f64e78 --- /dev/null +++ b/src/Aws/Lambda/Meta/Main.hs @@ -0,0 +1,27 @@ +{-| main function generation for interoperation with the layer -} +module Aws.Lambda.Meta.Main + ( LambdaOptions(..) + , generate + , Options.getRecord + ) where + +import GHC.Generics (Generic) + +import qualified Language.Haskell.TH as Meta +import qualified Options.Generic as Options + +import Aws.Lambda.Meta.Common + +-- | Options that the generated main expects +data LambdaOptions = LambdaOptions + { eventObject :: !String + , contextObject :: !String + , functionHandler :: !String + , executionUuid :: !String + } deriving (Generic, Options.ParseRecord) + +-- | Generate the main function that the layer will call +generate :: Meta.DecsQ +generate = [d| + $(declarationName "main") = getRecord "" >>= run + |] diff --git a/src/Aws/Lambda/Meta/Run.hs b/src/Aws/Lambda/Meta/Run.hs new file mode 100644 index 0000000..7277add --- /dev/null +++ b/src/Aws/Lambda/Meta/Run.hs @@ -0,0 +1,22 @@ +module Aws.Lambda.Meta.Run + ( generate + ) where + +import qualified Language.Haskell.TH as Meta + +import Aws.Lambda.Meta.Common +import qualified Aws.Lambda.Meta.Discover as Discover +import qualified Aws.Lambda.Meta.Dispatch as Dispatch + +{-| Generate the run function + +It will create a dispatcher that is a huge @case@ expression that +expects the name of the handler provided by AWS Lambda, and will +execute the appropriate user function + -} +generate :: Meta.DecQ +generate = do + handlers <- Meta.runIO Discover.handlers + clause' <- getFieldsFrom "LambdaOptions" ["functionHandler", "contextObject", "eventObject", "executionUuid"] + body <- Dispatch.generate handlers + pure $ Meta.FunD (Meta.mkName "run") [Meta.Clause [clause'] (Meta.NormalB body) []] diff --git a/src/Aws/Lambda/Runtime.hs b/src/Aws/Lambda/Runtime.hs index 5712278..9922ece 100644 --- a/src/Aws/Lambda/Runtime.hs +++ b/src/Aws/Lambda/Runtime.hs @@ -1,272 +1,49 @@ -module Aws.Lambda.Runtime where +module Aws.Lambda.Runtime + ( runLambda + ) where -import Control.Exception (Exception, IOException, try) -import Control.Monad.Except (ExceptT, catchError, throwError) -import Data.Aeson -import System.Exit (ExitCode (..)) -import qualified Data.Text as Text -import Data.Text (Text) -import GHC.Generics -import qualified Data.ByteString.Lazy as LazyByteString -import qualified Data.ByteString as ByteString -import Control.Monad.Trans -import Text.Read (readMaybe) -import qualified Data.Text.Encoding as Encoding -import Control.Monad -import Data.Function ((&)) -import Data.Maybe (listToMaybe) -import Data.Monoid ((<>)) - -import qualified Data.CaseInsensitive as CI -import Lens.Micro.Platform hiding ((.=)) -import qualified Network.Wreq as Wreq +import Control.Exception.Safe.Checked import qualified Network.HTTP.Client as Http -import qualified System.Environment as Environment -import qualified System.Process as Process -import qualified Data.UUID as UUID -import qualified Data.UUID.V4 as UUID -import System.IO (hFlush, stdout) - - -type LByteString = LazyByteString.ByteString -type ByteString = ByteString.ByteString - -type App a = - ExceptT RuntimeError IO a - - -data RuntimeError - = EnvironmentVariableNotSet Text - | ApiConnectionError - | ApiHeaderNotSet Text - | ParseError Text Text - | InvocationError Text - deriving (Show) -instance Exception RuntimeError - -instance ToJSON RuntimeError where - toJSON (EnvironmentVariableNotSet msg) = object - [ "errorType" .= ("EnvironmentVariableNotSet" :: Text) - , "errorMessage" .= msg - ] - - toJSON ApiConnectionError = object - [ "errorType" .= ("ApiConnectionError" :: Text) - , "errorMessage" .= ("Could not connect to API to retrieve AWS Lambda parameters" :: Text) - ] - - toJSON (ApiHeaderNotSet headerName) = object - [ "errorType" .= ("ApiHeaderNotSet" :: Text) - , "errorMessage" .= headerName - ] - - toJSON (ParseError objectBeingParsed value) = object - [ "errorType" .= ("ParseError" :: Text) - , "errorMessage" .= ("Parse error for " <> objectBeingParsed <> ", could not parse value '" <> value <> "'") - ] - - -- We return the user error as it is - toJSON (InvocationError err) = toJSON err - - - -data Context = Context - { memoryLimitInMb :: !Int - , functionName :: !Text - , functionVersion :: !Text - , invokedFunctionArn :: !Text - , awsRequestId :: !Text - , xrayTraceId :: !Text - , logStreamName :: !Text - , logGroupName :: !Text - , deadline :: !Int - } deriving (Generic) -instance FromJSON Context -instance ToJSON Context - - -newtype LambdaResult = - LambdaResult Text - - -awsLambdaVersion :: String -awsLambdaVersion = "2018-06-01" - - -nextInvocationEndpoint :: Text -> String -nextInvocationEndpoint endpoint = - "http://" <> Text.unpack endpoint <> "/"<> awsLambdaVersion <>"/runtime/invocation/next" - - -responseEndpoint :: Text -> Text -> String -responseEndpoint lambdaApi requestId = - "http://"<> Text.unpack lambdaApi <> "/" <> awsLambdaVersion <> "/runtime/invocation/"<> Text.unpack requestId <> "/response" - - -invocationErrorEndpoint :: Text -> Text -> String -invocationErrorEndpoint lambdaApi requestId = - "http://"<> Text.unpack lambdaApi <> "/" <> awsLambdaVersion <> "/runtime/invocation/"<> Text.unpack requestId <> "/error" - - -runtimeInitErrorEndpoint :: Text -> String -runtimeInitErrorEndpoint lambdaApi = - "http://"<> Text.unpack lambdaApi <> "/" <> awsLambdaVersion <> "/runtime/init/error" - - -readEnvironmentVariable :: Text -> App Text -readEnvironmentVariable envVar = do - v <- lift (Environment.lookupEnv $ Text.unpack envVar) - case v of - Nothing -> throwError (EnvironmentVariableNotSet envVar) - Just value -> pure (Text.pack value) - - -readFunctionMemory :: App Int -readFunctionMemory = do - let envVar = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE" - let parseMemory txt = readMaybe (Text.unpack txt) - memoryValue <- readEnvironmentVariable envVar - case parseMemory memoryValue of - Just value -> pure value - Nothing -> throwError (ParseError envVar memoryValue) - - -getApiData :: Text -> App (Wreq.Response LByteString) -getApiData endpoint = - keepRetrying (Wreq.getWith opts $ nextInvocationEndpoint endpoint) - where - opts = Wreq.defaults - & Wreq.manager .~ Left (Http.defaultManagerSettings { Http.managerResponseTimeout = Http.responseTimeoutNone }) - keepRetrying :: IO (Wreq.Response LByteString) -> App (Wreq.Response LByteString) - keepRetrying f = do - result <- (liftIO $ try f) :: App (Either IOException (Wreq.Response LByteString)) - case result of - Right x -> return x - _ -> keepRetrying f - - -extractHeader :: Wreq.Response LByteString -> Text -> Text -extractHeader apiData header = - Encoding.decodeUtf8 (apiData ^. Wreq.responseHeader (CI.mk $ Encoding.encodeUtf8 header)) - - -extractIntHeader :: Wreq.Response LByteString -> Text -> App Int -extractIntHeader apiData headerName = do - let header = extractHeader apiData headerName - case readMaybe $ Text.unpack header of - Nothing -> throwError (ParseError "deadline" header) - Just value -> pure value - - -extractBody :: Wreq.Response LByteString -> Text -extractBody apiData = - Encoding.decodeUtf8 $ LazyByteString.toStrict (apiData ^. Wreq.responseBody) - - -propagateXRayTrace :: Text -> App () -propagateXRayTrace xrayTraceId = - liftIO $ Environment.setEnv "_X_AMZN_TRACE_ID" $ Text.unpack xrayTraceId - - -initializeContext :: Wreq.Response LByteString -> App Context -initializeContext apiData = do - functionName <- readEnvironmentVariable "AWS_LAMBDA_FUNCTION_NAME" - version <- readEnvironmentVariable "AWS_LAMBDA_FUNCTION_VERSION" - logStream <- readEnvironmentVariable "AWS_LAMBDA_LOG_STREAM_NAME" - logGroup <- readEnvironmentVariable "AWS_LAMBDA_LOG_GROUP_NAME" - memoryLimitInMb <- readFunctionMemory - deadline <- extractIntHeader apiData "Lambda-Runtime-Deadline-Ms" - let xrayTraceId = extractHeader apiData "Lambda-Runtime-Trace-Id" - let awsRequestId = extractHeader apiData "Lambda-Runtime-Aws-Request-Id" - let invokedFunctionArn = extractHeader apiData "Lambda-Runtime-Invoked-Function-Arn" - propagateXRayTrace xrayTraceId - pure $ Context - { functionName = functionName - , functionVersion = version - , logStreamName = logStream - , logGroupName = logGroup - , memoryLimitInMb = memoryLimitInMb - , invokedFunctionArn = invokedFunctionArn - , xrayTraceId = xrayTraceId - , awsRequestId = awsRequestId - , deadline = deadline - } - - -getFunctionResult :: UUID.UUID -> Text -> App (Maybe Text) -getFunctionResult u stdOut = do - let out = Text.lines stdOut - - out - & takeWhile (/= uuid) - & mapM_ ( \t -> do - liftIO $ putStrLn $ Text.unpack t - liftIO $ hFlush stdout) - - out - & dropWhile (/= uuid) - & dropWhile (== uuid) - & listToMaybe - & return - where - uuid = Text.pack $ UUID.toString u - - -invoke :: Text -> Context -> App LambdaResult -invoke event context = do - handlerName <- readEnvironmentVariable "_HANDLER" - runningDirectory <- readEnvironmentVariable "LAMBDA_TASK_ROOT" - let contextJSON = Encoding.decodeUtf8 $ LazyByteString.toStrict $ encode context - uuid <- liftIO UUID.nextRandom - out <- liftIO $ Process.readProcessWithExitCode (Text.unpack runningDirectory <> "/haskell_lambda") - [ "--eventObject", Text.unpack event - , "--contextObject", Text.unpack contextJSON - , "--functionHandler", Text.unpack handlerName - , "--executionUuid", UUID.toString uuid - ] - "" - case out of - (ExitSuccess, stdOut, _) -> do - res <- getFunctionResult uuid (Text.pack stdOut) - case res of - Nothing -> throwError (ParseError "parsing result" $ Text.pack stdOut) - Just value -> pure (LambdaResult value) - (_, stdOut, stdErr) -> - if stdErr /= "" - then throwError (InvocationError $ Text.pack stdErr) - else do - res <- getFunctionResult uuid (Text.pack stdOut) - case res of - Nothing -> throwError (ParseError "parsing error" $ Text.pack stdOut) - Just value -> throwError (InvocationError value) - - -publishResult :: Context -> Text -> LambdaResult -> App () -publishResult Context {..} lambdaApi (LambdaResult result) = - void $ liftIO $ Wreq.post (responseEndpoint lambdaApi awsRequestId) (Encoding.encodeUtf8 result) - - -invokeAndPublish :: Context -> Text -> Text -> App () -invokeAndPublish ctx event lambdaApiEndpoint = do - res <- invoke event ctx - publishResult ctx lambdaApiEndpoint res - - -publishError :: Context -> Text -> RuntimeError -> App () -publishError Context {..} lambdaApiEndpoint (InvocationError err) = - void (liftIO $ Wreq.post (invocationErrorEndpoint lambdaApiEndpoint awsRequestId) (Encoding.encodeUtf8 err)) - -publishError Context {..} lambdaApiEndpoint (ParseError t t2) = - void (liftIO $ Wreq.post (invocationErrorEndpoint lambdaApiEndpoint awsRequestId) (toJSON $ ParseError t t2)) - -publishError Context {..} lambdaApiEndpoint err = - void (liftIO $ Wreq.post (runtimeInitErrorEndpoint lambdaApiEndpoint) (toJSON err)) - -lambdaRunner :: App () -lambdaRunner = do - lambdaApiEndpoint <- readEnvironmentVariable "AWS_LAMBDA_RUNTIME_API" - apiData <- getApiData lambdaApiEndpoint - let event = extractBody apiData - ctx <- initializeContext apiData - invokeAndPublish ctx event lambdaApiEndpoint `catchError` publishError ctx lambdaApiEndpoint +import qualified Aws.Lambda.Runtime.ApiInfo as ApiInfo +import qualified Aws.Lambda.Runtime.Context as Context +import qualified Aws.Lambda.Runtime.Environment as Environment +import qualified Aws.Lambda.Runtime.Error as Error +import qualified Aws.Lambda.Runtime.IPC as IPC +import qualified Aws.Lambda.Runtime.Publish as Publish + +-- | Runs the user @haskell_lambda@ executable and posts back the +-- results +runLambda + :: Http.Manager + -> IO () +runLambda manager = do + lambdaApi <- Environment.apiEndpoint `catch` variableNotSet + event <- ApiInfo.fetchEvent manager lambdaApi `catch` errorParsing + context <- Context.initialize event `catch` errorParsing `catch` variableNotSet + ((invokeAndRun manager lambdaApi event context + `catch` \err -> Publish.parsingError err lambdaApi context manager) + `catch` \err -> Publish.invocationError err lambdaApi context manager) + `catch` \(err :: Error.EnvironmentVariableNotSet) -> Publish.runtimeInitError err lambdaApi context manager + +invokeAndRun + :: Throws Error.Parsing + => Throws Error.Invocation + => Throws Error.EnvironmentVariableNotSet + => Http.Manager + -> String + -> ApiInfo.Event + -> Context.Context + -> IO () +invokeAndRun manager lambdaApi event context = do + result <- IPC.invoke (ApiInfo.event event) context + Publish.result result lambdaApi context manager + `catch` \err -> Publish.invocationError err lambdaApi context manager + +variableNotSet :: Error.EnvironmentVariableNotSet -> IO a +variableNotSet (Error.EnvironmentVariableNotSet env) = + error ("Error initializing, variable not set: " <> env) + +errorParsing :: Error.Parsing -> IO a +errorParsing Error.Parsing{..} = + error ("Failed parsing " <> errorMessage <> ", got" <> actualValue) diff --git a/src/Aws/Lambda/Runtime/API/Endpoints.hs b/src/Aws/Lambda/Runtime/API/Endpoints.hs new file mode 100644 index 0000000..37e7326 --- /dev/null +++ b/src/Aws/Lambda/Runtime/API/Endpoints.hs @@ -0,0 +1,61 @@ +module Aws.Lambda.Runtime.API.Endpoints + ( response + , invocationError + , runtimeInitError + , nextInvocation + , Endpoint(..) + ) where + +import qualified Aws.Lambda.Runtime.API.Version as Version + +newtype Endpoint = + Endpoint String + deriving (Show) + +-- | Endpoint that provides the ID of the next invocation +nextInvocation :: String -> Endpoint +nextInvocation lambdaApi = + Endpoint $ concat + [ "http://" + , lambdaApi + , "/" + , Version.value + , "/runtime/invocation/next" + ] + +-- | Where the response of the Lambda gets published +response :: String -> String -> Endpoint +response lambdaApi requestId = + Endpoint $ concat + [ "http://" + , lambdaApi + , "/" + , Version.value + , "/runtime/invocation/" + , requestId + , "/response" + ] + +-- | Invocation (runtime) errors should be published here +invocationError :: String -> String -> Endpoint +invocationError lambdaApi requestId = + Endpoint $ concat + [ "http://" + , lambdaApi + , "/" + , Version.value + , "/runtime/invocation/" + , requestId + , "/error" + ] + +-- | Runtime initialization errors should go here +runtimeInitError :: String -> Endpoint +runtimeInitError lambdaApi = + Endpoint $ concat + [ "http://" + , lambdaApi + , "/" + , Version.value + , "/runtime/init/error" + ] diff --git a/src/Aws/Lambda/Runtime/API/Version.hs b/src/Aws/Lambda/Runtime/API/Version.hs new file mode 100644 index 0000000..c1f2060 --- /dev/null +++ b/src/Aws/Lambda/Runtime/API/Version.hs @@ -0,0 +1,7 @@ +module Aws.Lambda.Runtime.API.Version + ( value + ) where + +-- | Version of the AWS Lambda runtime REST API +value :: String +value = "2018-06-01" diff --git a/src/Aws/Lambda/Runtime/ApiInfo.hs b/src/Aws/Lambda/Runtime/ApiInfo.hs new file mode 100644 index 0000000..45576b8 --- /dev/null +++ b/src/Aws/Lambda/Runtime/ApiInfo.hs @@ -0,0 +1,77 @@ +module Aws.Lambda.Runtime.ApiInfo + ( Event(..) + , fetchEvent + ) where + +import qualified Control.Monad as Monad +import qualified Text.Read as Read + +import Control.Exception (IOException) +import Control.Exception.Safe.Checked +import Data.ByteString.Char8 (ByteString) +import qualified Data.ByteString.Char8 as ByteString +import qualified Data.ByteString.Lazy.Char8 as Lazy +import qualified Network.HTTP.Client as Http +import qualified Network.HTTP.Types.Header as Http + +import qualified Aws.Lambda.Runtime.API.Endpoints as Endpoints +import qualified Aws.Lambda.Runtime.Error as Error + +-- | Event that is fetched out of the AWS Lambda API +data Event = Event + { deadlineMs :: !Int + , traceId :: !String + , awsRequestId :: !String + , invokedFunctionArn :: !String + , event :: !Lazy.ByteString + } + +-- | Performs a GET to the endpoint that provides the next event +fetchEvent :: Throws Error.Parsing => Http.Manager -> String -> IO Event +fetchEvent manager lambdaApi = do + response <- fetchApiData manager lambdaApi + let body = Http.responseBody response + let headers = Http.responseHeaders response + Monad.foldM reduceEvent (initialEvent body) headers + +fetchApiData :: Http.Manager -> String -> IO (Http.Response Lazy.ByteString) +fetchApiData manager lambdaApi = do + let Endpoints.Endpoint endpoint = Endpoints.nextInvocation lambdaApi + request <- Http.parseRequest endpoint + keepRetrying $ Http.httpLbs request manager + +reduceEvent :: Throws Error.Parsing => Event -> (Http.HeaderName, ByteString) -> IO Event +reduceEvent event header = + case header of + ("Lambda-Runtime-Deadline-Ms", value) -> + case Read.readMaybe $ ByteString.unpack value of + Just ms -> pure event { deadlineMs = ms } + Nothing -> throw (Error.Parsing "deadlineMs" $ ByteString.unpack value) + + ("Lambda-Runtime-Trace-Id", value) -> + pure event { traceId = ByteString.unpack value } + + ("Lambda-Runtime-Aws-Request-Id", value) -> + pure event { awsRequestId = ByteString.unpack value } + + ("Lambda-Runtime-Invoked-Function-Arn", value) -> + pure event { invokedFunctionArn = ByteString.unpack value } + + _ -> + pure event + +initialEvent :: Lazy.ByteString -> Event +initialEvent body = Event + { deadlineMs = 0 + , traceId = "" + , awsRequestId = "" + , invokedFunctionArn = "" + , event = body + } + +keepRetrying :: IO (Http.Response Lazy.ByteString) -> IO (Http.Response Lazy.ByteString) +keepRetrying action = do + result <- try action :: IO (Either IOException (Http.Response Lazy.ByteString)) + case result of + Right success -> pure success + _ -> keepRetrying action diff --git a/src/Aws/Lambda/Runtime/Context.hs b/src/Aws/Lambda/Runtime/Context.hs new file mode 100644 index 0000000..9737c9a --- /dev/null +++ b/src/Aws/Lambda/Runtime/Context.hs @@ -0,0 +1,46 @@ +module Aws.Lambda.Runtime.Context + ( Context(..) + , initialize + ) where + +import Control.Exception.Safe.Checked +import Data.Aeson (FromJSON (..), ToJSON (..)) +import GHC.Generics (Generic) + +import qualified Aws.Lambda.Runtime.ApiInfo as ApiInfo +import qualified Aws.Lambda.Runtime.Environment as Environment +import qualified Aws.Lambda.Runtime.Error as Error + +-- | Context that is passed to all the handlers +data Context = Context + { memoryLimitInMb :: !Int + , functionName :: !String + , functionVersion :: !String + , invokedFunctionArn :: !String + , awsRequestId :: !String + , xrayTraceId :: !String + , logStreamName :: !String + , logGroupName :: !String + , deadline :: !Int + } deriving (Generic, FromJSON, ToJSON) + +-- | Initializes the context out of the environment +initialize :: Throws Error.Parsing => Throws Error.EnvironmentVariableNotSet => ApiInfo.Event -> IO Context +initialize ApiInfo.Event{..} = do + functionName <- Environment.functionName + version <- Environment.functionVersion + logStream <- Environment.logStreamName + logGroup <- Environment.logGroupName + memoryLimitInMb <- Environment.functionMemory + Environment.setXRayTrace traceId + pure Context + { functionName = functionName + , functionVersion = version + , logStreamName = logStream + , logGroupName = logGroup + , memoryLimitInMb = memoryLimitInMb + , invokedFunctionArn = invokedFunctionArn + , xrayTraceId = traceId + , awsRequestId = awsRequestId + , deadline = deadlineMs + } diff --git a/src/Aws/Lambda/Runtime/Environment.hs b/src/Aws/Lambda/Runtime/Environment.hs new file mode 100644 index 0000000..94a4381 --- /dev/null +++ b/src/Aws/Lambda/Runtime/Environment.hs @@ -0,0 +1,65 @@ +{-| Provides all the values out of +the environment variables of the system +-} +module Aws.Lambda.Runtime.Environment + ( functionMemory + , apiEndpoint + , handlerName + , taskRoot + , functionName + , functionVersion + , logStreamName + , logGroupName + , setXRayTrace + ) where + +import qualified Aws.Lambda.Runtime.Error as Error +import Control.Exception.Safe.Checked +import qualified System.Environment as Environment +import qualified Text.Read as Read + +logGroupName :: Throws Error.EnvironmentVariableNotSet => IO String +logGroupName = + readEnvironmentVariable "AWS_LAMBDA_LOG_GROUP_NAME" + +logStreamName :: Throws Error.EnvironmentVariableNotSet => IO String +logStreamName = + readEnvironmentVariable "AWS_LAMBDA_LOG_STREAM_NAME" + +functionVersion :: Throws Error.EnvironmentVariableNotSet => IO String +functionVersion = + readEnvironmentVariable "AWS_LAMBDA_FUNCTION_VERSION" + +functionName :: Throws Error.EnvironmentVariableNotSet => IO String +functionName = + readEnvironmentVariable "AWS_LAMBDA_FUNCTION_NAME" + +setXRayTrace :: String -> IO () +setXRayTrace = Environment.setEnv "_X_AMZN_TRACE_ID" + +taskRoot :: Throws Error.EnvironmentVariableNotSet => IO String +taskRoot = + readEnvironmentVariable "LAMBDA_TASK_ROOT" + +handlerName :: Throws Error.EnvironmentVariableNotSet => IO String +handlerName = + readEnvironmentVariable "_HANDLER" + +apiEndpoint :: Throws Error.EnvironmentVariableNotSet => IO String +apiEndpoint = + readEnvironmentVariable "AWS_LAMBDA_RUNTIME_API" + +functionMemory :: Throws Error.Parsing => Throws Error.EnvironmentVariableNotSet => IO Int +functionMemory = do + let envVar = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE" + memoryValue <- readEnvironmentVariable envVar + case Read.readMaybe memoryValue of + Just value -> pure value + Nothing -> throw (Error.Parsing envVar memoryValue) + +readEnvironmentVariable :: Throws Error.EnvironmentVariableNotSet => String -> IO String +readEnvironmentVariable envVar = do + v <- Environment.lookupEnv envVar + case v of + Just value -> pure value + Nothing -> throw (Error.EnvironmentVariableNotSet envVar) diff --git a/src/Aws/Lambda/Runtime/Error.hs b/src/Aws/Lambda/Runtime/Error.hs new file mode 100644 index 0000000..90535d9 --- /dev/null +++ b/src/Aws/Lambda/Runtime/Error.hs @@ -0,0 +1,39 @@ +{-| All the errors that the runtime can throw +-} +module Aws.Lambda.Runtime.Error + ( EnvironmentVariableNotSet(..) + , Parsing(..) + , Invocation(..) + ) where + +import Control.Exception.Safe.Checked +import Data.Aeson (ToJSON (..), object, (.=)) + +newtype EnvironmentVariableNotSet = + EnvironmentVariableNotSet String + deriving (Show, Exception) + +instance ToJSON EnvironmentVariableNotSet where + toJSON (EnvironmentVariableNotSet msg) = object + [ "errorType" .= ("EnvironmentVariableNotSet" :: String) + , "errorMessage" .= msg + ] + +data Parsing = Parsing + { errorMessage :: String + , actualValue :: String + } deriving (Show, Exception) + +instance ToJSON Parsing where + toJSON (Parsing objectBeingParsed value) = object + [ "errorType" .= ("Parsing" :: String) + , "errorMessage" .= ("Parse error for " <> objectBeingParsed <> ", could not parse value '" <> value <> "'") + ] + +newtype Invocation = + Invocation String + deriving (Show, Exception) + +instance ToJSON Invocation where + -- We return the user error as it is + toJSON (Invocation err) = toJSON err diff --git a/src/Aws/Lambda/Runtime/IPC.hs b/src/Aws/Lambda/Runtime/IPC.hs new file mode 100644 index 0000000..5fcb7e5 --- /dev/null +++ b/src/Aws/Lambda/Runtime/IPC.hs @@ -0,0 +1,116 @@ +{-| Inter-Process Communication + +Used for when the user project is called from a layer. + +This is used to call the @haskell_lambda@ executable, which is +provided by the user, when they want to use the layer. + +This IPC protocol is based on printing an UUID that is +created by the layer, and then the result. So everything that +is printed before the UUID, is considered STDOUT printed by +the lambda, while what comes after the UUID is considered. + +In the case that the lambda execution fails, the exit code +won't be 0 (exit-success), so it will use the STDERR. +-} +module Aws.Lambda.Runtime.IPC + ( invoke + , returnAndFail + , returnAndSucceed + ) where + + +import Data.Function ((&)) +import qualified Data.Maybe as Maybe +import qualified Data.String as String +import qualified System.Exit as Exit +import qualified System.IO as IO +import qualified System.Process as Process + +import Control.Exception.Safe.Checked +import Data.Aeson +import qualified Data.ByteString.Lazy.Char8 as ByteString +import qualified Data.UUID as UUID +import qualified Data.UUID.V4 as UUID + +import Aws.Lambda.Runtime.Context (Context (..)) +import qualified Aws.Lambda.Runtime.Environment as Environment +import qualified Aws.Lambda.Runtime.Error as Error +import Aws.Lambda.Runtime.Result (LambdaResult (..)) + +-- | Returns the JSON value failing, according to the protocol +returnAndFail :: ToJSON a => String -> a -> IO () +returnAndFail uuid v = do + IO.hFlush IO.stdout + putStrLn uuid + IO.hFlush IO.stdout + putStrLn (ByteString.unpack $ encode v) + IO.hFlush IO.stdout + IO.hFlush IO.stderr + Exit.exitFailure + +-- | Returns the JSON value succeeding, according to the protocol +returnAndSucceed :: ToJSON a => String -> a -> IO () +returnAndSucceed uuid v = do + IO.hFlush IO.stdout + putStrLn uuid + IO.hFlush IO.stdout + putStrLn (ByteString.unpack $ encode v) + IO.hFlush IO.stdout + Exit.exitSuccess + +-- | Invokes a function defined by the user as the @haskell_lambda@ executable +invoke + :: Throws Error.Invocation + => Throws Error.Parsing + => Throws Error.EnvironmentVariableNotSet + => ByteString.ByteString + -> Context + -> IO LambdaResult +invoke event context = do + handlerName <- Environment.handlerName + runningDirectory <- Environment.taskRoot + let contextJSON = ByteString.unpack $ encode context + uuid <- UUID.nextRandom + out <- Process.readProcessWithExitCode (runningDirectory <> "/haskell_lambda") + [ "--eventObject", ByteString.unpack event + , "--contextObject", contextJSON + , "--functionHandler", handlerName + , "--executionUuid", UUID.toString uuid + ] + "" + case out of + (Exit.ExitSuccess, stdOut, _) -> do + res <- getFunctionResult uuid stdOut + case res of + Nothing -> throw (Error.Parsing "parsing result" stdOut) + Just value -> pure (LambdaResult value) + (_, stdOut, stdErr) -> + if stdErr /= "" + then throw (Error.Invocation stdErr) + else do + res <- getFunctionResult uuid stdOut + case res of + Nothing -> throw (Error.Parsing "parsing error" stdOut) + Just value -> throw (Error.Invocation value) + +getFunctionResult :: UUID.UUID -> String -> IO (Maybe String) +getFunctionResult u stdOut = do + let out = String.lines stdOut + let uuid = UUID.toString u + printAfterUuid uuid out + returnAfterUuid uuid out + where + printAfterUuid uuid out = + out + & takeWhile (/= uuid) + & mapM_ ( \t -> do + putStrLn t + IO.hFlush IO.stdout ) + + returnAfterUuid uuid out = + out + & dropWhile (/= uuid) + & dropWhile (== uuid) + & Maybe.listToMaybe + & pure diff --git a/src/Aws/Lambda/Runtime/Publish.hs b/src/Aws/Lambda/Runtime/Publish.hs new file mode 100644 index 0000000..f56508a --- /dev/null +++ b/src/Aws/Lambda/Runtime/Publish.hs @@ -0,0 +1,55 @@ +{-| Publishing of results/errors back to the +AWS Lambda runtime API -} +module Aws.Lambda.Runtime.Publish + ( result + , invocationError + , parsingError + , runtimeInitError + ) where + +import Control.Monad (void) +import Data.Aeson +import qualified Data.ByteString.Char8 as ByteString +import qualified Network.HTTP.Client as Http + +import qualified Aws.Lambda.Runtime.API.Endpoints as Endpoints +import Aws.Lambda.Runtime.Context (Context (..)) +import qualified Aws.Lambda.Runtime.Error as Error +import Aws.Lambda.Runtime.Result (LambdaResult (..)) + +-- | Publishes the result back to AWS Lambda +result :: LambdaResult -> String -> Context -> Http.Manager -> IO () +result (LambdaResult res) lambdaApi context manager = do + let Endpoints.Endpoint endpoint = Endpoints.response lambdaApi (awsRequestId context) + rawRequest <- Http.parseRequest endpoint + let request = rawRequest + { Http.method = "POST" + , Http.requestBody = Http.RequestBodyBS (ByteString.pack res) + } + void $ Http.httpNoBody request manager + +-- | Publishes an invocation error back to AWS Lambda +invocationError :: Error.Invocation -> String -> Context -> Http.Manager -> IO () +invocationError err lambdaApi context = + publish err (Endpoints.invocationError lambdaApi $ awsRequestId context) + context + +-- | Publishes a parsing error back to AWS Lambda +parsingError :: Error.Parsing -> String -> Context -> Http.Manager -> IO () +parsingError err lambdaApi context = + publish err (Endpoints.invocationError lambdaApi $ awsRequestId context) + context + +-- | Publishes a runtime initialization error back to AWS Lambda +runtimeInitError :: ToJSON err => err -> String -> Context -> Http.Manager -> IO () +runtimeInitError err lambdaApi = + publish err (Endpoints.runtimeInitError lambdaApi) + +publish :: ToJSON err => err -> Endpoints.Endpoint -> Context -> Http.Manager -> IO () +publish err (Endpoints.Endpoint endpoint) Context{..} manager = do + rawRequest <- Http.parseRequest endpoint + let request = rawRequest + { Http.method = "POST" + , Http.requestBody = Http.RequestBodyLBS (encode err) + } + void $ Http.httpNoBody request manager diff --git a/src/Aws/Lambda/Runtime/Result.hs b/src/Aws/Lambda/Runtime/Result.hs new file mode 100644 index 0000000..466189b --- /dev/null +++ b/src/Aws/Lambda/Runtime/Result.hs @@ -0,0 +1,7 @@ +module Aws.Lambda.Runtime.Result + ( LambdaResult(..) + ) where + +-- | Wrapper type to handle the result of the user +newtype LambdaResult = + LambdaResult String diff --git a/stack.yaml b/stack.yaml index 498b8d3..4213fef 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,7 +17,7 @@ # # resolver: ./custom-snapshot.yaml # resolver: https://example.com/snapshots/2018-01-01.yaml -resolver: lts-13.0 +resolver: lts-13.25 # User packages to be built. # Various formats can be used as shown in the example below.