diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,14 @@
+# Revision history for reverse-list
+
+## 0.2.0 -- 2022-01-27
+
+* Remove `Foldable` instance until I determine the correct semantics.
+* Add `init`, `last`.
+* Add `Data.List.Cons` module and `Tsil` synonym for "symmetry".
+* Rename `Data.List.RList` to `Data.List.Snoc`
+* `RList.{safeLast,safeInit}`
+* `RList.toSet`
+
+## 0.1.0 -- 2021-12-27
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Okuno Zankoku (c) 2021
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Okuno Zankoku nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# reverse-list
+
+The key idea of this library is to leverage the type system to control the performance characteristics of list-manipulation code.
+It defines the type `RList`, which is a snoc-list rather than a cons-list.
+It also creates a symmetric module for cons-lists, which focuses on the efficient and safe use of linked lists.
+
+Admittedly, parsing `String`s as in this example is bad for performance anyway, but the potential bugs are the same for any use of lists as accumulators:
+```
+import qualified Data.List.Snoc as RList
+
+parseSqlString :: String -> Maybe String
+parseSqlString str0 = case str0 of
+  '\'':rest -> loop "" rest
+  _ -> Nothing
+  where
+  loop :: RList Char -> [Char] -> Maybe [Char]
+  loop acc [] = Nothing
+  -- it is impossible to accidentally return the accumulator without reversing
+  loop acc "\'" = Just $ Rlist.toList acc
+  loop acc ('\'':'\'':rest) = loop (Snoc acc '\'') rest
+  loop acc (c:rest) = loop (Snoc acc c) rest
+```
+
+Currently, we only support the basic introduction/elimination forms (though reasonably ergonomically), and conversions.
+Additional functions should certainly be exposed, after due consideration is given to their semantics, including performance.
+If you run into anything you think deserved to be exported, open an issue or a pull request and I'll be happy to get it done.
diff --git a/reverse-list.cabal b/reverse-list.cabal
new file mode 100644
--- /dev/null
+++ b/reverse-list.cabal
@@ -0,0 +1,31 @@
+cabal-version: 3.0
+name: reverse-list
+version: 0.2.0
+synopsis: reversed lists/snoc lists
+description: The key idea of this library is to leverage the type system to control the performance characteristics of list-manipulation code.
+  It defines the type `RList`, which is a snoc-list rather than a cons-list.
+  It also creates a symmetric module for cons-lists, which focuses on the efficient and safe use of linked lists.
+  See README.md for more information.
+category: Data
+homepage: https://github.com/edemko/reverse-list
+bug-reports: https://github.com/edemko/reverse-list/issues
+author: Eric Demko
+maintainer: zankoku.okuno@gmail.com
+copyright: 2021 Eric Demko
+license: BSD-3-Clause
+license-file: LICENSE
+extra-source-files: README.md, CHANGELOG.md
+
+library
+  hs-source-dirs: src
+  exposed-modules:
+    Data.List.Cons
+    Data.List.Snoc
+  -- other-modules:
+  build-depends:
+    , base >=4.14.3 && <4.16
+    , containers >=0.6 && <0.7
+    , contiguous >= 0.6 && <0.7
+    , deepseq >=1.4 && <1.5
+  default-language: Haskell2010
+  ghc-options: -O2 -Wall -Wunticked-promoted-constructors
diff --git a/src/Data/List/Cons.hs b/src/Data/List/Cons.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/List/Cons.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- | This module exists primarily for symmetry with "Data.List.Snoc"
+-- However, it can also be used in place of the "Prelude" list type:
+--
+-- This module only exports functions that are efficient on linked lists. Many
+-- functions on that type ('Prelude.last' 'Data.List.isSuffixOf') though
+-- technically implementable, do not represent the intended use of a linked list
+-- in terms of performance.
+--
+-- Additionally, this module does not export any partial functions: 'head' and
+-- 'tail' return their results under a 'Maybe'.
+module Data.List.Cons
+  ( List
+  , pattern Nil
+  , pattern Cons
+  , nil
+  , cons
+  , uncons
+  , singleton
+  , head
+  , tail
+  ) where
+
+import Prelude hiding (head,tail)
+
+-- | As a counterpart to 'Data.List.Snoc.RList'/'Data.List.Snoc.Tsil'.
+type List = ([])
+
+{-# COMPLETE Nil, Cons #-}
+
+-- | An empty 'List', such as 'nil'.
+pattern Nil :: List a
+pattern Nil = []
+
+-- | The 'List' consisting of head and tail elements, such as created by 'cons'.
+pattern Cons :: a -> List a -> List a
+pattern Cons x xs <- (uncons -> Just (x, xs))
+  where Cons = cons
+
+-- | The empty 'List'.
+nil :: List a
+{-# INLINABLE nil #-}
+nil = []
+
+-- | @O(1)@ Append an element.
+--
+-- If you are looking for @snoc@, you should use an 'RList', or a finite sequence/queue type.
+cons :: a -> List a -> List a
+{-# INLINABLE cons #-}
+cons = (:)
+
+-- | @O(1)@ Access the first element and trailing portion of the list.
+-- See also 'head' and 'tail' if you only need one component.
+--
+-- If you are looking for @unsnoc@, you should use an 'RList', or a finite sequence/queue type.
+uncons :: List a -> Maybe (a, List a)
+{-# INLINABLE uncons #-}
+uncons [] = Nothing
+uncons (x:xs) = Just (x, xs)
+
+-- | @O(1)@ extract the first element of a list, if it exists.
+-- See also 'uncons' if you also need 'tail' at the same time.
+head :: List a -> Maybe a
+head Nil = Nothing
+head (Cons x _) = Just x
+
+-- | @O(1)@ extract the elements of a list other than the last, if they exist.
+-- See also 'uncons' if you also need 'head' at the same time.
+tail :: List a -> Maybe (List a)
+tail Nil = Nothing
+tail (Cons _ xs) = Just xs
+
+-- | Create a single-element 'List'.
+singleton :: a -> List a
+{-# INLINE singleton #-}
+singleton = (:[])
diff --git a/src/Data/List/Snoc.hs b/src/Data/List/Snoc.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/List/Snoc.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE ViewPatterns #-}
+
+
+-- | Snoc-lists: lists where prepending is linear-time, but _appending_ is constant-time.
+-- Useful for describing zippers and functional queues/buffers more naturally and safely.
+--
+-- We call it an `RList` because this is really just a vanilla list, but where
+-- the semantics are that the last-added thing (internally cons'ed) is
+-- understood to be at the \"end\" of the list.
+--
+-- WARNING: the `Foldable` instance provides a `Foldable.toList`; this simply unwraps the `RList` rather than reversing it.
+-- If you need to convert from revered semantics to forward semantics, use this module's `toList`.
+module Data.List.Snoc
+  ( RList
+  , Tsil
+  -- * Introduction and Elimination
+  , nil
+  , snoc
+  , singleton
+  , unsnoc
+  -- ** Patterns
+  , pattern Nil
+  , pattern Snoc
+  -- * Queries
+  , null
+  , init
+  , last
+  -- * Traversal
+  , catMaybes
+  -- * Conversion
+  , toList
+  , fromList
+  , reverseIn
+  , reverseOut
+  , toArrayN
+  , toSet
+  ) where
+
+import Prelude hiding (null,init,last,reverse)
+
+import Control.Applicative(Alternative(..))
+import Control.DeepSeq (NFData)
+import Data.Bifunctor (first)
+import Data.Primitive.Contiguous (Contiguous, Element, SmallArray)
+import Data.Set (Set)
+import GHC.Generics (Generic)
+
+import qualified Data.List as List
+import qualified Data.Maybe as Maybe
+import qualified Data.Primitive.Contiguous as Arr
+import qualified Data.Set as Set
+import qualified Prelude
+
+-- | This datatype defines snoc-lists: lists with O(1) append and O(n) prepend.
+-- Underneath the hood, it is just a plain list, but understood as containing its elements in reverse order.
+newtype RList a = RList { unRList :: [a] }
+  deriving stock (Generic)
+  deriving newtype (Functor,Applicative)
+instance (NFData a) => NFData (RList a)
+
+instance (Show a) => Show (RList a) where
+  show = show . toList
+instance (Read a) => Read (RList a) where
+  readsPrec i = (fmap . first) fromList . readsPrec i
+
+instance Semigroup (RList a) where
+  (RList a) <> (RList b) = RList (b <> a)
+instance Monoid (RList a) where
+  mempty = Nil
+
+instance Alternative RList where
+  empty = mempty
+  (RList a) <|> (RList b) = RList (b <|> a)
+
+-- | See? It's \"List\" in reverse?
+-- I dunno, I just think 'RList' is an inelegant name, and word-initial @/t͜s/@ is one of my favorite phonemes.
+type Tsil = RList
+
+{-# COMPLETE Nil, Snoc #-}
+
+-- | An empty 'RList', such as 'nil'.
+pattern Nil :: RList a
+pattern Nil = RList []
+
+-- | The 'RList' consisting of initial and last elements, such as created by 'snoc'.
+pattern Snoc :: RList a -> a -> RList a
+pattern Snoc xs x <- (unsnoc -> Just (xs, x))
+  where Snoc = snoc
+
+-- | The empty 'RList'.
+nil :: RList a
+{-# INLINABLE nil #-}
+nil = RList []
+
+-- | @O(1)@ Append an element.
+--
+-- If you are looking for @cons@, you should use a plain list, or a finite sequence/queue type.
+snoc :: RList a -> a -> RList a
+{-# INLINABLE snoc #-}
+snoc (RList xs) x = RList (x:xs)
+
+-- | @O(1)@ Access the last element and initial portion of the list.
+-- See also 'last' and 'init' if you only need one component.
+--
+-- If you are looking for @uncons@, you should use a plain list, or a finite sequence/queue type.
+unsnoc :: RList a -> Maybe (RList a, a)
+{-# INLINABLE unsnoc #-}
+unsnoc (RList []) = Nothing
+unsnoc (RList (x:xs)) = Just (RList xs, x)
+
+-- | Create a single-element 'RList'.
+singleton :: a -> RList a
+{-# INLINE singleton #-}
+singleton = RList . (:[])
+
+-- | Test if an 'RList' is empty.
+null :: RList a -> Bool
+{-# INLINE null #-}
+null (RList xs) = List.null xs
+
+-- | @O(1)@ extract the last element of a list, if it exists.
+-- See also 'unsnoc' if you also need 'init' at the same time.
+last :: RList a -> Maybe a
+last Nil = Nothing
+last (Snoc _ x) = Just x
+
+-- | @O(1)@ extract the elements of a list other than the last, if they exist.
+-- See also 'unsnoc' if you also need 'last' at the same time.
+init :: RList a -> Maybe (RList a)
+init Nil = Nothing
+init (Snoc xs _) = Just xs
+
+-- | Remove all 'Nothing's from an 'RList' of 'Maybe's.
+catMaybes :: RList (Maybe a) -> RList a
+{-# INLINE catMaybes #-}
+catMaybes = RList . Maybe.catMaybes . unRList
+
+-- | @O(n)@ Convert to a plain list, maintaining order.
+--
+-- This is here so that you can escape back out to normal cons-list land once
+-- you're done building your list.
+--
+-- See 'reverseOut' for when order doesn't matter.
+toList :: RList a -> [a]
+{-# INLINE toList #-}
+toList (RList xs) = Prelude.reverse xs
+
+-- | @O(n)@ Convert from a plain list, maintaining order.
+--
+-- This is added for completion's sake, as I'm not sure you'll often need this adapter.
+--
+-- See `toList` for the inverse, or `reverseIn` for when order doesn't matter.
+fromList :: [a] -> RList a
+{-# INLINE fromList #-}
+fromList = RList . Prelude.reverse
+
+-- | @O(0)@ Reverse an `RList`, returning a plain cons list.
+--
+-- This is here so that when the output list is fed to an order-agnostic
+-- function, you don't have to pay the cost of reversing the representation.
+--
+-- See 'toList' for when order matters.
+reverseOut :: RList a -> [a]
+{-# INLINE reverseOut #-}
+reverseOut = unRList
+
+
+-- | @O(0)@ Reverse a plain cons list, rerutning an `RList`.
+--
+-- See `reverseOut` for the inverse, and why you might use these.
+reverseIn :: [a] -> RList a
+{-# INLINE reverseIn #-}
+reverseIn = RList
+
+-- | Write the contents of the `RList` into an array, assuming you know the length of the array.
+-- This is useful in the common case of buffering an unknown-length stream before allocating contiguous space for the elements.
+--
+-- If you sepcify to small a langth, the initial part of the array will be uninitialized.
+-- If you specify to large a length, the initial part of the list will not be written.
+--
+-- If you are unaware of the size of the list, `Arr.fromList . fromList` will do the trick, but will obviously be slower.
+toArrayN :: (Contiguous arr, Element arr a) => Int -> RList a -> arr a
+{-# INLINE toArrayN #-} -- use inline instead of inlinable, because inlinable with Contiguous is busted
+{-# SPECIALIZE toArrayN :: Int -> RList a -> SmallArray a #-}
+toArrayN n (RList xs0) = Arr.create $ do
+  mut <- Arr.new n
+  loop mut (n - 1) xs0
+  pure mut
+  where
+  loop _ (-1) _ = pure ()
+  loop _ _ [] = pure ()
+  loop arr i (x:xs) = do
+    Arr.write arr i x
+    loop arr (i - 1) xs
+
+-- | Convert to a set without an intermediate conversion to a cons-list.
+toSet :: (Ord a) => RList a -> Set a
+{-# INLINABLE toSet #-}
+toSet (RList xs) = Set.fromList xs
