diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+# 0.1.1
+
+* Add `mapMaybe` and `catMaybes`.
+* Add `mapEither` and `partitionEithers`.
+* Decrease operator precedence for `(...)` and `(....)`.
+* Add fusion rules for `genericTake`.
+* Remove harmful fusion rules for `drop` and `dropWhile`.
+  Cf. https://gitlab.haskell.org/ghc/ghc/-/issues/23021.
+* Fix `instance Monad Infinite` on 32-bit machines.
+  It was violating monad laws once the index exceeds 2^32.
+
 # 0.1
 
 * Initial release.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -55,6 +55,13 @@
 map f ~(a :| as) = f a :| fmap f as
 ```
 
+which is equivalent to
+
+```haskell
+map :: (a -> b) -> NonEmpty a -> NonEmpty b
+map f x = (let a :| _ = x in f a) :| (let _ :| as = x in fmap f as)
+```
+
 Because of it forcing the result to WHNF does not force any of the arguments, e. g., ``Data.List.NonEmpty.map undefined undefined `seq` 1`` returns `1`. This is not the case for normal lists: since there are two constructors, `map` has to inspect the argument before returning anything, and ``Data.List.map undefined undefined `seq` 1`` throws an error.
 
 While `Data.List.Infinite` has a single constructor, we believe that following the example of `Data.List.NonEmpty` is harmful for the majority of applications. Instead the laziness of the API is modeled on the laziness of respective operations on `Data.List`: a function `Data.List.Infinite.foo` operating over `Infinite a` is expected to have the same strictness properties as `Data.List.foo` operating over `[a]`. For instance, ``Data.List.Infinite.map undefined undefined `seq` 1`` diverges.
diff --git a/infinite-list.cabal b/infinite-list.cabal
--- a/infinite-list.cabal
+++ b/infinite-list.cabal
@@ -1,13 +1,14 @@
-cabal-version:   1.18
+cabal-version:   2.2
 name:            infinite-list
-version:         0.1
-license:         BSD3
+version:         0.1.1
+license:         BSD-3-Clause
 license-file:    LICENSE
 maintainer:      andrew.lelechenko@gmail.com
 author:          Bodigrim
 tested-with:
     ghc ==8.0.2 ghc ==8.2.2 ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.4
-    ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.5 ghc ==9.4.3
+    ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.8 ghc ==9.4.8 ghc ==9.6.3
+    ghc ==9.8.1
 
 homepage:        https://github.com/Bodigrim/infinite-list
 synopsis:        Infinite lists
@@ -50,7 +51,7 @@
     build-depends:    base >=4.9 && <5
 
     if impl(ghc <8.2)
-        build-depends: ghc-prim
+        build-depends: ghc-prim <1
 
 test-suite infinite-properties
     type:             exitcode-stdio-1.0
@@ -65,6 +66,19 @@
         tasty,
         tasty-quickcheck
 
+test-suite infinite-properties-O0
+    type:             exitcode-stdio-1.0
+    main-is:          Properties.hs
+    hs-source-dirs:   test
+    default-language: Haskell2010
+    ghc-options:      -Wall -O0
+    build-depends:
+        base,
+        infinite-list,
+        QuickCheck,
+        tasty,
+        tasty-quickcheck
+
 test-suite infinite-fusion
     type:             exitcode-stdio-1.0
     main-is:          Fusion.hs
@@ -91,3 +105,6 @@
         base,
         infinite-list,
         tasty-bench
+
+    if impl(ghc >=8.6)
+        ghc-options: -fproc-alignment=64
diff --git a/src/Data/List/Infinite.hs b/src/Data/List/Infinite.hs
--- a/src/Data/List/Infinite.hs
+++ b/src/Data/List/Infinite.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
@@ -85,10 +84,14 @@
   stripPrefix,
 
   -- * Searching
+  filter,
   lookup,
   find,
-  filter,
+  mapMaybe,
+  catMaybes,
   partition,
+  mapEither,
+  partitionEithers,
 
   -- * Indexing
   (!!),
@@ -152,12 +155,14 @@
 import Data.Bits ((.&.))
 import Data.Char (Char, isSpace)
 import Data.Coerce (coerce)
+import Data.Either (Either, either)
 import Data.Eq (Eq, (/=), (==))
 import qualified Data.Foldable as F
 import Data.Functor (Functor (..))
 import qualified Data.List as List
 import Data.List.NonEmpty (NonEmpty (..))
 import qualified Data.List.NonEmpty as NE
+import Data.Maybe (maybe)
 import Data.Ord (Ord, Ordering (..), compare, (<), (<=), (>), (>=))
 import qualified GHC.Exts
 import Numeric.Natural (Natural)
@@ -173,7 +178,8 @@
 import Data.List.Infinite.Zip
 
 -- | Right-associative fold of an infinite list, necessarily lazy in the accumulator.
--- Any unconditional attempt to force the accumulator even to WHNF
+-- Any unconditional attempt to force the accumulator even
+-- to the weak head normal form (WHNF)
 -- will hang the computation. E. g., the following definition isn't productive:
 --
 -- > import Data.List.NonEmpty (NonEmpty(..))
@@ -182,6 +188,8 @@
 -- One should use lazy patterns, e. g.,
 --
 -- > toNonEmpty = foldr (\a ~(x :| xs) -> a :| x : xs)
+--
+-- This is a catamorphism on infinite lists.
 foldr :: (a -> b -> b) -> Infinite a -> b
 foldr f = go
   where
@@ -197,7 +205,14 @@
     cons x (g cons)
   #-}
 
--- | Convert to a list. Use 'cycle' to go in another direction.
+-- | Paramorphism on infinite lists.
+para :: forall a b. (a -> Infinite a -> b -> b) -> Infinite a -> b
+para f = go
+  where
+    go :: Infinite a -> b
+    go (x :< xs) = f x xs (go xs)
+
+-- | Convert to a list. Use 'cycle' to go in the opposite direction.
 toList :: Infinite a -> [a]
 toList = foldr (:)
 {-# NOINLINE [0] toList #-}
@@ -208,7 +223,7 @@
     GHC.Exts.build (\cons -> const (foldr cons xs))
   #-}
 
--- | Generate infinite sequences, starting from a given element,
+-- | Generate an infinite progression, starting from a given element,
 -- similar to @[x..]@.
 -- For better user experience consider enabling @{\-# LANGUAGE PostfixOperators #-\}@:
 --
@@ -221,10 +236,16 @@
 -- >>> :set -XPostfixOperators
 -- >>> Data.List.Infinite.take 10 (EQ...)
 -- [EQ,GT,EQ,GT,EQ,GT,EQ,GT,EQ,GT]
+--
+-- Remember that 'Int' is a finite type as well. One is unlikely to hit this
+-- on a 64-bit architecture, but on a 32-bit machine it's fairly possible to traverse
+-- @((0 :: 'Int') ...)@ far enough to encounter @0@ again.
 (...) :: Enum a => a -> Infinite a
 (...) = unsafeCycle . enumFrom
 {-# INLINE [0] (...) #-}
 
+infix 0 ...
+
 {-# RULES
 "ellipsis3Int" (...) = ellipsis3Int
 "ellipsis3Word" (...) = ellipsis3Word
@@ -248,7 +269,7 @@
 ellipsis3Natural = iterate' (+ 1)
 {-# INLINE ellipsis3Natural #-}
 
--- | Generate infinite sequences, starting from given elements,
+-- | Generate an infinite arithmetic progression, starting from given elements,
 -- similar to @[x,y..]@.
 -- For better user experience consider enabling @{\-# LANGUAGE PostfixOperators #-\}@:
 --
@@ -261,10 +282,16 @@
 -- >>> :set -XPostfixOperators
 -- >>> Data.List.Infinite.take 10 ((EQ,GT)....)
 -- [EQ,GT,EQ,GT,EQ,GT,EQ,GT,EQ,GT]
+--
+-- Remember that 'Int' is a finite type as well: for a sufficiently large
+-- step of progression @y - x@ one may observe @((x :: Int, y)....)@ cycling back
+-- to emit @x@ fairly soon.
 (....) :: Enum a => (a, a) -> Infinite a
 (....) = unsafeCycle . uncurry enumFromThen
 {-# INLINE [0] (....) #-}
 
+infix 0 ....
+
 {-# RULES
 "ellipsis4Int" (....) = ellipsis4Int
 "ellipsis4Word" (....) = ellipsis4Word
@@ -322,15 +349,19 @@
 
 -- | 'Control.Applicative.ZipList' cannot be made a lawful 'Monad',
 -- but 'Infinite', being a
--- <https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable Representable>,
+-- [@Representable@](https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable),
 -- can. Namely, 'Control.Monad.join'
 -- picks up a diagonal of an infinite matrix of 'Infinite' ('Infinite' @a@).
--- This is mostly useful for parallel list comprehensions once
--- @{\-# LANGUAGE MonadComprehensions #-\}@ is enabled.
+-- Bear in mind that this instance gets slow
+-- very soon because of linear indexing, so it is not recommended to be used
+-- in practice.
 instance Monad Infinite where
   xs >>= f = go 0 xs
     where
-      go n (y :< ys) = f y !! n :< go (n + 1) ys
+      go !n (y :< ys) = (f y `index` n) :< go (n + 1) ys
+      index :: Infinite a -> Natural -> a
+      index ys n = head (genericDrop n ys)
+  {-# INLINE (>>=) #-}
   (>>) = (*>)
 
 -- | Get the first elements of an infinite list.
@@ -379,6 +410,10 @@
   #-}
 
 -- | Flatten out an infinite list of non-empty lists.
+--
+-- The peculiar type with 'NonEmpty' is to guarantee that 'concat'
+-- is productive and results in an infinite list. Otherwise the
+-- concatenation of infinitely many @[a]@ could still be a finite list.
 concat :: Infinite (NonEmpty a) -> Infinite a
 concat = foldr (\(x :| xs) acc -> x :< (xs `prependList` acc))
 {-# NOINLINE [1] concat #-}
@@ -390,6 +425,10 @@
   #-}
 
 -- | First 'map' every element, then 'concat'.
+--
+-- The peculiar type with 'NonEmpty' is to guarantee that 'concatMap'
+-- is productive and results in an infinite list. Otherwise the
+-- concatenation of infinitely many @[b]@ could still be a finite list.
 concatMap :: (a -> NonEmpty b) -> Infinite a -> Infinite b
 concatMap f = foldr (\a acc -> let (x :| xs) = f a in x :< (xs `prependList` acc))
 {-# NOINLINE [1] concatMap #-}
@@ -417,6 +456,10 @@
 
 -- | Insert a non-empty list between adjacent elements of an infinite list,
 -- and subsequently flatten it out.
+--
+-- The peculiar type with 'NonEmpty' is to guarantee that 'intercalate'
+-- is productive and results in an infinite list. If separator is an empty list,
+-- concatenation of infinitely many @[a]@ could still be a finite list.
 intercalate :: NonEmpty a -> Infinite [a] -> Infinite a
 intercalate ~(a :| as) = foldr (\xs -> prependList xs . (a :<) . prependList as)
 {-# NOINLINE [1] intercalate #-}
@@ -430,7 +473,7 @@
 -- | Transpose rows and columns of an argument.
 --
 -- This is actually @distribute@ from
--- <https://hackage.haskell.org/package/distributive/docs/Data-Distributive.html#t:Distributive Distributive>
+-- [@Distributive@](https://hackage.haskell.org/package/distributive/docs/Data-Distributive.html#t:Distributive)
 -- type class in disguise.
 transpose :: Functor f => f (Infinite a) -> Infinite (f a)
 transpose xss = fmap head xss :< transpose (fmap tail xss)
@@ -441,9 +484,12 @@
 
 -- | Generate an infinite list of all non-empty subsequences of the argument.
 subsequences1 :: Infinite a -> Infinite (NonEmpty a)
-subsequences1 (x :< xs) = (x :| []) :< foldr f (subsequences1 xs)
+subsequences1 = foldr go
   where
-    f ys r = ys :< (x `NE.cons` ys) :< r
+    go :: a -> Infinite (NonEmpty a) -> Infinite (NonEmpty a)
+    go x sxs = (x :| []) :< foldr f sxs
+      where
+        f ys r = ys :< (x `NE.cons` ys) :< r
 
 -- | Generate an infinite list of all permutations of the argument.
 permutations :: Infinite a -> Infinite (Infinite a)
@@ -461,12 +507,12 @@
           where
             (us, zs) = interleaveList' (f . (y :<)) ys r
 
--- |
+-- | Fold an infinite list from the left and return a list of successive reductions,
+-- starting from the initial accumulator:
+--
 -- > scanl f acc (x1 :< x2 :< ...) = acc :< f acc x1 :< f (f acc x1) x2 :< ...
 scanl :: (b -> a -> b) -> b -> Infinite a -> Infinite b
-scanl f = go
-  where
-    go z ~(x :< xs) = z :< go (f z x) xs
+scanl f z0 = (z0 :<) . flip (foldr (\x acc z -> let fzx = f z x in fzx :< acc fzx)) z0
 
 scanlFB :: (elt' -> elt -> elt') -> (elt' -> lst -> lst) -> elt -> (elt' -> lst) -> elt' -> lst
 scanlFB f cons = \elt g -> oneShot (\x -> let elt' = f x elt in elt' `cons` g elt')
@@ -486,9 +532,7 @@
 
 -- | Same as 'scanl', but strict in accumulator.
 scanl' :: (b -> a -> b) -> b -> Infinite a -> Infinite b
-scanl' f = go
-  where
-    go !z ~(x :< xs) = z :< go (f z x) xs
+scanl' f z0 = (z0 :<) . flip (foldr (\x acc z -> let !fzx = f z x in fzx :< acc fzx)) z0
 
 scanlFB' :: (elt' -> elt -> elt') -> (elt' -> lst -> lst) -> elt -> (elt' -> lst) -> elt' -> lst
 scanlFB' f cons = \elt g -> oneShot (\x -> let !elt' = f x elt in elt' `cons` g elt')
@@ -506,24 +550,25 @@
     tail (scanl' f a bs)
   #-}
 
--- |
+-- | Fold an infinite list from the left and return a list of successive reductions,
+-- starting from the first element:
+--
 -- > scanl1 f (x0 :< x1 :< x2 :< ...) = x0 :< f x0 x1 :< f (f x0 x1) x2 :< ...
 scanl1 :: (a -> a -> a) -> Infinite a -> Infinite a
 scanl1 f (x :< xs) = scanl f x xs
 
--- | If you are looking how to traverse with a state, look no further:
+-- | Fold an infinite list from the left and return a list of successive reductions,
+-- keeping accumulator in a state:
 --
 -- > mapAccumL f acc0 (x1 :< x2 :< ...) =
 -- >   let (acc1, y1) = f acc0 x1 in
 -- >     let (acc2, y2) = f acc1 x2 in
 -- >       ...
 -- >         y1 :< y2 :< ...
+--
+-- If you are looking how to traverse with a state, look no further.
 mapAccumL :: (acc -> x -> (acc, y)) -> acc -> Infinite x -> Infinite y
-mapAccumL f = go
-  where
-    go s (x :< xs) = y :< go s' xs
-      where
-        (s', y) = f s x
+mapAccumL f = flip (foldr (\x acc s -> let (s', y) = f s x in y :< acc s'))
 
 mapAccumLFB :: (acc -> x -> (acc, y)) -> x -> (acc -> Infinite y) -> acc -> Infinite y
 mapAccumLFB f = \x r -> oneShot (\s -> let (s', y) = f s x in y :< r s')
@@ -604,6 +649,9 @@
 -- | Repeat a non-empty list ad infinitum.
 -- If you were looking for something like @fromList :: [a] -> Infinite a@,
 -- look no further.
+--
+-- It would be less annoying to take @[a]@ instead of 'NonEmpty' @a@,
+-- but we strive to avoid partial functions.
 cycle :: NonEmpty a -> Infinite a
 cycle (x :| xs) = unsafeCycle (x : xs)
 {-# INLINE cycle #-}
@@ -628,6 +676,8 @@
   #-}
 
 -- | Build an infinite list from a seed value.
+--
+-- This is an anamorphism on infinite lists.
 unfoldr :: (b -> (a, b)) -> b -> Infinite a
 unfoldr f = go
   where
@@ -637,7 +687,7 @@
 -- | Generate an infinite list of @f@ 0, @f@ 1, @f@ 2...
 --
 -- 'tabulate' and '(!!)' witness that 'Infinite' is
--- <https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable Representable>.
+-- [@Representable@](https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable).
 tabulate :: (Word -> a) -> Infinite a
 tabulate f = unfoldr (\n -> (f n, n + 1)) 0
 {-# INLINE tabulate #-}
@@ -645,70 +695,46 @@
 -- | Take a prefix of given length.
 take :: Int -> Infinite a -> [a]
 take = GHC.Exts.inline genericTake
-
-takeFB :: (elt -> lst -> lst) -> lst -> elt -> (Int -> lst) -> Int -> lst
-takeFB cons nil x xs = \m -> if m <= 1 then x `cons` nil else x `cons` xs (m - 1)
-
 {-# INLINE [1] take #-}
 
-{-# INLINE [0] takeFB #-}
+{-# INLINE [1] genericTake #-}
 
+{-# INLINE [0] genericTakeFB #-}
+
 {-# RULES
-"take" [~1] forall n xs.
-  take n xs =
+"take"
+  take =
+    genericTake
+"genericTake" [~1] forall n xs.
+  genericTake n xs =
     GHC.Exts.build
       ( \cons nil ->
           if n >= 1
-            then foldr (takeFB cons nil) xs n
+            then foldr (genericTakeFB cons nil) xs n
             else nil
       )
-"takeList" [1] forall n xs.
-  foldr (takeFB (:) []) xs n =
-    take n xs
+"genericTakeList" [1] forall n xs.
+  foldr (genericTakeFB (:) []) xs n =
+    genericTake n xs
   #-}
 
 -- | Take a prefix of given length.
 genericTake :: Integral i => i -> Infinite a -> [a]
 genericTake n
   | n < 1 = const []
-  | otherwise = unsafeTake n
-  where
-    unsafeTake 1 (x :< _) = [x]
-    unsafeTake m (x :< xs) = x : unsafeTake (m - 1) xs
+  | otherwise = flip (foldr (\hd f m -> hd : (if m <= 1 then [] else f (m - 1)))) n
 
+genericTakeFB :: Integral i => (elt -> lst -> lst) -> lst -> elt -> (i -> lst) -> i -> lst
+genericTakeFB cons nil x xs = \m -> if m <= 1 then x `cons` nil else x `cons` xs (m - 1)
+
 -- | Drop a prefix of given length.
 drop :: Int -> Infinite a -> Infinite a
 drop = GHC.Exts.inline genericDrop
 
-dropFB :: (elt -> lst -> lst) -> elt -> (Int -> lst) -> Int -> lst
-dropFB cons x xs = \m -> if m < 1 then x `cons` xs m else xs (m - 1)
-
-{-# INLINE [1] drop #-}
-
-{-# INLINE [0] dropFB #-}
-
-{-# RULES
-"drop" [~1] forall n xs.
-  drop n xs =
-    build
-      ( \cons ->
-          if n >= 1
-            then foldr (dropFB cons) xs n
-            else foldr cons xs
-      )
-"dropList" [1] forall n xs.
-  foldr (dropFB (:<)) xs n =
-    drop n xs
-  #-}
-
 -- | Drop a prefix of given length.
 genericDrop :: Integral i => i -> Infinite a -> Infinite a
-genericDrop n
-  | n < 1 = id
-  | otherwise = unsafeDrop n
-  where
-    unsafeDrop 1 (_ :< xs) = xs
-    unsafeDrop m (_ :< xs) = unsafeDrop (m - 1) xs
+genericDrop = flip (para (\hd tl f m -> if m < 1 then hd :< tl else f (m - 1)))
+{-# INLINEABLE genericDrop #-}
 
 -- | Split an infinite list into a prefix of given length and the rest.
 splitAt :: Int -> Infinite a -> ([a], Infinite a)
@@ -718,18 +744,12 @@
 genericSplitAt :: Integral i => i -> Infinite a -> ([a], Infinite a)
 genericSplitAt n
   | n < 1 = ([],)
-  | otherwise = unsafeSplitAt n
-  where
-    unsafeSplitAt 1 (x :< xs) = ([x], xs)
-    unsafeSplitAt m (x :< xs) = first (x :) (unsafeSplitAt (m - 1) xs)
+  | otherwise = flip (para (\hd tl f m -> if m <= 1 then ([hd], tl) else first (hd :) (f (m - 1)))) n
+{-# INLINEABLE genericSplitAt #-}
 
 -- | Take the longest prefix satisfying a predicate.
 takeWhile :: (a -> Bool) -> Infinite a -> [a]
-takeWhile p = go
-  where
-    go (x :< xs)
-      | p x = x : go xs
-      | otherwise = []
+takeWhile p = foldr (\x xs -> if p x then x : xs else [])
 
 takeWhileFB :: (elt -> Bool) -> (elt -> lst -> lst) -> lst -> elt -> lst -> lst
 takeWhileFB p cons nil = \x r -> if p x then x `cons` r else nil
@@ -752,27 +772,7 @@
 -- This function isn't productive (e. g., 'head' . 'dropWhile' @f@ won't terminate),
 -- if all elements of the input list satisfy the predicate.
 dropWhile :: (a -> Bool) -> Infinite a -> Infinite a
-dropWhile p = go
-  where
-    go xxs@(x :< xs)
-      | p x = go xs
-      | otherwise = xxs
-
-dropWhileFB :: (elt -> Bool) -> (elt -> lst -> lst) -> elt -> (Bool -> lst) -> (Bool -> lst)
-dropWhileFB p cons = \x r drp -> if drp && p x then r True else x `cons` r False
-
-{-# NOINLINE [1] dropWhile #-}
-
-{-# INLINE [0] dropWhileFB #-}
-
-{-# RULES
-"dropWhile" [~1] forall p xs.
-  dropWhile p xs =
-    build (\cons -> foldr (dropWhileFB p cons) xs True)
-"dropWhileList" [1] forall p xs.
-  foldr (dropWhileFB p (:<)) xs True =
-    dropWhile p xs
-  #-}
+dropWhile p = para (\x xs -> if p x then id else const (x :< xs))
 
 -- | Split an infinite list into the longest prefix satisfying a predicate and the rest.
 --
@@ -780,11 +780,7 @@
 -- (e. g., 'head' . 'snd' . 'span' @f@ won't terminate),
 -- if all elements of the input list satisfy the predicate.
 span :: (a -> Bool) -> Infinite a -> ([a], Infinite a)
-span p = go
-  where
-    go xxs@(x :< xs)
-      | p x = first (x :) (go xs)
-      | otherwise = ([], xxs)
+span p = para (\x xs -> if p x then first (x :) else const ([], x :< xs))
 
 -- | Split an infinite list into the longest prefix /not/ satisfying a predicate and the rest.
 --
@@ -797,10 +793,12 @@
 -- | If a list is a prefix of an infinite list, strip it and return the rest.
 -- Otherwise return 'Nothing'.
 stripPrefix :: Eq a => [a] -> Infinite a -> Maybe (Infinite a)
-stripPrefix [] ys = Just ys
-stripPrefix (x : xs) (y :< ys)
-  | x == y = stripPrefix xs ys
-  | otherwise = Nothing
+stripPrefix [] = Just
+stripPrefix (p : ps) = flip (para alg) (p :| ps)
+  where
+    alg x xs acc (y :| ys)
+      | x == y = maybe (Just xs) acc (NE.nonEmpty ys)
+      | otherwise = Nothing
 
 -- | Group consecutive equal elements.
 group :: Eq a => Infinite a -> Infinite (NonEmpty a)
@@ -808,6 +806,9 @@
 
 -- | Overloaded version of 'group'.
 groupBy :: (a -> a -> Bool) -> Infinite a -> Infinite (NonEmpty a)
+-- Quite surprisingly, 'groupBy' is not a simple catamorphism.
+-- Since @f@ is not guaranteed to be transitive, it's a full-blown
+-- histomorphism, at which point a manual recursion becomes much more readable.
 groupBy f = go
   where
     go (x :< xs) = (x :| ys) :< go zs
@@ -846,10 +847,10 @@
 
 -- | Check whether a list is a prefix of an infinite list.
 isPrefixOf :: Eq a => [a] -> Infinite a -> Bool
-isPrefixOf [] _ = True
-isPrefixOf (x : xs) (y :< ys)
-  | x == y = isPrefixOf xs ys
-  | otherwise = False
+isPrefixOf [] = const True
+isPrefixOf (p : ps) = flip (foldr alg) (p :| ps)
+  where
+    alg x acc (y :| ys) = x == y && maybe True acc (NE.nonEmpty ys)
 
 -- | Find the first pair, whose first component is equal to the first argument,
 -- and return the second component.
@@ -866,6 +867,13 @@
 --
 -- This function isn't productive (e. g., 'head' . 'filter' @f@ won't terminate),
 -- if no elements of the input list satisfy the predicate.
+--
+-- A common objection is that since it could happen that no elements of the input
+-- satisfy the predicate, the return type should be @[a]@ instead of 'Infinite' @a@.
+-- This would not however make 'filter' any more productive. Note that such
+-- hypothetical 'filter' could not ever generate @[]@ constructor, only @(:)@, so
+-- we would just have a more lax type gaining nothing instead. Same reasoning applies
+-- to other filtering \/ partitioning \/ searching functions.
 filter :: (a -> Bool) -> Infinite a -> Infinite a
 filter f = foldr (\a -> if f a then (a :<) else id)
 
@@ -906,13 +914,10 @@
 -- to avoid 'Prelude.error' on negative arguments.
 --
 -- This is actually @index@ from
--- <https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable Representable>
+-- [@Representable@](https://hackage.haskell.org/package/adjunctions/docs/Data-Functor-Rep.html#t:Representable)
 -- type class in disguise.
 (!!) :: Infinite a -> Word -> a
-(!!) = flip go
-  where
-    go 0 (x :< _) = x
-    go !m (_ :< ys) = go (m - 1) ys
+(!!) = foldr (\x acc m -> if m == 0 then x else acc (m - 1))
 
 infixl 9 !!
 
@@ -931,20 +936,14 @@
 -- | Return an index of the first element, satisfying a predicate.
 -- If there is nothing to be found, this function will hang indefinitely.
 findIndex :: (a -> Bool) -> Infinite a -> Word
-findIndex f = go 0
-  where
-    go !n (x :< xs)
-      | f x = n
-      | otherwise = go (n + 1) xs
+findIndex f = flip (foldr (\x acc !m -> if f x then m else acc (m + 1))) 0
 
 -- | Return indices of all elements, satisfying a predicate.
 --
 -- This function isn't productive (e. g., 'head' . 'elemIndices' @f@ won't terminate),
 -- if no elements of the input list satisfy the predicate.
 findIndices :: (a -> Bool) -> Infinite a -> Infinite Word
-findIndices f = go 0
-  where
-    go !n (x :< xs) = (if f x then (n :<) else id) (go (n + 1) xs)
+findIndices f = flip (foldr (\x acc !m -> (if f x then (m :<) else id) (acc (m + 1)))) 0
 
 -- | Unzip an infinite list of tuples.
 unzip :: Infinite (a, b) -> (Infinite a, Infinite b)
@@ -976,30 +975,49 @@
 unzip7 = foldr (\(a, b, c, d, e, f, g) ~(as, bs, cs, ds, es, fs, gs) -> (a :< as, b :< bs, c :< cs, d :< ds, e :< es, f :< fs, g :< gs))
 {-# INLINE unzip7 #-}
 
--- | Split an infinite string into lines, by @\\n@.
+-- | Split an infinite string into lines, by @\\n@. Empty lines are preserved.
+--
+-- In contrast to their counterparts from "Data.List", it holds that
+-- 'unlines' @.@ 'lines' @=@ 'id'.
 lines :: Infinite Char -> Infinite [Char]
-lines xs = l :< lines xs'
+lines = foldr go
   where
-    (l, ~(_ :< xs')) = break (== '\n') xs
+    go '\n' xs = [] :< xs
+    go c ~(x :< xs) = (c : x) :< xs
 
 -- | Concatenate lines together with @\\n@.
+--
+-- In contrast to their counterparts from "Data.List", it holds that
+-- 'unlines' @.@ 'lines' @=@ 'id'.
 unlines :: Infinite [Char] -> Infinite Char
 unlines = foldr (\l xs -> l `prependList` ('\n' :< xs))
 
 -- | Split an infinite string into words, by any 'isSpace' symbol.
+-- Leading spaces are removed and, as underlined by the return type,
+-- repeated spaces are treated as a single delimiter.
 words :: Infinite Char -> Infinite (NonEmpty Char)
-words xs = (u :| us) :< words vs
+-- This is fundamentally a zygomorphism with 'isSpace' . 'head' as the small algebra.
+-- But manual implementation via catamorphism requires twice less calls of 'isSpace'.
+words = uncurry repack . foldr go
   where
-    u :< ys = dropWhile isSpace xs
-    (us, vs) = break isSpace ys
+    repack zs acc = maybe acc (:< acc) (NE.nonEmpty zs)
 
+    go x ~(zs, acc) = (zs', acc')
+      where
+        s = isSpace x
+        zs' = if s then [] else x : zs
+        acc' = if s then repack zs acc else acc
+
 wordsFB :: (NonEmpty Char -> lst -> lst) -> Infinite Char -> lst
-wordsFB cons = go
+wordsFB cons = uncurry repack . foldr go
   where
-    go xs = (u :| us) `cons` go vs
+    repack zs acc = maybe acc (`cons` acc) (NE.nonEmpty zs)
+
+    go x ~(zs, acc) = (zs', acc')
       where
-        u :< ys = dropWhile isSpace xs
-        (us, vs) = break isSpace ys
+        s = isSpace x
+        zs' = if s then [] else x : zs
+        acc' = if s then repack zs acc else acc
 
 {-# NOINLINE [1] words #-}
 
@@ -1011,6 +1029,10 @@
   #-}
 
 -- | Concatenate words together with a space.
+--
+-- The function is meant to be a counterpart of with 'words'.
+-- If you need to concatenate together 'Infinite' @[@'Char'@]@,
+-- use 'intercalate' @(@'pure' @' ')@.
 unwords :: Infinite (NonEmpty Char) -> Infinite Char
 unwords = foldr (\(l :| ls) acc -> l :< ls `prependList` (' ' :< acc))
 
@@ -1032,14 +1054,7 @@
 
 -- | Overloaded version of 'nub'.
 nubBy :: (a -> a -> Bool) -> Infinite a -> Infinite a
-nubBy eq = go []
-  where
-    go seen (x :< xs)
-      | elemBy x seen = go seen xs
-      | otherwise = x :< go (x : seen) xs
-
-    elemBy _ [] = False
-    elemBy y (x : xs) = eq x y || elemBy y xs
+nubBy eq = flip (foldr (\x acc seen -> if List.any (`eq` x) seen then acc seen else x :< acc (x : seen))) []
 
 -- | Remove all occurrences of an element from an infinite list.
 delete :: Eq a => a -> Infinite a -> Infinite a
@@ -1047,11 +1062,7 @@
 
 -- | Overloaded version of 'delete'.
 deleteBy :: (a -> b -> Bool) -> a -> Infinite b -> Infinite b
-deleteBy eq x = go
-  where
-    go (y :< ys)
-      | eq x y = ys
-      | otherwise = y :< go ys
+deleteBy eq x = para (\y ys acc -> if eq x y then ys else y :< acc)
 
 -- | Take an infinite list and remove the first occurrence of every element
 -- of a finite list.
@@ -1079,11 +1090,7 @@
 
 -- | Overloaded version of 'insert'.
 insertBy :: (a -> a -> Ordering) -> a -> Infinite a -> Infinite a
-insertBy cmp x = go
-  where
-    go yys@(y :< ys) = case cmp x y of
-      GT -> y :< go ys
-      _ -> x :< yys
+insertBy cmp x = para (\y ys acc -> case cmp x y of GT -> y :< acc; _ -> x :< y :< ys)
 
 -- | Return all elements of an infinite list, which are simultaneously
 -- members of a finite list.
@@ -1097,3 +1104,42 @@
 -- | Prepend a list to an infinite list.
 prependList :: [a] -> Infinite a -> Infinite a
 prependList = flip (F.foldr (:<))
+
+-- | Apply a function to every element of an infinite list and collect 'Just' results.
+--
+-- This function isn't productive (e. g., 'head' . 'mapMaybe' @f@ won't terminate),
+-- if no elements of the input list result in 'Just'.
+--
+-- @since 0.1.1
+mapMaybe :: (a -> Maybe b) -> Infinite a -> Infinite b
+mapMaybe = foldr . (maybe id (:<) .)
+
+-- | Keep only 'Just' elements.
+--
+-- This function isn't productive (e. g., 'head' . 'catMaybes' won't terminate),
+-- if no elements of the input list are 'Just'.
+--
+-- @since 0.1.1
+catMaybes :: Infinite (Maybe a) -> Infinite a
+catMaybes = foldr (maybe id (:<))
+
+-- | Apply a function to every element of an infinite list and
+-- separate 'Data.Either.Left' and 'Data.Either.Right' results.
+--
+-- This function isn't productive (e. g., 'head' . 'Data.Tuple.fst' .
+-- 'mapEither' @f@ won't terminate),
+-- if no elements of the input list result in 'Data.Either.Left' or 'Data.Either.Right'.
+--
+-- @since 0.1.1
+mapEither :: (a -> Either b c) -> Infinite a -> (Infinite b, Infinite c)
+mapEither = foldr . (either (first . (:<)) (second . (:<)) .)
+
+-- | Separate 'Data.Either.Left' and 'Data.Either.Right' elements.
+--
+-- This function isn't productive (e. g., 'head' . 'Data.Tuple.fst' . 'partitionEithers'
+-- won't terminate),
+-- if no elements of the input list are 'Data.Either.Left' or 'Data.Either.Right'.
+--
+-- @since 0.1.1
+partitionEithers :: Infinite (Either a b) -> (Infinite a, Infinite b)
+partitionEithers = foldr (either (first . (:<)) (second . (:<)))
diff --git a/src/Data/List/Infinite/Internal.hs b/src/Data/List/Infinite/Internal.hs
--- a/src/Data/List/Infinite/Internal.hs
+++ b/src/Data/List/Infinite/Internal.hs
@@ -9,6 +9,9 @@
 ) where
 
 -- | Type of infinite lists.
+--
+-- In terms of recursion schemes, 'Infinite' @a@ is a fix point of the base functor @(a,)@,
+-- 'Data.List.Infinite.foldr' is a catamorphism and 'Data.List.Infinite.unfoldr' is an anamorphism.
 data Infinite a = a :< Infinite a
 
 infixr 5 :<
diff --git a/test/Fusion.hs b/test/Fusion.hs
--- a/test/Fusion.hs
+++ b/test/Fusion.hs
@@ -67,15 +67,9 @@
 takeRepeat :: Int -> [Int]
 takeRepeat x = I.take x (I.repeat x)
 
-takeDropRepeat :: Int -> [Int]
-takeDropRepeat x = I.take x (I.drop x (I.repeat x))
-
 takeWhileIterate :: Int -> [Int]
 takeWhileIterate x = I.takeWhile (< 10) (I.iterate (+ 1) x)
 
-takeWhileDropWhileIterate :: Int -> [Int]
-takeWhileDropWhileIterate x = I.takeWhile (< 20) $ I.dropWhile (< 10) (I.iterate (+ 1) x)
-
 foldrCycle :: NonEmpty Int -> [Int]
 foldrCycle xs = I.foldr (:) (I.cycle xs)
 
@@ -261,9 +255,7 @@
   , $(inspectTest $ 'foldrScanl `hasNoType` ''Word)
   , $(inspectTest $ 'foldrScanl' `hasNoType` ''Word)
   , $(inspectTest $ 'takeRepeat `hasNoType` ''Infinite)
-  , $(inspectTest $ 'takeDropRepeat `hasNoType` ''Infinite)
   , $(inspectTest $ 'takeWhileIterate `hasNoType` ''Infinite)
-  , $(inspectTest $ 'takeWhileDropWhileIterate `hasNoType` ''Infinite)
   , $(inspectTest $ 'foldrCycle `hasNoType` ''Infinite)
   , $(inspectTest $ 'foldrWordsCycle `hasNoType` ''NonEmpty)
   , $(inspectTest $ 'mapAccumLRepeat `hasNoType` ''Word)
diff --git a/test/Properties.hs b/test/Properties.hs
--- a/test/Properties.hs
+++ b/test/Properties.hs
@@ -7,7 +7,9 @@
 {-# LANGUAGE TupleSections       #-}
 {-# LANGUAGE ViewPatterns        #-}
 
-{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+{-# OPTIONS_GHC -Wno-unrecognised-warning-flags #-}
+{-# OPTIONS_GHC -Wno-x-partial #-}
 
 {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
 {-# HLINT ignore "Use <$>" #-}
@@ -23,13 +25,17 @@
 import Control.Applicative
 import Control.Monad
 import Data.Bifunctor
+import Data.Bits
+import Data.Either
 import qualified Data.List as L
 import Data.List.Infinite (Infinite(..))
 import qualified Data.List.Infinite as I
 import Data.List.NonEmpty (NonEmpty(..))
 import qualified Data.List.NonEmpty as NE
 import Data.Maybe
+import Data.Word (Word32)
 import Numeric.Natural
+import Prelude hiding (Applicative(..))
 
 instance Arbitrary a => Arbitrary (Infinite a) where
   arbitrary = (:<) <$> arbitrary <*> arbitrary
@@ -47,426 +53,462 @@
 mapMapFusion :: Infinite Int -> Infinite Int
 mapMapFusion xs = I.map fromIntegral (I.map fromIntegral xs :: Infinite Word)
 
+mapEither :: (a -> Either b c) -> [a] -> ([b], [c])
+mapEither f = foldr (either (first . (:)) (second . (:)) . f) ([], [])
+
 main :: IO ()
 main = defaultMain $ testGroup "All"
   [ testProperty "head" $
     \(Blind (xs :: Infinite Int)) ->
-      I.head xs == L.head (trim xs)
+      I.head xs === L.head (trim xs)
   , testProperty "tail" $
     \(Blind (xs :: Infinite Int)) ->
-      trim (I.tail xs) == L.tail (trim1 xs)
+      trim (I.tail xs) === L.tail (trim1 xs)
   , testProperty "uncons" $
     \(Blind (xs :: Infinite Int)) ->
-      Just (fmap trim (I.uncons xs)) == L.uncons (trim1 xs)
+      Just (fmap trim (I.uncons xs)) === L.uncons (trim1 xs)
 
   , testProperty "map" $
     \(applyFun -> f :: Int -> Word) (Blind (xs :: Infinite Int)) ->
-      trim (I.map f xs) == L.map f (trim xs)
+      trim (I.map f xs) === L.map f (trim xs)
 
   , testProperty "fmap" $
     \(applyFun -> f :: Int -> Int) (Blind (xs :: Infinite Int)) ->
-      trim (fmap f xs) == fmap f (trim xs)
+      trim (fmap f xs) === fmap f (trim xs)
   , testProperty "<$" $
     \(x :: Word) (Blind (xs :: Infinite Int)) ->
-      trim (x <$ xs) == trim (fmap (const x) xs)
+      trim (x <$ xs) === trim (fmap (const x) xs)
 
   , testProperty "pure" $
     \(applyFun -> f :: Int -> Word) (x :: Int) ->
-      trim (pure f <*> pure x) == trim (pure (f x))
+      trim (pure f <*> pure x) === trim (pure (f x))
   , testProperty "*>" $
     \(Blind (xs :: Infinite Int)) (Blind (ys :: Infinite Word)) ->
-      trim (xs *> ys) == trim ((id <$ xs) <*> ys)
+      trim (xs *> ys) === trim ((id <$ xs) <*> ys)
   , testProperty "<*" $
     \(Blind (xs :: Infinite Int)) (Blind (ys :: Infinite Word)) ->
-      trim (xs <* ys) == trim (liftA2 const xs ys)
+      trim (xs <* ys) === trim (liftA2 const xs ys)
 
   , testProperty ">>= 1" $
     \x ((I.cycle .) . applyFun -> k :: Int -> Infinite Word) ->
-      trim (return x >>= k) == trim (k x)
+      trim (return x >>= k) === trim (k x)
   , testProperty ">>= 2" $
     \(Blind (xs :: Infinite Int)) ->
-      trim (xs >>= return) == trim xs
+      trim (xs >>= return) === trim xs
   , testProperty ">>= 3" $
     \(Blind xs) ((I.cycle .) . applyFun -> k :: Int -> Infinite Word)  ((I.cycle .) . applyFun -> h :: Word -> Infinite Char) ->
-      trim (xs >>= (k >=> h)) == trim ((xs >>= k) >>= h)
+      trim (xs >>= (k >=> h)) === trim ((xs >>= k) >>= h)
   , testProperty ">>" $
     \(Blind (xs :: Infinite Int)) (Blind (ys :: Infinite Word)) ->
-      trim (xs >> ys) == trim ys
+      trim (xs >> ys) === trim ys
 
   , testProperty "concat" $
     \(Blind (xs :: Infinite (NonEmpty Int))) ->
-      trim (I.concat xs) == L.take 10 (L.concatMap NE.toList (I.toList xs))
+      trim (I.concat xs) === L.take 10 (L.concatMap NE.toList (I.toList xs))
   , testProperty "concatMap" $
     \(applyFun -> f :: Int -> NonEmpty Word) (Blind xs) ->
-      trim (I.concatMap f xs) == L.take 10 (L.concatMap (NE.toList . f) (I.toList xs))
+      trim (I.concatMap f xs) === L.take 10 (L.concatMap (NE.toList . f) (I.toList xs))
 
   , testProperty "intersperse" $
     \(x :: Int) (Blind xs) ->
-      I.take 19 (I.intersperse x xs) == L.intersperse x (trim xs)
+      I.take 19 (I.intersperse x xs) === L.intersperse x (trim xs)
   , testProperty "intersperse laziness 1" $
-    I.head (I.intersperse undefined ('q' :< undefined)) == 'q'
+    I.head (I.intersperse undefined ('q' :< undefined)) === 'q'
   , testProperty "intersperse laziness 2" $
-    I.take 2 (I.intersperse 'w' ('q' :< undefined)) == "qw"
+    I.take 2 (I.intersperse 'w' ('q' :< undefined)) === "qw"
 
   , testProperty "intercalate" $
     \(x :: NonEmpty Int) (Blind xs) ->
-      I.take (sum (map length (trim xs)) + 9 * length x) (I.intercalate x xs) == L.intercalate (NE.toList x) (trim xs)
+      I.take (sum (map length (trim xs)) + 9 * length x) (I.intercalate x xs) === L.intercalate (NE.toList x) (trim xs)
   , testProperty "intercalate laziness 1" $
-    I.take 3 (I.intercalate undefined ("foo" :< undefined)) == "foo"
+    I.take 3 (I.intercalate undefined ("foo" :< undefined)) === "foo"
   , testProperty "intercalate laziness 2" $
-    I.take 6 (I.intercalate (NE.fromList "bar") ("foo" :< undefined)) == "foobar"
+    I.take 6 (I.intercalate (NE.fromList "bar") ("foo" :< undefined)) === "foobar"
 
   , testProperty "interleave 1" $
     \(Blind (xs :: Infinite Int)) (Blind ys) ->
-      trim (I.map snd (I.filter fst (I.zip (I.cycle (True :| [False])) (I.interleave xs ys)))) == trim xs
+      trim (I.map snd (I.filter fst (I.zip (I.cycle (True :| [False])) (I.interleave xs ys)))) === trim xs
   , testProperty "interleave 2" $
     \(Blind (xs :: Infinite Int)) (Blind ys) ->
-      trim (I.map snd (I.filter fst (I.zip (I.cycle (False :| [True])) (I.interleave xs ys)))) == trim ys
+      trim (I.map snd (I.filter fst (I.zip (I.cycle (False :| [True])) (I.interleave xs ys)))) === trim ys
   , testProperty "interleave laziness" $
-    I.head (I.interleave ('a' :< undefined) undefined) == 'a'
+    I.head (I.interleave ('a' :< undefined) undefined) === 'a'
 
   , testProperty "transpose []" $
     \(fmap getBlind -> xss :: [Infinite Int]) -> not (null xss) ==>
-      trim (I.transpose xss) == L.transpose (map trim xss)
+      trim (I.transpose xss) === L.transpose (map trim xss)
   , testProperty "transpose NE" $
     \(fmap getBlind -> xss :: NonEmpty (Infinite Int)) ->
-      NE.fromList (trim (I.transpose xss)) == NE.transpose (NE.map (NE.fromList . trim) xss)
+      NE.fromList (trim (I.transpose xss)) === NE.transpose (NE.map (NE.fromList . trim) xss)
   , testProperty "transpose laziness 1" $
-    I.head (I.transpose ['a' :< undefined, 'b' :< undefined]) == "ab"
+    I.head (I.transpose ['a' :< undefined, 'b' :< undefined]) === "ab"
   , testProperty "transpose laziness 2" $
-    I.head (I.transpose (('a' :< undefined) :| ['b' :< undefined])) == 'a' :| "b"
+    I.head (I.transpose (('a' :< undefined) :| ['b' :< undefined])) === 'a' :| "b"
 
   , testProperty "subsequences" $
     \(Blind (xs :: Infinite Int)) ->
-      I.take 16 (I.subsequences xs) == L.subsequences (I.take 4 xs)
+      I.take 16 (I.subsequences xs) === L.subsequences (I.take 4 xs)
   , testProperty "subsequences laziness 1" $
-    I.head (I.subsequences undefined) == ""
+    I.head (I.subsequences undefined) === ""
   , testProperty "subsequences laziness 2" $
-    I.take 2 (I.subsequences ('q' :< undefined)) == ["", "q"]
+    I.take 2 (I.subsequences ('q' :< undefined)) === ["", "q"]
 
   , testProperty "permutations" $
     \(Blind (xs :: Infinite Int)) ->
-      map (I.take 4) (I.take 24 (I.permutations xs)) == L.permutations (I.take 4 xs)
+      map (I.take 4) (I.take 24 (I.permutations xs)) === L.permutations (I.take 4 xs)
   , testProperty "permutations laziness" $
-    I.take 6 (I.map (I.take 3) (I.permutations ('q' :< 'w' :< 'e' :< undefined))) == ["qwe","wqe","ewq","weq","eqw","qew"]
+    I.take 6 (I.map (I.take 3) (I.permutations ('q' :< 'w' :< 'e' :< undefined))) === ["qwe","wqe","ewq","weq","eqw","qew"]
 
   , testProperty "... Bool" $
     \(x :: Bool) ->
       trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Int" $
     \(x :: Int) ->
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Int maxBound" $
     \(NonNegative (x' :: Int)) -> let x = maxBound - x' in
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Word" $
     \(x :: Word) ->
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Word maxBound" $
     \(NonNegative (x' :: Word)) -> let x = maxBound - x' in
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Integer" $
     \(x :: Integer) ->
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
   , testProperty "... Natural" $
     \(NonNegative (x' :: Integer)) -> let x = fromInteger x' :: Natural in
-      trim (x I....) == L.take 10 (L.cycle [x..])
+      trim (x I....) === L.take 10 (L.cycle [x..])
 
   , testProperty ".... Bool" $
     \(x :: Bool) y ->
-      trim ((x, y) I.....) == L.take 10 (L.cycle [x, y..])
+      trim ((x, y) I.....) === L.take 10 (L.cycle [x, y..])
   , testProperty ".... Int" $
     \(x :: Int) y ->
-      trim ((x, y) I.....) == L.take 10 (L.cycle [x, y..]) .&&.
-      trim ((maxBound + x, y) I.....) == L.take 10 (L.cycle [maxBound + x, y..]) &&
-      trim ((x, maxBound + y) I.....) == L.take 10 (L.cycle [x, maxBound + y..]) &&
-      trim ((maxBound + x, maxBound + y) I.....) == L.take 10 (L.cycle [maxBound + x, maxBound + y..])
+      trim ((x, y) I.....) === L.take 10 (L.cycle [x, y..]) .&&.
+      trim ((maxBound + x, y) I.....) === L.take 10 (L.cycle [maxBound + x, y..]) .&&.
+      trim ((x, maxBound + y) I.....) === L.take 10 (L.cycle [x, maxBound + y..]) .&&.
+      trim ((maxBound + x, maxBound + y) I.....) === L.take 10 (L.cycle [maxBound + x, maxBound + y..])
   , testProperty ".... Word" $
     \(x :: Word) y ->
-      trim ((x, y) I.....) == L.take 10 (L.cycle [x, y..]) .&&.
-      trim ((maxBound + x, y) I.....) == L.take 10 (L.cycle [maxBound + x, y..]) &&
-      trim ((x, maxBound + y) I.....) == L.take 10 (L.cycle [x, maxBound + y..]) &&
-      trim ((maxBound + x, maxBound + y) I.....) == L.take 10 (L.cycle [maxBound + x, maxBound + y..])
+      trim ((x, y) I.....) === L.take 10 (L.cycle [x, y..]) .&&.
+      trim ((maxBound + x, y) I.....) === L.take 10 (L.cycle [maxBound + x, y..]) .&&.
+      trim ((x, maxBound + y) I.....) === L.take 10 (L.cycle [x, maxBound + y..]) .&&.
+      trim ((maxBound + x, maxBound + y) I.....) === L.take 10 (L.cycle [maxBound + x, maxBound + y..])
   , testProperty ".... Integer" $
     \(x :: Integer) y ->
-      trim ((x, y) I.....) == L.take 10 (L.cycle [x, y..])
+      trim ((x, y) I.....) === L.take 10 (L.cycle [x, y..])
   , testProperty ".... Natural" $
     \(NonNegative (x' :: Integer)) (NonNegative (y' :: Integer)) ->
       let x = fromInteger x' :: Natural in let y = fromInteger y' in
-        trim ((x, y) I.....) == L.take 10 (L.cycle [x, y..])
+        trim ((x, y) I.....) === L.take 10 (L.cycle [x, y..])
 
   , testProperty "toList" $
     \(Blind (xs :: Infinite Int)) ->
-      L.take 10 (I.toList xs) == trim xs
+      L.take 10 (I.toList xs) === trim xs
 
   , testProperty "scanl" $
     \(curry . applyFun -> f :: Word -> Int -> Word) s (Blind xs) ->
-      trim1 (I.scanl f s xs) == L.scanl f s (trim xs)
+      trim1 (I.scanl f s xs) === L.scanl f s (trim xs)
   , testProperty "scanl laziness" $
-    I.head (I.scanl undefined 'q' undefined) == 'q'
+    I.head (I.scanl undefined 'q' undefined) === 'q'
   , testProperty "scanl'" $
     \(curry . applyFun -> f :: Word -> Int -> Word) s (Blind xs) ->
-      trim1 (I.scanl' f s xs) == L.scanl' f s (trim xs)
+      trim1 (I.scanl' f s xs) === L.scanl' f s (trim xs)
   , testProperty "scanl' laziness" $
-    I.head (I.scanl' undefined 'q' undefined) == 'q'
+    I.head (I.scanl' undefined 'q' undefined) === 'q'
   , testProperty "scanl1" $
     \(curry . applyFun -> f :: Int -> Int -> Int) (Blind xs) ->
-      trim (I.scanl1 f xs) == L.scanl1 f (trim xs)
+      trim (I.scanl1 f xs) === L.scanl1 f (trim xs)
   , testProperty "scanl1 laziness" $
-    I.head (I.scanl1 undefined ('q' :< undefined)) == 'q'
+    I.head (I.scanl1 undefined ('q' :< undefined)) === 'q'
 
   , testProperty "mapAccumL" $
     \(curry . applyFun -> f :: Bool -> Int -> (Bool, Word)) (Blind xs) ->
-      trim (I.mapAccumL f False xs) == snd (L.mapAccumL f False (trim xs))
+      trim (I.mapAccumL f False xs) === snd (L.mapAccumL f False (trim xs))
   , testProperty "mapAccumL laziness" $
-    I.head (I.mapAccumL (\_ x -> (undefined, x)) undefined ('q' :< undefined)) == 'q'
+    I.head (I.mapAccumL (\_ x -> (undefined, x)) undefined ('q' :< undefined)) === 'q'
 
   , testProperty "iterate" $
     \(applyFun -> f :: Int -> Int) s ->
-      trim (I.iterate f s) == L.take 10 (L.iterate f s)
+      trim (I.iterate f s) === L.take 10 (L.iterate f s)
   , testProperty "iterate laziness" $
-      I.head (I.iterate undefined 'q') == 'q'
+      I.head (I.iterate undefined 'q') === 'q'
   , testProperty "iterate'" $
     \(applyFun -> f :: Int -> Int) s ->
-      trim (I.iterate' f s) == L.take 10 (L.iterate f s)
+      trim (I.iterate' f s) === L.take 10 (L.iterate f s)
   , testProperty "iterate' laziness" $
-      I.head (I.iterate' undefined 'q') == 'q'
+      I.head (I.iterate' undefined 'q') === 'q'
 
   , testProperty "repeat" $
     \(s :: Int) ->
-      trim (I.repeat s) == L.replicate 10 s
+      trim (I.repeat s) === L.replicate 10 s
 
   , testProperty "cycle" $
     \(xs :: NonEmpty Int) ->
-      trim (I.cycle xs) == L.take 10 (L.cycle (NE.toList xs))
+      trim (I.cycle xs) === L.take 10 (L.cycle (NE.toList xs))
   , testProperty "cycle laziness" $
-    I.head (I.cycle ('q' :| undefined)) == 'q'
+    I.head (I.cycle ('q' :| undefined)) === 'q'
 
   , testProperty "unfoldr" $
     \(applyFun -> f :: Word -> (Int, Word)) s ->
-      trim (I.unfoldr f s) == L.take 10 (L.unfoldr (Just . f) s)
+      trim (I.unfoldr f s) === L.take 10 (L.unfoldr (Just . f) s)
   , testProperty "unfoldr laziness" $
-    I.head (I.unfoldr (, undefined) 'q') == 'q'
+    I.head (I.unfoldr (, undefined) 'q') === 'q'
 
   , testProperty "take" $
     \n (Blind (xs :: Infinite Int)) ->
-      L.take 10 (I.take n xs) == L.take n (trim xs)
+      L.take 10 (I.take n xs) === L.take n (trim xs)
   , testProperty "take laziness 1" $
-    I.take 0 undefined == ""
+    I.take 0 undefined === ""
   , testProperty "take laziness 2" $
-    I.take 1 ('q' :< undefined) == "q"
+    I.take 1 ('q' :< undefined) === "q"
   , testProperty "drop" $
     \n (Blind (xs :: Infinite Int)) ->
-      trim (I.drop n xs) == L.drop n (I.take (max n 0 + 10) xs)
+      trim (I.drop n xs) === L.drop n (I.take (max n 0 + 10) xs)
+  , testProperty "drop laziness" $
+    I.head (I.drop 0 ('q' :< undefined)) === 'q'
   , testProperty "splitAt" $
     \n (Blind (xs :: Infinite Int)) ->
-      bimap (L.take 10) trim (I.splitAt n xs) ==
+      bimap (L.take 10) trim (I.splitAt n xs) ===
         first (L.take 10) (L.splitAt n (I.take (max n 0 + 10) xs))
   , testProperty "splitAt laziness 1" $
-    fst (I.splitAt 0 undefined) == ""
+    fst (I.splitAt 0 undefined) === ""
   , testProperty "splitAt laziness 2" $
-    fst (I.splitAt 1 ('q' :< undefined)) == "q"
+    fst (I.splitAt 1 ('q' :< undefined)) === "q"
 
   , testProperty "takeWhile" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
-      let ys = L.take 10 (I.takeWhile f xs) in
-        L.take 10 (L.takeWhile f (I.take (length ys + 10) xs)) ==
-          L.take 10 (I.takeWhile f xs)
+      L.take 10 (L.takeWhile f (I.foldr (:) xs)) ===
+        L.take 10 (I.takeWhile f xs)
   , testProperty "takeWhile laziness 1" $
       L.null (I.takeWhile (const False) ('q' :< undefined))
   , testProperty "takeWhile laziness 2" $
-      L.head (I.takeWhile (const True) ('q' :< undefined)) == 'q'
+      L.head (I.takeWhile (const True) ('q' :< undefined)) === 'q'
   , testProperty "fst . span" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
       let ys = L.take 10 (fst (I.span f xs)) in
-        L.take 10 (L.takeWhile f (I.take (length ys + 10) xs)) ==
+        L.take 10 (L.takeWhile f (I.take (length ys + 10) xs)) ===
           L.take 10 (fst (I.span f xs))
   , testProperty "fst . break" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
       let ys = L.take 10 (fst (I.break f xs)) in
-        L.take 10 (L.takeWhile (not . f) (I.take (length ys + 10) xs)) ==
+        L.take 10 (L.takeWhile (not . f) (I.take (length ys + 10) xs)) ===
           L.take 10 (fst (I.break f xs))
   , testProperty "dropWhile" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
-      trim (L.foldr (:<) (I.dropWhile f xs) (I.takeWhile f xs)) == trim xs
+      trim (L.foldr (:<) (I.dropWhile f xs) (I.takeWhile f xs)) === trim xs
   , testProperty "snd . span" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
-      trim (L.foldr (:<) (snd (I.span f xs)) (I.takeWhile f xs)) == trim xs
+      trim (L.foldr (:<) (snd (I.span f xs)) (I.takeWhile f xs)) === trim xs
   , testProperty "snd . break" $
     \(applyFun -> f :: Ordering -> Bool) (Blind xs) ->
-      trim (L.foldr (:<) (snd (I.break f xs)) (I.takeWhile (not . f) xs)) == trim xs
+      trim (L.foldr (:<) (snd (I.break f xs)) (I.takeWhile (not . f) xs)) === trim xs
   , testProperty "span laziness" $
-    L.head (fst (I.span (/= '\n') ('q' :< undefined))) == 'q'
+    L.head (fst (I.span (/= '\n') ('q' :< undefined))) === 'q'
   , testProperty "break laziness" $
-    L.head (fst (I.break (== '\n') ('q' :< undefined))) == 'q'
+    L.head (fst (I.break (== '\n') ('q' :< undefined))) === 'q'
 
   , testProperty "stripPrefix" $
     \(xs :: [Int]) (Blind (ys :: Infinite Int)) ->
-      fmap trim (I.stripPrefix xs ys) == fmap (L.take 10) (L.stripPrefix xs (I.take (length xs + 10) ys))
+      fmap trim (I.stripPrefix xs ys) === fmap (L.take 10) (L.stripPrefix xs (I.take (length xs + 10) ys))
   , testProperty "stripPrefix laziness 1" $
     isNothing (I.stripPrefix ('q' : undefined) ('w' :< undefined))
   , testProperty "stripPrefix laziness 2" $
     isJust (I.stripPrefix "foo" ('f' :< 'o' :< 'o' :< undefined))
   , testProperty "isPrefixOf" $
     \(xs :: [Int]) (Blind (ys :: Infinite Int)) ->
-      I.isPrefixOf xs ys == L.isPrefixOf xs (I.take (length xs + 10) ys)
+      I.isPrefixOf xs ys === L.isPrefixOf xs (I.take (length xs + 10) ys)
   , testProperty "isPrefixOf laziness 1" $
-    not (I.isPrefixOf ('q' : undefined) ('w' :< undefined))
+    I.isPrefixOf "" undefined
   , testProperty "isPrefixOf laziness 2" $
+    not (I.isPrefixOf ('q' : undefined) ('w' :< undefined))
+  , testProperty "isPrefixOf laziness 3" $
     I.isPrefixOf "foo" ('f' :< 'o' :< 'o' :< undefined)
 
   , testProperty "zip" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) ->
-      trim (I.zip xs1 xs2) == L.zip (trim xs1) (trim xs2)
+      trim (I.zip xs1 xs2) === L.zip (trim xs1) (trim xs2)
   , testProperty "zip3" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) (Blind (xs3 :: Infinite Bool)) ->
-      trim (I.zip3 xs1 xs2 xs3) == L.zip3 (trim xs1) (trim xs2) (trim xs3)
+      trim (I.zip3 xs1 xs2 xs3) === L.zip3 (trim xs1) (trim xs2) (trim xs3)
   , testProperty "zip4" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) (Blind (xs3 :: Infinite Bool)) (Blind (xs4 :: Infinite Char)) ->
-      trim (I.zip4 xs1 xs2 xs3 xs4) == L.zip4 (trim xs1) (trim xs2) (trim xs3) (trim xs4)
+      trim (I.zip4 xs1 xs2 xs3 xs4) === L.zip4 (trim xs1) (trim xs2) (trim xs3) (trim xs4)
   , testProperty "zip5" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) (Blind (xs3 :: Infinite Bool)) (Blind (xs4 :: Infinite Char)) (Blind (xs5 :: Infinite Ordering)) ->
-      trim (I.zip5 xs1 xs2 xs3 xs4 xs5) == L.zip5 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5)
+      trim (I.zip5 xs1 xs2 xs3 xs4 xs5) === L.zip5 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5)
   , testProperty "zip6" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) (Blind (xs3 :: Infinite Bool)) (Blind (xs4 :: Infinite Char)) (Blind (xs5 :: Infinite Ordering)) (Blind (xs6 :: Infinite String)) ->
-      trim (I.zip6 xs1 xs2 xs3 xs4 xs5 xs6) == L.zip6 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5) (trim xs6)
+      trim (I.zip6 xs1 xs2 xs3 xs4 xs5 xs6) === L.zip6 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5) (trim xs6)
   , testProperty "zip7" $
     \(Blind (xs1 :: Infinite Int)) (Blind (xs2 :: Infinite Word)) (Blind (xs3 :: Infinite Bool)) (Blind (xs4 :: Infinite Char)) (Blind (xs5 :: Infinite Ordering)) (Blind (xs6 :: Infinite String)) (Blind (xs7 :: Infinite Integer)) ->
-      trim (I.zip7 xs1 xs2 xs3 xs4 xs5 xs6 xs7) == L.zip7 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5) (trim xs6) (trim xs7)
+      trim (I.zip7 xs1 xs2 xs3 xs4 xs5 xs6 xs7) === L.zip7 (trim xs1) (trim xs2) (trim xs3) (trim xs4) (trim xs5) (trim xs6) (trim xs7)
 
   , testProperty "unzip" $
     \(Blind (xs :: Infinite (Int, Word))) ->
-      bimap trim trim (I.unzip xs) == L.unzip (trim xs)
+      bimap trim trim (I.unzip xs) === L.unzip (trim xs)
   , testProperty "unzip3" $
     \(Blind (xs :: Infinite (Int, Word, Bool))) ->
-      (\(xs1, xs2, xs3) -> (trim xs1, trim xs2, trim xs3)) (I.unzip3 xs) == L.unzip3 (trim xs)
+      (\(xs1, xs2, xs3) -> (trim xs1, trim xs2, trim xs3)) (I.unzip3 xs) === L.unzip3 (trim xs)
   , testProperty "unzip4" $
     \(Blind (xs :: Infinite (Int, Word, Bool, Char))) ->
-      (\(xs1, xs2, xs3, xs4) -> (trim xs1, trim xs2, trim xs3, trim xs4)) (I.unzip4 xs) == L.unzip4 (trim xs)
+      (\(xs1, xs2, xs3, xs4) -> (trim xs1, trim xs2, trim xs3, trim xs4)) (I.unzip4 xs) === L.unzip4 (trim xs)
   , testProperty "unzip5" $
     \(Blind (xs :: Infinite (Int, Word, Bool, Char, Ordering))) ->
-      (\(xs1, xs2, xs3, xs4, xs5) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5)) (I.unzip5 xs) == L.unzip5 (trim xs)
+      (\(xs1, xs2, xs3, xs4, xs5) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5)) (I.unzip5 xs) === L.unzip5 (trim xs)
   , testProperty "unzip6" $
     \(Blind (xs :: Infinite (Int, Word, Bool, Char, Ordering, String))) ->
-      (\(xs1, xs2, xs3, xs4, xs5, xs6) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5, trim xs6)) (I.unzip6 xs) == L.unzip6 (trim xs)
+      (\(xs1, xs2, xs3, xs4, xs5, xs6) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5, trim xs6)) (I.unzip6 xs) === L.unzip6 (trim xs)
   , testProperty "unzip7" $
     \(Blind (xs :: Infinite (Int, Word, Bool, Char, Ordering, String, Integer))) ->
-      (\(xs1, xs2, xs3, xs4, xs5, xs6, xs7) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5, trim xs6, trim xs7)) (I.unzip7 xs) == L.unzip7 (trim xs)
+      (\(xs1, xs2, xs3, xs4, xs5, xs6, xs7) -> (trim xs1, trim xs2, trim xs3, trim xs4, trim xs5, trim xs6, trim xs7)) (I.unzip7 xs) === L.unzip7 (trim xs)
 
   , testProperty "lines" $
     \(Blind (xs :: Infinite Char)) ->
-      I.take 3 (I.lines xs) == L.take 3 (L.lines (I.foldr (:) xs))
+      I.take 3 (I.lines xs) === L.take 3 (L.lines (I.foldr (:) xs))
   , testProperty "lines laziness 1" $
-    L.head (I.head (I.lines ('q' :< undefined))) == 'q'
+    L.head (I.head (I.lines ('q' :< undefined))) === 'q'
   , testProperty "lines laziness 2" $
     L.null (I.head (I.lines ('\n' :< undefined)))
   , testProperty "words" $
     \(Blind (xs :: Infinite Char)) ->
-      I.take 3 (I.map NE.toList (I.words xs)) == L.take 3 (L.words (I.foldr (:) xs))
+      I.take 3 (I.map NE.toList (I.words xs)) === L.take 3 (L.words (I.foldr (:) xs))
   , testProperty "words laziness" $
-    NE.head (I.head (I.words ('q' :< undefined))) == 'q'
+    NE.head (I.head (I.words ('q' :< undefined))) === 'q'
   , testProperty "unlines" $
     \(Blind (xs :: Infinite [Char])) ->
-      trim (I.unlines xs) == L.take 10 (L.unlines (trim xs))
+      trim (I.unlines xs) === L.take 10 (L.unlines (trim xs))
   , testProperty "unlines laziness" $
-    I.take 2 (I.unlines ("q" :< undefined)) == "q\n"
+    I.take 2 (I.unlines ("q" :< undefined)) === "q\n"
   , testProperty "unwords" $
     \(Blind (xs :: Infinite (NonEmpty Char))) ->
-      trim (I.unwords xs) == L.take 10 (L.unwords (L.map NE.toList (trim xs)))
+      trim (I.unwords xs) === L.take 10 (L.unwords (L.map NE.toList (I.foldr (:) xs)))
   , testProperty "unwords laziness" $
-    I.take 2 (I.unwords (('q' :| []) :< undefined)) == "q "
+    I.take 2 (I.unwords (('q' :| []) :< undefined)) === "q "
+  , testProperty "unlines . lines" $
+    \(Blind (xs :: Infinite Char)) ->
+      I.take 100 xs === I.take 100 (I.unlines (I.lines xs))
 
   , testProperty "group" $
     \(Blind (ys :: Infinite Ordering)) ->
-      trim (I.group ys) == L.take 10 (NE.group (I.foldr (:) ys))
+      trim (I.group ys) === L.take 10 (NE.group (I.foldr (:) ys))
+  , testProperty "groupBy" $
+    \(curry . applyFun -> f :: Ordering -> Ordering -> Bool) (Blind ys) ->
+      all (\x -> not $ all (f x) [minBound..maxBound]) [minBound..maxBound] ==>
+        trim (I.groupBy f ys) === L.take 10 (NE.groupBy f (I.foldr (:) ys))
   , testProperty "group laziness" $
-    NE.head (I.head (I.group ('q' :< undefined))) == 'q'
+    NE.head (I.head (I.group ('q' :< undefined))) === 'q'
   , testProperty "nub" $
     \(Blind (ys :: Infinite (Large Int))) ->
-      I.take 3 (I.nub ys) == L.take 3 (L.nub (I.foldr (:) ys))
+      fmap getLarge (I.take 3 (I.nub ys)) === fmap getLarge (L.take 3 (L.nub (I.foldr (:) ys)))
   , testProperty "nub laziness" $
-    I.head (I.nub ('q' :< undefined)) == 'q'
+    I.head (I.nub ('q' :< undefined)) === 'q'
 
   , testProperty "delete" $
     \(x :: Ordering) (Blind xs) ->
-      trim (I.delete x xs) == L.take 10 (L.delete x (I.foldr (:) xs))
+      trim (I.delete x xs) === L.take 10 (L.delete x (I.foldr (:) xs))
   , testProperty "delete laziness" $
-    I.head (I.delete 'q' ('w' :< undefined)) == 'w'
+    I.head (I.delete 'q' ('w' :< undefined)) === 'w'
   , testProperty "insert" $
     \(x :: Int) (Blind xs) ->
-      trim (I.insert x xs) == L.take 10 (L.insert x (I.foldr (:) xs))
+      trim (I.insert x xs) === L.take 10 (L.insert x (I.foldr (:) xs))
   , testProperty "insert laziness" $
-    I.take 2 (I.insert 'q' ('w' :< undefined)) == "qw"
+    I.take 2 (I.insert 'q' ('w' :< undefined)) === "qw"
 
   , testProperty "\\\\" $
     \(Blind (xs :: Infinite Ordering)) ys ->
-      trim (xs I.\\ ys) == L.take 10 (I.foldr (:) xs L.\\ ys)
+      trim (xs I.\\ ys) === L.take 10 (I.foldr (:) xs L.\\ ys)
   , testProperty "\\\\ laziness" $
-    I.head (('q' :< undefined) I.\\ []) == 'q'
+    I.head (('q' :< undefined) I.\\ []) === 'q'
   , testProperty "union" $
     \xs (Blind (ys :: Infinite Ordering)) ->
-      I.take 3 (I.union xs ys) == L.take 3 (xs `L.union` I.foldr (:) ys)
+      I.take 3 (I.union xs ys) === L.take 3 (xs `L.union` I.foldr (:) ys)
   , testProperty "union laziness" $
-    I.head (I.union ('q' : undefined) undefined) == 'q'
+    I.head (I.union ('q' : undefined) undefined) === 'q'
   , testProperty "intersect" $
     \(Blind (xs :: Infinite Ordering)) ys -> not (null ys) ==>
-      I.head (I.intersect xs ys) == L.head (I.foldr (:) xs `L.intersect` ys)
+      I.head (I.intersect xs ys) === L.head (I.foldr (:) xs `L.intersect` ys)
   , testProperty "intersect laziness" $
-    I.head (I.intersect ('q' :< undefined) ('q' : undefined)) == 'q'
+    I.head (I.intersect ('q' :< undefined) ('q' : undefined)) === 'q'
 
   , testProperty "inits" $
     \(Blind (xs :: Infinite Int)) ->
-      I.take 21 (I.inits xs) == L.inits (I.take 20 xs)
+      I.take 21 (I.inits xs) === L.inits (I.take 20 xs)
   , testProperty "inits laziness 1" $
     L.null (I.head (I.inits undefined))
   , testProperty "inits laziness 2" $
-    I.take 2 (I.inits ('q' :< undefined)) == ["", "q"]
+    I.take 2 (I.inits ('q' :< undefined)) === ["", "q"]
   , testProperty "inits1" $
     \(Blind (xs :: Infinite Int)) ->
-      map NE.toList (trim (I.inits1 xs)) == L.tail (L.inits (trim xs))
+      map NE.toList (trim (I.inits1 xs)) === L.tail (L.inits (trim xs))
   , testProperty "tails" $
     \(Blind (xs :: Infinite Int)) ->
       map trim (trim (I.tails xs)) === map (L.take 10) (L.take 10 (L.tails (I.take 20 xs)))
   , testProperty "tails laziness" $
-    I.head (I.head (I.tails ('q' :< undefined))) == 'q'
+    I.head (I.head (I.tails ('q' :< undefined))) === 'q'
 
   , testProperty "lookup" $
     \(xs :: [(Int, Word)]) y zs ->
       let pairs = NE.fromList (xs ++ (y : zs)) in
-        Just (I.lookup (fst y) (I.cycle pairs)) == L.lookup (fst y) (NE.toList pairs)
+        Just (I.lookup (fst y) (I.cycle pairs)) === L.lookup (fst y) (NE.toList pairs)
   , testProperty "lookup laziness" $
-    I.lookup True ((True, 'q') :< undefined) == 'q'
+    I.lookup True ((True, 'q') :< undefined) === 'q'
   , testProperty "find" $
     \(xs :: [(Int, Word)]) y zs ->
       let pairs = NE.fromList (xs ++ (y : zs)) in
-        Just (I.find ((== snd y) . snd) (I.cycle pairs)) == L.find ((== snd y) . snd) (NE.toList pairs)
+        Just (I.find ((== snd y) . snd) (I.cycle pairs)) === L.find ((== snd y) . snd) (NE.toList pairs)
   , testProperty "find laziness" $
-    I.find odd (1 :< undefined) == (1 :: Int)
+    I.find odd (1 :< undefined) === (1 :: Int)
 
   , testProperty "filter" $
     \(applyFun -> f :: Int -> Bool) xs (Blind ys) ->
       let us = L.filter f xs in
-        us == I.take (length us) (I.filter f (I.prependList xs ys))
+        us === I.take (length us) (I.filter f (I.prependList xs ys))
+  , testProperty "mapMaybe" $
+    \(applyFun -> f :: Int -> Maybe Word) xs (Blind ys) ->
+      let us = mapMaybe f xs in
+        us === I.take (length us) (I.mapMaybe f (I.prependList xs ys))
+  , testProperty "catMaybes" $
+    \(xs :: [Maybe Word]) (Blind ys) ->
+      let us = catMaybes xs in
+        us === I.take (length us) (I.catMaybes (I.prependList xs ys))
   , testProperty "partition" $
     \(applyFun -> f :: Int -> Bool) xs (Blind ys) ->
       let (us, vs) = L.partition f xs in
         let (us', vs') = I.partition f (I.prependList xs ys) in
-          us == I.take (length us) us' && vs == I.take (length vs) vs'
+          us === I.take (length us) us' .&&. vs === I.take (length vs) vs'
+  , testProperty "mapEither" $
+    \(applyFun -> f :: Int -> Either Word Char) xs (Blind ys) ->
+      let (us, vs) = mapEither f xs in
+        let (us', vs') = I.mapEither f (I.prependList xs ys) in
+          us === I.take (length us) us' .&&. vs === I.take (length vs) vs'
+  , testProperty "partitionEithers" $
+    \(xs :: [Either Word Char]) (Blind ys) ->
+      let (us, vs) = partitionEithers xs in
+        let (us', vs') = I.partitionEithers (I.prependList xs ys) in
+          us === I.take (length us) us' .&&. vs === I.take (length vs) vs'
 
   , testProperty "!!" $
     \(Blind (xs :: Infinite Int)) n ->
-      xs I.!! n == I.foldr (:) xs L.!! fromIntegral n
+      xs I.!! n === I.foldr (:) xs L.!! fromIntegral n
   , testProperty "tabulate" $
     \(applyFun -> f :: Word -> Char) n ->
-      I.tabulate f I.!! n == f n
+      I.tabulate f I.!! n === f n
 
   , testProperty "elemIndex" $
     \xs (x :: Int) (Blind ys) ->
       let zs = I.prependList xs (x :< ys) in
-        Just (fromIntegral (I.elemIndex x zs)) == L.elemIndex x (I.foldr (:) zs)
+        Just (fromIntegral (I.elemIndex x zs)) === L.elemIndex x (I.foldr (:) zs)
   , testProperty "elemIndices" $
     \xs (x :: Ordering) (Blind ys) ->
       let zs = I.prependList xs (x :< ys) in
         let is = L.elemIndices x (xs ++ [x]) in
-          map fromIntegral (I.take (length is) (I.elemIndices x zs)) == is
+          map fromIntegral (I.take (length is) (I.elemIndices x zs)) === is
+
+  , testProperty ">>= 32bit" $
+    let ix = maxBound :: Word32 in
+      finiteBitSize (0 :: Word) /= 32 ||
+        I.head (I.tail (I.genericDrop ix (I.repeat () >>= const (False :< I.repeat True))))
   ]
