diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,40 @@
-Change log
-==========
+# Change log
+
+0.3.0
+=====
+
+* [#41](https://github.com/kowainik/relude/issues/41):
+  Add `Foldable1`.
+* [#64](https://github.com/kowainik/relude/issues/64):
+  Remove `Print` typeclass.
+  Add `put[L]BS[Ln]` functions.
+  `trace` functions now take `String` as argument instead of `Text`.
+
+  **Important:** this is a breaking change. If you used polymorphic `putStrLn`
+  you need to remove type application or switch to one of the monomorphic
+  functions. Also, you can't abstract over `Print` typeclass anymore.
+* [#66](https://github.com/kowainik/relude/issues/66):
+  Export `(>>>)` and `(<<<)` from `Control.Category`.
+* [#59](https://github.com/kowainik/relude/issues/59):
+  Introduce `flap` function and its operator version `??`.
+* [#64](https://github.com/kowainik/relude/issues/64):
+  Improve performance of functions from `Foldable1`.
+  Add `foldl1'` function.
+* Reexport `uncons` from `base`.
+* Rewrite `die` implementation to use `die` from `base`.
+* [#19](https://github.com/kowainik/relude/issues/19):
+  Rewrite `.hlint.yaml` to Dhall.
+* Move `stdin`- and `stdout`-related functions to new module `Relude.Lifted.Terminal`.
+* [#67](https://github.com/kowainik/relude/issues/67):
+  Add HLint rules for `put*` functions.
+* [#22](https://github.com/kowainik/relude/issues/22):
+  `readFile`, `writeFile` and `appendFile` now work with `String`.
+  Add lifted version of `hClose`.
+  Add `readFile`, `writeFile` and `appendFile` alternatives for `Text` and `ByteString`.
+* [#61](https://github.com/kowainik/relude/issues/61):
+  Add `under2` and `underF2` functions to `Relude.Extra.Newtype`.
+* [#60](https://github.com/kowainik/relude/issues/60):
+  Add `hoistMaybe` and `hoistEither` functions.
 
 0.2.0
 =====
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,8 +19,8 @@
 without a corresponding issue. It's always better to discuss your future
 work first. Even if such an issue exists it's still better to express your willing
 to do that issue under comment section. Thus you will show that you're doing
-that issue, and nobody else will accidentally do it in parallel with you. Furthermore you
-also can discuss the best way to implement that issue!
+that issue, and nobody else will accidentally do it in parallel with you. Furthermore,
+you also can discuss the best way to implement that issue!
 
 To get started with this you should first fork, then clone the repo:
 
@@ -33,11 +33,14 @@
 - [ ] Project compiles
 - [ ] New/fixed features work as expected
 - [ ] Old features do not break after the change
-- [ ] `stylish-haskell` with config in this repo root was used to format code
+- [ ] All new and existing tests pass
+- [ ] [`stylish-haskell`](https://github.com/kowainik/org/blob/master/.stylish-haskell.yaml)
+      with config in this repo root was used to format code
 - [ ] _Recommended:_ Commit messages are in the proper format. If the commit
   addresses an issue start the first line of the commit with the issue number in
   square parentheses.
   + **_Example:_** `[#42] Short commit description`
+- [ ] All changes are reflected in `.hlint.yaml` (Use `hlint/hlint.dhall` to make changes).
 
 If you fix bugs or add new features, please add tests.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
 [![Hackage](https://img.shields.io/hackage/v/relude.svg)](https://hackage.haskell.org/package/relude)
 [![Stackage LTS](http://stackage.org/package/relude/badge/lts)](http://stackage.org/lts/package/relude)
 [![Stackage Nightly](http://stackage.org/package/relude/badge/nightly)](http://stackage.org/nightly/package/relude)
+[![Hackage-Deps](https://img.shields.io/hackage-deps/v/relude.svg)](http://packdeps.haskellers.com/reverse/relude)
 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
 
 `relude` is a custom prelude, an alternative to default `Prelude`.
@@ -129,9 +130,6 @@
     space-efficient). So you should write `sortOn length` (would sort elements
     by length) but `sortWith fst` (would sort list of pairs by first element).
 * Functions `sum` and `product` are strict now, which makes them more efficient.
-* If you try to do something like `putStrLn "hi"`, you'll get an error message if
-  `OverloadedStrings` is enabled – it happens because the compiler doesn't know what
-  type to infer for the string. Use `putTextLn` in this case.
 * Since `show` doesn't come from `Show` anymore, you need to export `Show` from
   `Text.Show` module if you want to implement `Show` instance manually.
 * You can't call `elem` and `notElem` functions over `Set` and `HashSet`. These
@@ -291,9 +289,11 @@
   "foo"
   ```
 
-* A lot of other cool things:
-  + Explore `Extra` modules: [`Relude.Extra`](src/Relude/Extra/)
+* `Foldable1` typeclass that contains generalized interface for folding
+  non-empty structures like `NonEmpty`.
 
+Explore `Extra` modules: [`Relude.Extra`](src/Relude/Extra/)
+
 ## Migration guide [↑](#structure-of-this-tutorial)
 
 In order to replace default `Prelude` with `relude` you should start with instructions given in
@@ -323,19 +323,45 @@
      + `whenJustM     :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()`
 
    * `Either`
-     + `fromLeft   :: a -> Either a b -> a`
-     + `fromRight  :: b -> Either a b -> b`
-     + `either     :: (a -> c) -> (b -> c) -> Either a b -> c`
+     + `fromLeft    :: a -> Either a b -> a`
+     + `fromRight   :: b -> Either a b -> b`
+     + `either      :: (a -> c) -> (b -> c) -> Either a b -> c`
      + `whenRight_  :: Applicative f => Either l r -> (r -> f ()) -> f ()`
      + `whenRightM_ :: Monad m => m (Either l r) -> (r -> m ()) -> m ()`
 
 5. Forget about `String` type.
-   + Replace `putStr` and `putStrLn` with `putText` and `putTextLn`.
    + Replace `(++)` with `(<>)` for `String`-like types.
    + Try to use [`fmt`](http://hackage.haskell.org/package/fmt) library if you need to construct messages.
    + Use `toText/toLText/toString` functions to convert to `Text/LazyText/String` types.
    + Use `encodeUtf8/decodeUtf8` to convert to/from `ByteString`.
+   + Use `(putStr[Ln]|readFile|writeFile|appendFile)[Text|LText|BS|LBS]` functions.
 6. Run `hlint` using `.hlint.yaml` file from `relude` package to cleanup code and imports.
+
+### For Developers
+
+#### Generating .hlint.yaml
+
+Note, that we are using custom `hlint` setting which are `Relude` specific. To
+keep it up to date don't forget to reflect your changes in this file. We are
+using `Dhall` to maintain the configurations. To use it follow the steps below.
+
+First time:
+
+```shell
+$ cabal new-install dhall-json
+```
+
+To generate `hlint` file:
+
+```shell
+$ dhall-to-yaml --omitNull <<< './hlint/hlint.dhall' > .hlint.yaml
+```
+
+Check that you have generated valid `.hlint.yaml` file without parse errors:
+
+```shell
+$ hlint test/Spec.hs
+```
 
 ### Acknowledgement
 
diff --git a/relude.cabal b/relude.cabal
--- a/relude.cabal
+++ b/relude.cabal
@@ -1,6 +1,6 @@
-cabal-version:       1.24
+cabal-version:       2.0
 name:                relude
-version:             0.2.0
+version:             0.3.0
 synopsis:            Custom prelude from Kowainik
 description:
     == Goals
@@ -80,6 +80,7 @@
                            Relude.Debug
                            Relude.DeepSeq
                            Relude.Exception
+                           Relude.File
                            Relude.Foldable
                                Relude.Foldable.Fold
                                Relude.Foldable.Reexport
@@ -92,6 +93,7 @@
                                Relude.Lifted.Exit
                                Relude.Lifted.File
                                Relude.Lifted.IORef
+                               Relude.Lifted.Terminal
                            Relude.List
                                Relude.List.NonEmpty
                                Relude.List.Reexport
@@ -111,6 +113,7 @@
                            Relude.Extra.Bifunctor
                            Relude.Extra.CallStack
                            Relude.Extra.Enum
+                           Relude.Extra.Foldable1
                            Relude.Extra.Group
                            Relude.Extra.Map
                            Relude.Extra.Newtype
@@ -123,18 +126,18 @@
                        -Wincomplete-record-updates
                        -fwarn-implicit-prelude
 
-  build-depends:       base >= 4.8 && < 5
-                     , bytestring
-                     , containers
-                     , deepseq
-                     , ghc-prim >= 0.4.0.0
-                     , hashable
-                     , mtl
-                     , stm
-                     , text
-                     , transformers
-                     , unordered-containers
-                     , utf8-string
+  build-depends:       base >= 4.9 && < 5
+                     , bytestring ^>= 0.10
+                     , containers >= 0.5.7 && < 0.7
+                     , deepseq ^>= 1.4
+                     , ghc-prim >= 0.4.0.0 && < 0.6
+                     , hashable ^>= 1.2
+                     , mtl ^>= 2.2
+                     , stm ^>= 2.4
+                     , text ^>= 1.2
+                     , transformers ^>= 0.5
+                     , unordered-containers >= 0.2.7 && < 0.3
+                     , utf8-string ^>= 1.0
 
   default-language:    Haskell2010
   default-extensions:  NoImplicitPrelude
@@ -147,7 +150,7 @@
 
   other-modules:       Test.Relude.Property
 
-  build-depends:       base >= 4.8 && < 5
+  build-depends:       base >= 4.9 && < 5
                      , relude
                      , bytestring
                      , text
@@ -173,7 +176,7 @@
   hs-source-dirs:      test
   main-is:             Doctest.hs
 
-  build-depends:       base >= 4.8 && < 5
+  build-depends:       base >= 4.9 && < 5
                      , doctest
                      , Glob
 
@@ -185,7 +188,7 @@
   hs-source-dirs:      benchmark
   main-is:             Main.hs
 
-  build-depends:       base >= 4.8 && < 5
+  build-depends:       base >= 4.9 && < 5
                      , relude
                      , containers
                      , gauge
diff --git a/src/Relude.hs b/src/Relude.hs
--- a/src/Relude.hs
+++ b/src/Relude.hs
@@ -58,6 +58,7 @@
 * __"Relude.Exception"__: reexports "Control.Exception", introduces 'bug'
   function as better 'error' and 'Exc' pattern synonym for convenient
   pattern-matching on exceptions.
+* __"Relude.File"__: functions to work with file content as 'Text' or 'ByteString'.
 * __"Relude.Foldable"__: reexports functions for 'Foldable' and 'Traversable'.
 * __"Relude.Function"__: almost everything from "Data.Function" module.
 * __"Relude.Functor"__: reexports from "Data.Functor", "Data.Bifunctor",
@@ -70,7 +71,7 @@
   monad transormers, various combinators.
 * __"Relude.Monoid"__: reexports from "Data.Monoid" and "Data.Semigroup".
 * __"Relude.Nub"__: better versions of @nub@ function for list.
-* __"Relude.Print"__: polymorphic 'putStrLn' function and functions to print 'Text'.
+* __"Relude.Print"__: printing to terminal functions for 'Text' and 'ByteString'.
 * __"Relude.String"__: reexports from @text@ and @bytestring@ packages with
     conversion functions between different textual types.
 
@@ -99,6 +100,7 @@
        , module Relude.Debug
        , module Relude.DeepSeq
        , module Relude.Exception
+       , module Relude.File
        , module Relude.Foldable
        , module Relude.Function
        , module Relude.Functor
@@ -118,6 +120,7 @@
 import Relude.Debug
 import Relude.DeepSeq
 import Relude.Exception
+import Relude.File
 import Relude.Foldable
 import Relude.Function
 import Relude.Functor
diff --git a/src/Relude/Debug.hs b/src/Relude/Debug.hs
--- a/src/Relude/Debug.hs
+++ b/src/Relude/Debug.hs
@@ -23,72 +23,79 @@
 -}
 
 module Relude.Debug
-       ( Undefined (..)
-       , error
-       , trace
+       ( -- * Tracing
+         trace
        , traceM
        , traceId
        , traceShow
        , traceShowId
        , traceShowM
+
+         -- * Imprecise error
+       , error
+       , Undefined (..)
        , undefined
        ) where
 
 import Data.Data (Data)
 import GHC.Exts (RuntimeRep, TYPE)
-import System.IO.Unsafe (unsafePerformIO)
 
-import Relude.Applicative (pass)
-import Relude.Base (Generic, HasCallStack, Typeable)
-import Relude.Monad.Reexport (Monad (..))
-import Relude.Print (Print, putStrLn)
-import Relude.String (Text, toString)
+import Relude.Applicative (Applicative)
+import Relude.Base (Bounded, Enum, Eq, Generic, HasCallStack, Ord, Show, String, Typeable)
+import Relude.String (Read, Text, toString)
 
-import qualified Prelude as P
+import qualified Debug.Trace as Debug
+import qualified Prelude
 
--- | Generalized over string version of 'Debug.Trace.trace' that leaves warnings.
-{-# WARNING trace "'trace' remains in code" #-}
-trace :: Print b => b -> a -> a
-trace string expr = unsafePerformIO (do
-    putStrLn string
-    return expr)
+----------------------------------------------------------------------------
+-- trace
+----------------------------------------------------------------------------
 
--- | 'P.error' that takes 'Text' as an argument.
-error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack
-      => Text -> a
-error s = P.error (toString s)
+-- | Version of 'Debug.Trace.trace' that leaves warning.
+trace :: String -> a -> a
+trace = Debug.trace
+{-# WARNING trace "'trace' remains in code" #-}
 
 -- | Version of 'Debug.Trace.traceShow' that leaves warning.
+traceShow :: Show a => a -> b -> b
+traceShow = Debug.traceShow
 {-# WARNING traceShow "'traceShow' remains in code" #-}
-traceShow :: P.Show a => a -> b -> b
-traceShow a b = trace (P.show a) b
 
--- | Version of 'Debug.Trace.traceShow' that leaves warning.
+-- | Version of 'Debug.Trace.traceShowId' that leaves warning.
+traceShowId :: Show a => a -> a
+traceShowId = Debug.traceShowId
 {-# WARNING traceShowId "'traceShowId' remains in code" #-}
-traceShowId :: P.Show a => a -> a
-traceShowId a = trace (P.show a) a
 
 -- | Version of 'Debug.Trace.traceShowM' that leaves warning.
+traceShowM :: (Show a, Applicative f) => a -> f ()
+traceShowM = Debug.traceShowM
 {-# WARNING traceShowM "'traceShowM' remains in code" #-}
-traceShowM :: (P.Show a, Monad m) => a -> m ()
-traceShowM a = trace (P.show a) pass
 
--- | Version of 'Debug.Trace.traceM' that leaves warning and takes 'Text'.
+-- | Version of 'Debug.Trace.traceM' that leaves warning.
+traceM :: (Applicative f) => String -> f ()
+traceM = Debug.traceM
 {-# WARNING traceM "'traceM' remains in code" #-}
-traceM :: (Monad m) => Text -> m ()
-traceM s = trace (toString s) pass
 
--- | Version of 'Debug.Trace.traceId' that leaves warning and takes 'Text'.
+-- | Version of 'Debug.Trace.traceId' that leaves warning.
+traceId :: String -> String
+traceId = Debug.traceId
 {-# WARNING traceId "'traceId' remains in code" #-}
-traceId :: Text -> Text
-traceId s = trace s s
 
+----------------------------------------------------------------------------
+-- error and undefined
+----------------------------------------------------------------------------
+
+-- | 'Prelude.error' that takes 'Text' as an argument.
+error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack
+      => Text -> a
+error e = Prelude.error (toString e)
+
 -- | Similar to 'undefined' but data type.
-{-# WARNING Undefined "'Undefined' type remains in code" #-}
 data Undefined = Undefined
-    deriving (P.Eq, P.Ord, P.Show, P.Read, P.Enum, P.Bounded, Data, Typeable, Generic)
+    deriving (Eq, Ord, Show, Read, Enum, Bounded, Data, Typeable, Generic)
+{-# WARNING Undefined "'Undefined' type remains in code" #-}
 
--- | 'P.undefined' that leaves warning in code on every usage.
-{-# WARNING undefined "'undefined' function remains in code (or use 'error')" #-}
+-- | 'Prelude.undefined' that leaves warning in code on every usage.
 undefined :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack => a
-undefined = P.undefined
+undefined = Prelude.undefined
+{-# WARNING undefined "'undefined' function remains in code" #-}
diff --git a/src/Relude/Extra/Foldable1.hs b/src/Relude/Extra/Foldable1.hs
new file mode 100644
--- /dev/null
+++ b/src/Relude/Extra/Foldable1.hs
@@ -0,0 +1,204 @@
+{-# LANGUAGE InstanceSigs     #-}
+{-# LANGUAGE TypeApplications #-}
+
+{- |
+Copyright: (c) 2011-2015 Edward Kmett
+           (c) 2018 Kowainik
+License:    MIT
+Maintainer: Kowainik <xrom.xkov@gmail.com>
+-}
+
+module Relude.Extra.Foldable1
+       ( Foldable1 (..)
+       , foldl1'
+       ) where
+
+import Relude hiding (Product (..), Sum (..))
+import Relude.Extra.Newtype (( #. ))
+
+import Data.Functor.Product (Product (..))
+import Data.Functor.Sum (Sum (..))
+
+import qualified Data.Semigroup as SG
+
+{- | The class of foldable data structures that cannot be empty.
+-}
+class Foldable f => Foldable1 f where
+    {-# MINIMAL foldMap1 #-}
+
+    {- | Map each element of the non-empty structure to a semigroup, and combine the results.
+
+    >>> foldMap1 SG.Sum (1 :| [2, 3, 4])
+    Sum {getSum = 10}
+    -}
+    foldMap1 :: Semigroup m => (a -> m) -> f a -> m
+
+    {- | Combine the elements of a non-empty structure using a semigroup.
+
+    >>> fold1 (1 :| [2, 3, 4 :: SG.Sum Int])
+    Sum {getSum = 10}
+    >>> fold1 (4 :| [5, 10 :: SG.Product Int])
+    Product {getProduct = 200}
+    -}
+    fold1 :: Semigroup m => f m -> m
+    fold1 = foldMap1 id
+
+    {- | Convert a non-empty data structre to a NonEmpty list.
+
+    >>> toNonEmpty (Identity 2)
+    2 :| []
+    -}
+    toNonEmpty :: f a -> NonEmpty a
+    toNonEmpty = foldMap1 (:|[])
+
+    {- | The first element of a non-empty data structure.
+
+    >>> head1 (1 :| [2, 3, 4])
+    1
+    -}
+    head1 :: f a -> a
+    head1 = SG.getFirst #. foldMap1 SG.First
+
+    {- | The last element of a non-empty data structure.
+
+    >>> last1 (1 :| [2, 3, 4])
+    4
+    -}
+    last1 :: f a -> a
+    last1 = SG.getLast #. foldMap1 SG.Last
+
+    {- | The largest element of a non-empty data structure.
+
+    >>> maximum1 (32 :| [64, 8, 128, 16])
+    128
+    -}
+    maximum1 :: Ord a => f a -> a
+    maximum1 = SG.getMax #. foldMap1 SG.Max
+
+    {- | The smallest elemenet of a non-empty data structure.
+
+    >>> minimum1 (32 :| [64, 8, 128, 16])
+    8
+    -}
+    minimum1 :: Ord a => f a -> a
+    minimum1 = SG.getMin #. foldMap1 SG.Min
+
+instance Foldable1 NonEmpty where
+    fold1 :: Semigroup m => NonEmpty m -> m
+    fold1 = sconcat
+    {-# INLINE fold1 #-}
+
+    foldMap1 :: Semigroup m => (a -> m) -> NonEmpty a -> m
+    foldMap1 f (a :| [])     = f a
+    foldMap1 f (a :| b : bs) = f a <> foldMap1 f (b :| bs)
+    {-# INLINE foldMap1 #-}
+
+
+    toNonEmpty :: NonEmpty a -> NonEmpty a
+    toNonEmpty = id
+    {-# INLINE toNonEmpty #-}
+
+    head1, last1 :: NonEmpty a -> a
+    head1 = head
+    last1 = last
+    {-# INLINE head1 #-}
+    {-# INLINE last1 #-}
+
+    maximum1, minimum1 :: Ord a => NonEmpty a -> a
+    maximum1 = foldl1' max
+    minimum1 = foldl1' min
+    {-# INLINE maximum1 #-}
+    {-# INLINE minimum1 #-}
+
+
+instance Foldable1 Identity where
+    foldMap1 :: Semigroup m => (a -> m) -> Identity a -> m
+    foldMap1 = coerce
+    {-# INLINE foldMap1 #-}
+
+    fold1 :: Semigroup m => Identity m -> m
+    fold1 = coerce
+    {-# INLINE fold1 #-}
+
+    toNonEmpty :: Identity a -> NonEmpty a
+    toNonEmpty = (:|[]) . coerce
+    {-# INLINE toNonEmpty #-}
+
+    head1 :: Identity a -> a
+    head1 = coerce
+    {-# INLINE head1 #-}
+
+    last1 :: Identity a -> a
+    last1 = coerce
+    {-# INLINE last1 #-}
+
+    maximum1 :: Ord a => Identity a -> a
+    maximum1 = coerce
+    {-# INLINE maximum1 #-}
+
+    minimum1 :: Ord a => Identity a -> a
+    minimum1 = coerce
+    {-# INLINE minimum1 #-}
+
+instance Foldable1 ((,) c) where
+    foldMap1 :: Semigroup m => (a -> m) -> (c, a) -> m
+    foldMap1 f = f . snd
+    {-# INLINE foldMap1 #-}
+
+    fold1 :: Semigroup m => (c, m) -> m
+    fold1 = snd
+    {-# INLINE fold1 #-}
+
+    toNonEmpty :: (c, a) -> NonEmpty a
+    toNonEmpty (_, y) = (y :| [])
+    {-# INLINE toNonEmpty #-}
+
+    head1, last1 :: (c, a) -> a
+    head1 = snd
+    last1 = snd
+    {-# INLINE head1 #-}
+    {-# INLINE last1 #-}
+
+    maximum1, minimum1 :: Ord a => (c, a) -> a
+    maximum1 = snd
+    minimum1 = snd
+    {-# INLINE maximum1 #-}
+    {-# INLINE minimum1 #-}
+
+instance (Foldable1 f, Foldable1 g) => Foldable1 (Compose f g) where
+    foldMap1 :: Semigroup m => (a -> m) -> Compose f g a -> m
+    foldMap1 f = foldMap1 (foldMap1 f) . getCompose
+    {-# INLINE foldMap1 #-}
+
+    head1 :: Compose f g a -> a
+    head1 = head1 . head1 . getCompose
+    {-# INLINE head1 #-}
+
+    last1 :: Compose f g a -> a
+    last1 = last1 . last1 . getCompose
+    {-# INLINE last1 #-}
+
+instance (Foldable1 f, Foldable1 g) => Foldable1 (Product f g) where
+    foldMap1 :: Semigroup m => (a -> m) -> Product f g a -> m
+    foldMap1 f (Pair a b) = foldMap1 f a <> foldMap1 f b
+    {-# INLINE foldMap1 #-}
+
+instance (Foldable1 f, Foldable1 g) => Foldable1 (Sum f g) where
+    foldMap1 :: Semigroup m => (a -> m) -> Sum f g a -> m
+    foldMap1 f (InL x) = foldMap1 f x
+    foldMap1 f (InR y) = foldMap1 f y
+    {-# INLINE foldMap1 #-}
+
+{- | Strictly folds non-empty structure with given function @f@:
+
+@
+foldl1' f [x0, x1, x2 ...] = f (f x0 x1) x2 ...
+@
+
+>>> foldl1' (++) ([1,2] :| [[3,4], [5,6]])
+[1,2,3,4,5,6]
+-}
+foldl1' :: (a -> a -> a) -> NonEmpty a -> a
+foldl1' _ (x :| [])     = x
+foldl1' f (x :| (y:ys)) = foldl' f (f x y) ys
+{-# INLINE foldl1' #-}
diff --git a/src/Relude/Extra/Newtype.hs b/src/Relude/Extra/Newtype.hs
--- a/src/Relude/Extra/Newtype.hs
+++ b/src/Relude/Extra/Newtype.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP                 #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 {- |
@@ -12,12 +13,18 @@
        ( un
        , wrap
        , under
+       , under2
+#if ( __GLASGOW_HASKELL__ != 802 )
+       , underF2
+#endif
+       , (#.)
        ) where
 
 import Relude
 
 -- $setup
 -- >>> :set -XTypeApplications
+-- >>> import Data.Semigroup (Max (..))
 
 {- | Unwraps value from @newtype@.
 
@@ -27,8 +34,9 @@
 >>> un (Size 5) == length ['a', 'x', 'b']
 False
 -}
-un :: forall b a . Coercible a b => a -> b
+un :: forall a n . Coercible a n => n -> a
 un = coerce
+{-# INLINE un #-}
 
 {- | Wraps value to @newtype@. Behaves exactly as 'un' but has more meaningnful
 name in case you need to convert some value to @newtype@.
@@ -37,8 +45,9 @@
 >>> wrap False == Flag True
 False
 -}
-wrap :: forall b a . Coercible a b => a -> b
+wrap :: forall n a . Coercible a n => a -> n
 wrap = coerce
+{-# INLINE wrap #-}
 
 {- | Applies function to the content of @newtype@. This function is not supposed
 to be used on @newtype@s that are created with the help of smart constructors.
@@ -50,5 +59,38 @@
 >>> under (filter (== 'a')) (Bar "abacaba")
 Bar "aaaa"
 -}
-under :: forall b a . Coercible a b => (b -> b) -> a -> a
+under :: forall n a . Coercible a n => (n -> n) -> (a -> a)
 under = coerce
+{-# INLINE under #-}
+
+{- | Lift binary function for @newtype@s to work over underlying @newtype@
+representation.
+
+>>> under2 @(Sum Int) (<>) (3 :: Int) 4
+7
+>>> under2 @All (<>) True False
+False
+-}
+under2 :: forall n a . Coercible a n => (n -> n -> n) -> (a -> a -> a)
+under2 = coerce
+{-# INLINE under2 #-}
+
+#if ( __GLASGOW_HASKELL__ != 802 )
+{- | Version of 'under2' that works on @newtype@s parametrized by their
+representation. Provided for convenience.
+
+>>> underF2 @Sum (<>) (3 :: Int) 4
+7
+>>> underF2 @Max (<>) 'p' 't'
+'t'
+-}
+underF2 :: forall n a . Coercible a (n a) => (n a -> n a -> n a) -> (a -> a -> a)
+underF2 = coerce
+{-# INLINE underF2 #-}
+#endif
+
+{- | Coercible composition
+-}
+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)
+(#.) _f = coerce
+{-# INLINE (#.) #-}
diff --git a/src/Relude/File.hs b/src/Relude/File.hs
new file mode 100644
--- /dev/null
+++ b/src/Relude/File.hs
@@ -0,0 +1,128 @@
+{- |
+Copyright:  (c) 2018 Kowainik
+License:    MIT
+Maintainer: Kowainik <xrom.xkov@gmail.com>
+
+Functions to work with files for 'Text', 'LText', 'ByteString' and 'LByteString'
+types.
+-}
+
+module Relude.File
+       ( -- * Text
+         readFileText
+       , writeFileText
+       , appendFileText
+
+         -- * Lazy Text
+       , readFileLText
+       , writeFileLText
+       , appendFileLText
+
+         -- * ByteString
+       , readFileBS
+       , writeFileBS
+       , appendFileBS
+
+         -- * Lazy ByteString
+       , readFileLBS
+       , writeFileLBS
+       , appendFileLBS
+       ) where
+
+import Relude.Base (FilePath, IO)
+import Relude.Function ((.))
+import Relude.Monad.Reexport (MonadIO (..))
+import Relude.String (ByteString, LByteString, LText, Text)
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.Text.IO as T
+import qualified Data.Text.Lazy.IO as LT
+
+----------------------------------------------------------------------------
+-- Text
+----------------------------------------------------------------------------
+
+-- | Lifted version of 'T.readFile'.
+readFileText :: MonadIO m => FilePath -> m Text
+readFileText = liftIO . T.readFile
+{-# SPECIALIZE readFileText :: FilePath -> IO Text #-}
+{-# INLINE readFileText #-}
+
+-- | Lifted version of 'T.writeFile'.
+writeFileText :: MonadIO m => FilePath -> Text -> m ()
+writeFileText p = liftIO . T.writeFile p
+{-# SPECIALIZE writeFileText :: FilePath -> Text -> IO () #-}
+{-# INLINE     writeFileText #-}
+
+-- | Lifted version of 'T.appendFile'.
+appendFileText :: MonadIO m => FilePath -> Text -> m ()
+appendFileText p = liftIO . T.appendFile p
+{-# SPECIALIZE appendFileText :: FilePath -> Text -> IO () #-}
+{-# INLINE     appendFileText #-}
+
+----------------------------------------------------------------------------
+-- Lazy Text
+----------------------------------------------------------------------------
+
+-- | Lifted version of 'LT.readFile'.
+readFileLText :: MonadIO m => FilePath -> m LText
+readFileLText = liftIO . LT.readFile
+{-# SPECIALIZE readFileLText :: FilePath -> IO LText #-}
+{-# INLINE readFileLText #-}
+
+-- | Lifted version of 'LT.writeFile'.
+writeFileLText :: MonadIO m => FilePath -> LText -> m ()
+writeFileLText p = liftIO . LT.writeFile p
+{-# SPECIALIZE writeFileLText :: FilePath -> LText -> IO () #-}
+{-# INLINE     writeFileLText #-}
+
+-- | Lifted version of 'LT.appendFile'.
+appendFileLText :: MonadIO m => FilePath -> LText -> m ()
+appendFileLText p = liftIO . LT.appendFile p
+{-# SPECIALIZE appendFileLText :: FilePath -> LText -> IO () #-}
+{-# INLINE     appendFileLText #-}
+
+----------------------------------------------------------------------------
+-- ByteString
+----------------------------------------------------------------------------
+
+-- | Lifted version of 'BS.readFile'.
+readFileBS :: MonadIO m => FilePath -> m ByteString
+readFileBS = liftIO . BS.readFile
+{-# SPECIALIZE readFileBS :: FilePath -> IO ByteString #-}
+{-# INLINE readFileBS #-}
+
+-- | Lifted version of 'BS.writeFile'.
+writeFileBS :: MonadIO m => FilePath -> ByteString -> m ()
+writeFileBS p = liftIO . BS.writeFile p
+{-# SPECIALIZE writeFileBS :: FilePath -> ByteString -> IO () #-}
+{-# INLINE     writeFileBS #-}
+
+-- | Lifted version of 'BS.appendFile'.
+appendFileBS :: MonadIO m => FilePath -> ByteString -> m ()
+appendFileBS p = liftIO . BS.appendFile p
+{-# SPECIALIZE appendFileBS :: FilePath -> ByteString -> IO () #-}
+{-# INLINE     appendFileBS #-}
+
+----------------------------------------------------------------------------
+-- Lazy ByteString
+----------------------------------------------------------------------------
+
+-- | Lifted version of 'LBS.readFile'.
+readFileLBS :: MonadIO m => FilePath -> m LByteString
+readFileLBS = liftIO . LBS.readFile
+{-# SPECIALIZE readFileLBS :: FilePath -> IO LByteString #-}
+{-# INLINE readFileLBS #-}
+
+-- | Lifted version of 'LBS.writeFile'.
+writeFileLBS :: MonadIO m => FilePath -> LByteString -> m ()
+writeFileLBS p = liftIO . LBS.writeFile p
+{-# SPECIALIZE writeFileLBS :: FilePath -> LByteString -> IO () #-}
+{-# INLINE     writeFileLBS #-}
+
+-- | Lifted version of 'LBS.appendFile'.
+appendFileLBS :: MonadIO m => FilePath -> LByteString -> m ()
+appendFileLBS p = liftIO . LBS.appendFile p
+{-# SPECIALIZE appendFileLBS :: FilePath -> LByteString -> IO () #-}
+{-# INLINE     appendFileLBS #-}
diff --git a/src/Relude/Foldable/Fold.hs b/src/Relude/Foldable/Fold.hs
--- a/src/Relude/Foldable/Fold.hs
+++ b/src/Relude/Foldable/Fold.hs
@@ -53,12 +53,7 @@
 
 -- $setup
 -- >>> :set -XOverloadedStrings
--- >>> import Relude.Base (Int, String, Rational, even, (/))
--- >>> import Relude.Bool (when)
--- >>> import Relude.List (replicate)
--- >>> import Relude.Monad (Maybe (..), (>=>))
--- >>> import Relude.Print (print, putTextLn)
--- >>> import Relude.String (Text, readMaybe)
+-- >>> import Relude
 -- >>> import qualified Data.HashMap.Strict as HashMap
 
 {- | Similar to 'foldl'' but takes a function with its arguments flipped.
diff --git a/src/Relude/Function.hs b/src/Relude/Function.hs
--- a/src/Relude/Function.hs
+++ b/src/Relude/Function.hs
@@ -9,10 +9,14 @@
 -}
 
 module Relude.Function
-       ( module Data.Function
+       ( module Control.Arrow
+       , module Control.Category
+       , module Data.Function
        , identity
        ) where
 
+import Control.Arrow ((&&&))
+import Control.Category ((<<<), (>>>))
 import Data.Function (const, fix, flip, id, on, ($), (&), (.))
 
 -- | Renamed version of 'Prelude.id'.
diff --git a/src/Relude/Functor/Fmap.hs b/src/Relude/Functor/Fmap.hs
--- a/src/Relude/Functor/Fmap.hs
+++ b/src/Relude/Functor/Fmap.hs
@@ -14,29 +14,59 @@
 module Relude.Functor.Fmap
        ( (<<$>>)
        , (<&>)
+       , flap
+       , (??)
        ) where
 
 import Relude.Function ((.))
 import Relude.Functor.Reexport (Functor (..))
 
+import Data.Functor ((<$>))
 #if MIN_VERSION_base(4,11,0)
 import Data.Functor ((<&>))
 #else
-import Data.Functor ((<$>))
+
+
 -- | Flipped version of '<$>'.
 infixl 1 <&>
 (<&>) :: Functor f => f a -> (a -> b) -> f b
 as <&> f = f <$> as
+{-# INLINE (<&>) #-}
 #endif
 
 -- $setup
--- >>> import Relude.Base (negate)
+-- >>> import Relude.Base (negate, (*), (+))
 -- >>> import Relude.Monad (Maybe (..))
+-- >>> import Relude.List ((++))
 
--- | Alias for @fmap . fmap@. Convenient to work with two nested 'Functor's.
---
--- >>> negate <<$>> Just [1,2,3]
--- Just [-1,-2,-3]
+{- | Alias for @fmap . fmap@. Convenient to work with two nested 'Functor's.
+
+>>> negate <<$>> Just [1,2,3]
+Just [-1,-2,-3]
+-}
+infixl 4 <<$>>
 (<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
 (<<$>>) = fmap . fmap
-infixl 4 <<$>>
+{-# INLINE (<<$>>) #-}
+
+{- | Takes a function in a 'Functor' context and applies it to a normal value.
+
+>>> flap (++) "relude" "P"
+"Prelude"
+-}
+flap :: Functor f => f (a -> b) -> a -> f b
+flap ff x = (\f -> f x) <$> ff
+{-# INLINE flap #-}
+
+{- | Operator version of the 'flap' function.
+
+>>> [(+2), (*3)] ?? 5
+[7,15]
+
+>>> Just (+3) ?? 5
+Just 8
+-}
+infixl 4 ??
+(??) :: Functor f => f (a -> b) -> a -> f b
+(??) = flap
+{-# INLINE (??) #-}
diff --git a/src/Relude/Functor/Reexport.hs b/src/Relude/Functor/Reexport.hs
--- a/src/Relude/Functor/Reexport.hs
+++ b/src/Relude/Functor/Reexport.hs
@@ -11,14 +11,12 @@
 -}
 
 module Relude.Functor.Reexport
-       ( module Control.Arrow
-       , module Data.Bifunctor
+       ( module Data.Bifunctor
        , module Data.Functor
        , module Data.Functor.Compose
        , module Data.Functor.Identity
        ) where
 
-import Control.Arrow ((&&&))
 import Data.Bifunctor (Bifunctor (..))
 import Data.Functor (Functor (..), void, ($>), (<$>))
 import Data.Functor.Compose (Compose (..))
diff --git a/src/Relude/Lifted.hs b/src/Relude/Lifted.hs
--- a/src/Relude/Lifted.hs
+++ b/src/Relude/Lifted.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE Safe #-}
-
 {- |
 Copyright: (c) 2016 Stephen Diehl
            (c) 20016-2018 Serokell
@@ -15,9 +13,11 @@
        , module Relude.Lifted.Exit
        , module Relude.Lifted.File
        , module Relude.Lifted.IORef
+       , module Relude.Lifted.Terminal
        ) where
 
 import Relude.Lifted.Concurrent
 import Relude.Lifted.Exit
 import Relude.Lifted.File
 import Relude.Lifted.IORef
+import Relude.Lifted.Terminal
diff --git a/src/Relude/Lifted/Exit.hs b/src/Relude/Lifted/Exit.hs
--- a/src/Relude/Lifted/Exit.hs
+++ b/src/Relude/Lifted/Exit.hs
@@ -19,12 +19,9 @@
 
 import Control.Monad.Trans (MonadIO, liftIO)
 import Data.String (String)
-import Prelude ((>>))
 import System.Exit (ExitCode)
-import System.IO (stderr)
 
 import qualified System.Exit as XIO
-import qualified System.IO (hPutStrLn)
 
 -- | Lifted version of 'System.Exit.exitWith'.
 exitWith :: MonadIO m => ExitCode -> m a
@@ -42,8 +39,6 @@
 {-# INLINE exitSuccess #-}
 
 -- | Lifted version of 'System.Exit.die'.
--- 'XIO.die' is available since base-4.8, but it's more convenient to
--- redefine it instead of using CPP.
 die :: MonadIO m => String -> m ()
-die err = liftIO (System.IO.hPutStrLn stderr err) >> exitFailure
+die err = liftIO (XIO.die err)
 {-# INLINE die #-}
diff --git a/src/Relude/Lifted/File.hs b/src/Relude/Lifted/File.hs
--- a/src/Relude/Lifted/File.hs
+++ b/src/Relude/Lifted/File.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE Safe #-}
-
 {- |
 Copyright: (c) 2016 Stephen Diehl
            (c) 20016-2018 Serokell
@@ -8,52 +6,49 @@
 Maintainer: Kowainik <xrom.xkov@gmail.com>
 
 Lifted versions of functions working with files and common IO.
-All functions are specialized to 'Data.Text.Text'.
 -}
 
 module Relude.Lifted.File
-       ( appendFile
-       , getLine
-       , openFile
-       , readFile
+       ( readFile
        , writeFile
+       , appendFile
+       , openFile
+       , hClose
        ) where
 
-import Control.Monad.Trans (MonadIO, liftIO)
-import Data.Text (Text)
-import Prelude (FilePath)
-import System.IO (Handle, IOMode)
+import Relude.Base (FilePath, Handle, IO, IOMode, String)
+import Relude.Function ((.))
+import Relude.Monad.Reexport (MonadIO (..))
 
-import qualified Data.Text.IO as XIO
-import qualified System.IO as XIO (openFile)
+import qualified System.IO as IO
 
-----------------------------------------------------------------------------
--- Text
-----------------------------------------------------------------------------
 
--- | Lifted version of 'Data.Text.appendFile'.
-appendFile :: MonadIO m => FilePath -> Text -> m ()
-appendFile a b = liftIO (XIO.appendFile a b)
-{-# INLINE appendFile #-}
-
--- | Lifted version of 'Data.Text.getLine'.
-getLine :: MonadIO m => m Text
-getLine = liftIO XIO.getLine
-{-# INLINE getLine #-}
-
--- | Lifted version of 'Data.Text.readFile'.
-readFile :: MonadIO m => FilePath -> m Text
-readFile a = liftIO (XIO.readFile a)
+-- | Lifted version of 'IO.readFile'.
+readFile :: MonadIO m => FilePath -> m String
+readFile = liftIO . IO.readFile
+{-# SPECIALIZE readFile :: FilePath -> IO String #-}
 {-# INLINE readFile #-}
 
--- | Lifted version of 'Data.Text.writeFile'.
-writeFile :: MonadIO m => FilePath -> Text -> m ()
-writeFile a b = liftIO (XIO.writeFile a b)
+-- | Lifted version of 'IO.writeFile'.
+writeFile :: MonadIO m => FilePath -> String -> m ()
+writeFile p= liftIO . IO.writeFile p
+{-# SPECIALIZE writeFile :: FilePath -> String -> IO () #-}
 {-# INLINE writeFile #-}
 
--- | Lifted version of 'System.IO.openFile'.
+-- | Lifted version of 'IO.appendFile'.
+appendFile :: MonadIO m => FilePath -> String -> m ()
+appendFile p = liftIO . IO.appendFile p
+{-# SPECIALIZE appendFile :: FilePath -> String -> IO () #-}
+{-# INLINE appendFile #-}
+
+-- | Lifted version of 'IO.openFile'.
 openFile :: MonadIO m => FilePath -> IOMode -> m Handle
-openFile a b = liftIO (XIO.openFile a b)
+openFile p = liftIO . IO.openFile p
+{-# SPECIALIZE openFile :: FilePath -> IOMode -> IO Handle #-}
 {-# INLINE openFile #-}
 
--- 'withFile' can't be lifted into 'MonadIO', as it uses 'bracket'
+-- | Lifted version of 'IO.hClose'.
+hClose :: MonadIO m => Handle -> m ()
+hClose = liftIO . hClose
+{-# SPECIALIZE hClose :: Handle -> IO () #-}
+{-# INLINE hClose #-}
diff --git a/src/Relude/Lifted/Terminal.hs b/src/Relude/Lifted/Terminal.hs
new file mode 100644
--- /dev/null
+++ b/src/Relude/Lifted/Terminal.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE ExplicitForAll #-}
+
+{- |
+Copyright:  (c) 2018 Kowainik
+License:    MIT
+Maintainer: Kowainik <xrom.xkov@gmail.com>
+
+Lifted functions to work with stdin and stdout.
+-}
+
+module Relude.Lifted.Terminal
+       ( getLine
+       , print
+       , putStr
+       , putStrLn
+       ) where
+
+import Relude.Base (IO, Show, String)
+import Relude.Function ((.))
+import Relude.Monad.Reexport (MonadIO (..))
+import Relude.String.Reexport (Text)
+
+import qualified Data.Text.IO as TIO
+import qualified System.IO as IO (print, putStr, putStrLn)
+
+-- | Lifted version of 'Data.Text.getLine'.
+getLine :: MonadIO m => m Text
+getLine = liftIO TIO.getLine
+{-# SPECIALIZE getLine :: IO Text #-}
+{-# INLINE getLine #-}
+
+-- | Lifted version of 'Prelude.print'.
+print :: forall a m . (MonadIO m, Show a) => a -> m ()
+print = liftIO . IO.print
+{-# SPECIALIZE print :: Show a => a -> IO () #-}
+{-# INLINE print #-}
+
+-- | Lifted version of 'IO.putStr'.
+putStr :: MonadIO m => String -> m ()
+putStr = liftIO . IO.putStr
+{-# SPECIALIZE putStr :: String -> IO () #-}
+{-# INLINE putStr #-}
+
+-- | Lifted version of 'IO.putStrLn'.
+putStrLn :: MonadIO m => String -> m ()
+putStrLn = liftIO . IO.putStrLn
+{-# SPECIALIZE putStrLn :: String -> IO () #-}
+{-# INLINE putStrLn #-}
diff --git a/src/Relude/List/NonEmpty.hs b/src/Relude/List/NonEmpty.hs
--- a/src/Relude/List/NonEmpty.hs
+++ b/src/Relude/List/NonEmpty.hs
@@ -12,7 +12,6 @@
 
 module Relude.List.NonEmpty
        ( viaNonEmpty
-       , uncons
        , whenNotNull
        , whenNotNullM
        ) where
@@ -24,13 +23,7 @@
 import Relude.Monad (Maybe (..), Monad (..))
 
 -- $setup
--- >>> import Relude.Applicative (pure)
--- >>> import Relude.Base ((==))
--- >>> import Relude.Bool (Bool (..), not)
--- >>> import Relude.Foldable (length)
--- >>> import Relude.Function (($))
--- >>> import Relude.List.Reexport (head)
--- >>> import Relude.Print (print)
+-- >>> import Relude
 
 {- | For safe work with lists using functinons for 'NonEmpty'.
 
@@ -42,20 +35,6 @@
 viaNonEmpty :: (NonEmpty a -> b) -> [a] -> Maybe b
 viaNonEmpty f = fmap f . nonEmpty
 {-# INLINE viaNonEmpty #-}
-
-{- | Destructuring list into its head and tail if possible. This function is total.
-
->>> uncons []
-Nothing
->>> uncons [1..5]
-Just (1,[2,3,4,5])
->>> uncons (5 : [1..5]) >>= \(f, l) -> pure $ f == length l
-Just True
-
--}
-uncons :: [a] -> Maybe (a, [a])
-uncons []     = Nothing
-uncons (x:xs) = Just (x, xs)
 
 {- | Performs given action over 'NonEmpty' list if given list is non empty.
 
diff --git a/src/Relude/List/Reexport.hs b/src/Relude/List/Reexport.hs
--- a/src/Relude/List/Reexport.hs
+++ b/src/Relude/List/Reexport.hs
@@ -20,6 +20,6 @@
                   genericReplicate, genericSplitAt, genericTake, group, inits, intercalate,
                   intersperse, isPrefixOf, iterate, map, permutations, repeat, replicate, reverse,
                   scanl, scanr, sort, sortBy, sortOn, splitAt, subsequences, tails, take, takeWhile,
-                  transpose, unfoldr, unzip, unzip3, zip, zip3, zipWith, (++))
+                  transpose, uncons, unfoldr, unzip, unzip3, zip, zip3, zipWith, (++))
 import Data.List.NonEmpty (NonEmpty (..), head, init, last, nonEmpty, tail)
 import GHC.Exts (sortWith)
diff --git a/src/Relude/Monad/Maybe.hs b/src/Relude/Monad/Maybe.hs
--- a/src/Relude/Monad/Maybe.hs
+++ b/src/Relude/Monad/Maybe.hs
@@ -21,14 +21,10 @@
        ) where
 
 import Relude.Applicative (Applicative, pass, pure)
-import Relude.Monad.Reexport (fromMaybe)
-import Relude.Monad.Reexport (Maybe (..), Monad (..))
+import Relude.Monad.Reexport (Maybe (..), Monad (..), fromMaybe)
 
 -- $setup
--- >>> import Relude.Bool (Bool (..), not)
--- >>> import Relude.Function (($))
--- >>> import Relude.Print (putTextLn, print)
--- >>> import Relude.String (readMaybe)
+-- >>> import Relude
 
 {- | Similar to 'fromMaybe' but with flipped arguments.
 
diff --git a/src/Relude/Monad/Trans.hs b/src/Relude/Monad/Trans.hs
--- a/src/Relude/Monad/Trans.hs
+++ b/src/Relude/Monad/Trans.hs
@@ -22,13 +22,17 @@
        , executingStateT
        , usingState
        , usingStateT
+         -- * Lifted to Transformers
+       , hoistMaybe
+       , hoistEither
        ) where
 
 import Prelude (flip, fst, snd)
 
+import Relude.Applicative (Applicative (pure))
 import Relude.Functor (Functor, (<$>))
-import Relude.Monad.Reexport (Reader, ReaderT, State, StateT, runReader, runReaderT, runState,
-                              runStateT)
+import Relude.Monad.Reexport (Either, ExceptT (..), Maybe, MaybeT (..), Reader, ReaderT, State,
+                              StateT, runReader, runReaderT, runState, runStateT)
 
 -- | Shorter and more readable alias for @flip runReaderT@.
 usingReaderT :: r -> ReaderT r m a -> m a
@@ -73,3 +77,14 @@
 executingState :: s -> State s a -> s
 executingState s st = snd (usingState s st)
 {-# INLINE executingState #-}
+
+
+-- | Lift a 'Maybe' to the 'MaybeT' monad
+hoistMaybe  :: Applicative m => Maybe a -> MaybeT m a
+hoistMaybe m = MaybeT (pure m)
+{-# INLINE hoistMaybe #-}
+
+-- | Lift a 'Either' to the 'ExceptT' monad
+hoistEither :: Applicative m => Either e a -> ExceptT e m a
+hoistEither e = ExceptT (pure e)
+{-# INLINE hoistEither #-}
diff --git a/src/Relude/Print.hs b/src/Relude/Print.hs
--- a/src/Relude/Print.hs
+++ b/src/Relude/Print.hs
@@ -10,80 +10,86 @@
 License:    MIT
 Maintainer: Kowainik <xrom.xkov@gmail.com>
 
-Generalization of 'Prelude.putStr' and 'Prelude.putStrLn' functions.
+Functions like 'Prelude.putStr' and 'Prelude.putStrLn' but for 'Text', 'LText',
+'ByteString' and 'LByteString'.
 -}
 
 module Relude.Print
-       ( Print (..)
-       , print
-       , putText
+       ( -- * 'Text' & 'LText'
+         putText
        , putTextLn
        , putLText
        , putLTextLn
-       ) where
 
-import Data.Function ((.))
-
-import Relude.Monad.Reexport (MonadIO, liftIO)
+         -- * 'ByteString' & 'LByteString'
+       , putBS
+       , putBSLn
+       , putLBS
+       , putLBSLn
+       ) where
 
-import qualified Prelude (print, putStr, putStrLn)
+import Relude.Function ((.))
+import Relude.Monad.Reexport (MonadIO (..))
+import Relude.String (ByteString, LByteString, LText, Text)
 
 import qualified Data.ByteString.Char8 as BS
-import qualified Data.ByteString.Lazy.Char8 as BL
-
-import qualified Data.Text as T
+import qualified Data.ByteString.Lazy.Char8 as LBS
 import qualified Data.Text.IO as T
-
-import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.IO as TL
-
 import qualified Relude.Base as Base
 
--- | Polymorfic over string and lifted to 'MonadIO' printing functions.
-class Print a where
-  putStr :: MonadIO m => a -> m ()
-  putStrLn :: MonadIO m => a -> m ()
-
-instance Print T.Text where
-  putStr = liftIO . T.putStr
-  putStrLn = liftIO . T.putStrLn
+----------------------------------------------------------------------------
+-- Text
+----------------------------------------------------------------------------
 
-instance Print TL.Text where
-  putStr = liftIO . TL.putStr
-  putStrLn = liftIO . TL.putStrLn
+-- | Lifted version of 'T.putStr'.
+putText :: MonadIO m => Text -> m ()
+putText = liftIO . T.putStr
+{-# SPECIALIZE putText :: Text -> Base.IO () #-}
+{-# INLINE putText #-}
 
-instance Print BS.ByteString where
-  putStr = liftIO . BS.putStr
-  putStrLn = liftIO . BS.putStrLn
+-- | Lifted version of 'T.putStrLn'.
+putTextLn :: MonadIO m => Text -> m ()
+putTextLn = liftIO . T.putStrLn
+{-# SPECIALIZE putTextLn :: Text -> Base.IO () #-}
+{-# INLINE putTextLn #-}
 
-instance Print BL.ByteString where
-  putStr = liftIO . BL.putStr
-  putStrLn = liftIO . BL.putStrLn
+-- | Lifted version of 'TL.putStr'.
+putLText :: MonadIO m => LText -> m ()
+putLText = liftIO . TL.putStr
+{-# SPECIALIZE putLText :: LText -> Base.IO () #-}
+{-# INLINE putLText #-}
 
-instance Print [Base.Char] where
-  putStr = liftIO . Prelude.putStr
-  putStrLn = liftIO . Prelude.putStrLn
+-- | Lifted version of 'TL.putStrLn'.
+putLTextLn :: MonadIO m => LText -> m ()
+putLTextLn = liftIO . TL.putStrLn
+{-# SPECIALIZE putLTextLn :: LText -> Base.IO () #-}
+{-# INLINE putLTextLn #-}
 
--- | Lifted version of 'Prelude.print'.
-print :: forall a m . (MonadIO m, Base.Show a) => a -> m ()
-print = liftIO . Prelude.print
+----------------------------------------------------------------------------
+-- ByteString
+----------------------------------------------------------------------------
 
--- | Specialized to 'T.Text' version of 'putStr' or forcing type inference.
-putText :: MonadIO m => T.Text -> m ()
-putText = putStr
-{-# SPECIALIZE putText :: T.Text -> Base.IO () #-}
+-- | Lifted version of 'BS.putStr'.
+putBS :: MonadIO m => ByteString -> m ()
+putBS = liftIO . BS.putStr
+{-# SPECIALIZE putBS :: ByteString -> Base.IO () #-}
+{-# INLINE putBS #-}
 
--- | Specialized to 'T.Text' version of 'putStrLn' or forcing type inference.
-putTextLn :: MonadIO m => T.Text -> m ()
-putTextLn = putStrLn
-{-# SPECIALIZE putTextLn :: T.Text -> Base.IO () #-}
+-- | Lifted version of 'BS.putStrLn'.
+putBSLn :: MonadIO m => ByteString -> m ()
+putBSLn = liftIO . BS.putStrLn
+{-# SPECIALIZE putBSLn :: ByteString -> Base.IO () #-}
+{-# INLINE putBSLn #-}
 
--- | Specialized to 'TL.Text' version of 'putStr' or forcing type inference.
-putLText :: MonadIO m => TL.Text -> m ()
-putLText = putStr
-{-# SPECIALIZE putLText :: TL.Text -> Base.IO () #-}
+-- | Lifted version of 'LBS.putStr'.
+putLBS :: MonadIO m => LByteString -> m ()
+putLBS = liftIO . LBS.putStr
+{-# SPECIALIZE putLBS :: LByteString -> Base.IO () #-}
+{-# INLINE putLBS #-}
 
--- | Specialized to 'TL.Text' version of 'putStrLn' or forcing type inference.
-putLTextLn :: MonadIO m => TL.Text -> m ()
-putLTextLn = putStrLn
-{-# SPECIALIZE putLTextLn :: TL.Text -> Base.IO () #-}
+-- | Lifted version of 'LBS.putStrLn'.
+putLBSLn :: MonadIO m => LByteString -> m ()
+putLBSLn = liftIO . LBS.putStrLn
+{-# SPECIALIZE putLBSLn :: LByteString -> Base.IO () #-}
+{-# INLINE putLBSLn #-}
diff --git a/src/Relude/String/Conversion.hs b/src/Relude/String/Conversion.hs
--- a/src/Relude/String/Conversion.hs
+++ b/src/Relude/String/Conversion.hs
@@ -61,9 +61,7 @@
 
 -- $setup
 -- >>> :set -XTypeApplications -XOverloadedStrings
--- >>> import Relude.Base (Int)
--- >>> import Relude.Function (($))
--- >>> import Relude.Print (putStrLn)
+-- >>> import Relude
 
 -- | Type synonym for 'Data.Text.Lazy.Text'.
 type LText = LT.Text
@@ -84,7 +82,7 @@
     --
     -- >>> decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"
     -- "\1087\1072\1090\1072\1082"
-    -- >>> putStrLn $ decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"
+    -- >>> putTextLn $ decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"
     -- патак
     decodeUtf8 :: b -> a
 
@@ -195,12 +193,14 @@
     toLazy :: s -> l
     toStrict :: l -> s
 
+-- | Alias for 'toStrict' function.
 fromLazy :: LazyStrict l s => l -> s
 fromLazy = toStrict
 {-# INLINE fromLazy #-}
 {-# SPECIALIZE fromLazy :: LByteString -> ByteString  #-}
 {-# SPECIALIZE fromLazy :: LText -> Text  #-}
 
+-- | Alias for 'toLazy' function.
 fromStrict :: LazyStrict l s => s -> l
 fromStrict = toLazy
 {-# INLINE fromStrict #-}
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -14,5 +14,4 @@
 import Test.Relude.Property (hedgehogTestTree)
 
 main :: IO ()
-main = do
-    defaultMain hedgehogTestTree
+main = defaultMain hedgehogTestTree
