diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,25 @@
+listlike
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All code is under the following license unless otherwise noted:
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+The GNU General Public License is available in the file COPYING in the source
+distribution.  Debian GNU/Linux users may find this in
+/usr/share/common-licenses/LGPL-2.1.
+
+If the LGPL is unacceptable for your uses, please e-mail me; alternative
+terms can be negotiated for your project.
+
diff --git a/ListLike.cabal b/ListLike.cabal
new file mode 100644
--- /dev/null
+++ b/ListLike.cabal
@@ -0,0 +1,43 @@
+Name: ListLike
+Version: 1.0.0
+License: LGPL
+Maintainer: John Goerzen <jgoerzen@complete.org>
+Author: John Goerzen
+Copyright: Copyright (c) 2007 John Goerzen
+license-file: COPYRIGHT
+extra-source-files: COPYING
+Category: Generics
+homepage: http://software.complete.org/listlike
+synopsis: Generic support for list-like structures
+Description: Generic support for list-like structures in Haskell.
+ .
+ The ListLike module provides a common interface to the various Haskell
+ types that are list-like.  Predefined interfaces include standard
+ Haskell lists, Arrays, ByteStrings, and lazy ByteStrings.  Custom
+ types can easily be made ListLike instances as well.
+ .
+ ListLike also provides for String-like types, such as String and
+ ByteString, for types that support input and output, and for types that can handle
+ infinite lists.
+Stability: Stable
+Hs-Source-Dirs: src
+Exposed-Modules: Data.ListLike,
+        Data.ListLike.Base,
+        Data.ListLike.FoldableLL,
+        Data.ListLike.IO,
+        Data.ListLike.Instances,
+        Data.ListLike.String,
+        Data.ListLike.Utils
+-- Other-Modules: Data.ConfigFile.Lexer
+Extensions: ExistentialQuantification, OverlappingInstances, 
+   UndecidableInstances
+Build-Depends: base,
+               haskell98, mtl
+GHC-Options: -O2
+
+Executable: runtests
+Buildable: False
+Main-Is: runtests.hs
+HS-Source-Dirs: testsrc, .
+Extensions: ExistentialQuantification, OverlappingInstances,
+    UndecidableInstances, CPP
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,5 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+
+main = defaultMain
+
diff --git a/src/Data/ListLike.hs b/src/Data/ListLike.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike.hs
@@ -0,0 +1,256 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Generic operations over list-like structures
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+Please start with the introduction at "Data.ListLike#intro".
+-}
+
+module Data.ListLike 
+                (-- * Introduction
+                 -- $intro
+                 
+                 -- * Creation & Basic Functions
+                 empty, singleton, 
+                 cons, snoc, append, head, last, tail, init, null, length,
+                 -- * List transformations
+                 map, rigidMap, reverse, intersperse,
+                 -- ** Conversions
+                 toList, fromList, fromListLike,
+                 -- * Reducing lists (folds), from "FoldableLL"
+                 foldl, foldl', foldl1, foldr, foldr', foldr1,
+                 -- ** Special folds
+                 concat, concatMap, rigidConcatMap,
+                 and, or,
+                 any, all,
+                 sum, product,
+                 maximum, minimum,
+                 fold, foldMap,
+                 -- * Building lists
+                 -- ** Scans
+                 -- ** Accumulating maps
+                 -- ** Infinite lists
+                 iterate, repeat, replicate, cycle,
+                 -- ** Unfolding
+                 -- * Sublists
+                 -- ** Extracting sublists
+                 take, drop, splitAt, takeWhile, dropWhile, span, break,
+                 group, inits, tails, 
+                 -- ** Predicates
+                 isPrefixOf, isSuffixOf, isInfixOf,
+                 -- * Searching lists
+                 -- ** Searching by equality
+                 elem, notElem,
+                 -- ** Searching with a predicate
+                 find, filter, partition,
+                 -- * Indexing lists
+                 index, elemIndex, elemIndices, findIndex, findIndices,
+                 -- * Zipping and unzipping lists
+                 zip, zipWith, unzip,
+                 -- * Monadic Operations
+                 sequence, sequence_, mapM, rigidMapM, mapM_,
+                 -- * Input and Output
+                 ListLikeIO(..),
+                 -- * Special lists
+                 -- ** Strings
+                 toString, fromString, lines, words,
+                 -- ** \"Set\" operations
+                 nub, delete, deleteFirsts, union, intersect,
+                 -- ** Ordered lists
+                 sort, insert,
+                 -- * Generalized functions
+                 -- ** The \"By\" operations
+                 -- *** User-supplied equality (replacing an Eq context)
+                 nubBy, deleteBy, deleteFirstsBy, unionBy, intersectBy,
+                 groupBy,
+                 -- *** User-supplied comparison (replacing an Ord context)
+                 sortBy, insertBy, -- maximumBy, minimumBy,
+                 -- ** The \"generic\" operations
+                 genericLength, genericTake, genericDrop, genericSplitAt,
+                 -- genericIndex,
+                 genericReplicate,
+                 -- * Notes on specific instances
+                 -- ** Lists
+                 -- $noteslist
+
+                 -- ** Arrays
+                 -- $notesarray
+
+                 -- ** Maps
+                 -- $notesmap
+
+                 -- ** ByteStrings
+                 -- $notesbytestring
+                 
+                 -- * Base Typeclasses
+                 -- ** The ListLike class
+                 ListLike,
+                 -- ** The FoldableLL class
+                 FoldableLL,
+                 -- ** The StringLike class
+                 StringLike,
+                 -- ** The InfiniteListLike class
+                 InfiniteListLike
+                )
+       where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords)
+import Data.ListLike.Base
+import Data.ListLike.FoldableLL
+import Data.ListLike.Instances()
+import Data.ListLike.String
+import Data.ListLike.Utils
+import Data.ListLike.IO
+
+{- $intro
+ #intro#
+Welcome to ListLike.
+
+This module provides abstractions over typical list operations.
+It is designed to let you freely interchange different ways to represent
+sequences of data.  It works with lists, various types of ByteStrings,
+and much more.
+
+In this module, you'll find generic versions of most of the functions
+you're used to using in the "Prelude", "Data.List", and "System.IO".
+They carry the
+same names, too.  Therefore, you'll want to be careful how you import
+the module.  I suggest using:
+
+>import qualified ListLike as LL
+
+Then, you can use LL.fold, LL.map, etc. to get the generic version of
+the functions you want.  Alternatively, you can hide the other versions
+from Prelude and import specific generic functions from here, such as:
+
+>import Prelude hiding (map)
+>import ListLike (map)
+
+The I\/O features of ListLike may a
+The module "Data.ListLike" actually simply re-exports the items found
+in a number of its sub-modules.  If you want a smaller subset of
+"Data.ListLike", look at the documentation for its sub-modules and import
+the relevant one.
+
+In most cases, functions here can act as drop-in replacements for their
+list-specific counterparts.  They will use the same underlying implementations
+for lists, so there should be no performance difference.
+    
+You can make your own types instances of 'ListLike' as well.  For more
+details, see the notes for the 'ListLike' typeclass.
+-}
+
+{- $noteslist
+
+Functions for operating on regular lists almost all use the native
+implementations in "Data.List", "Prelude", or similar standard
+modules.  The exceptions are:
+
+* 'mapM' uses the default 'ListLike' implementation
+
+* 'hGet' does not exist for 'String' in the Haskell modules.
+  It is implemented in terms of "Data.ByteString.Lazy".
+
+* 'hGetNonBlocking' is the same way. -}
+
+{- $notesarray
+
+'Data.Array.Array' is an instance of 'ListLike'.  Here are some notes about it:
+
+* The index you use must be an integral
+
+* 'ListLike' functions that take an index always take a 0-based index
+  for compatibility with other 'ListLike' instances.
+  This is translated by the instance functions into the proper offset from
+  the bounds in the Array.
+
+* 'ListLike' functions preserve the original Array index numbers when
+  possible.  Functions such as 'cons' will reduce the lower bound to do
+  their job.  'snoc' and 'append' increase the upper bound.  'drop' raises
+  the lower bound and 'take' lowers the upper bound.
+
+* Functions that change the length of the array by an amount not known
+  in advance, such as 'filter', will generate a new array with the lower
+  bound set to 0.  Furthermore, these functions cannot operate on infinite
+  lists because they must know their length in order to generate the
+  array.  'hGetContents' and its friends will therefore require the
+  entire file to be read into memory before processing is possible.
+
+* 'empty', 'singleton', and 'fromList' also generate an array with the
+  lower bound set to 0.
+
+* Many of these functions will generate runtime exceptions if you have
+  not assigned a value to every slot in the array.
+-}
+
+{- $notesmap
+
+'Data.Map.Map' is an instance of 'ListLike' and is a rather interesting one at
+that.  The \"item\" for the Map instance is a @(key, value)@ pair.  This
+permits you to do folds, maps, etc. over a Map just like you would on a list.
+
+The nature of a Map -- that every key is unique, and that it is internally
+sorted -- means that there are some special things to take note of:
+
+* 'cons' may or may not actually increase the size of the Map.  If the given
+   key is already in the map, its value will simply be updated.  Since
+   a Map has a set internal ordering, it is also not guaranteed that cons
+   will add something to the beginning of the Map.
+
+* 'snoc' is the same operation as 'cons'.
+
+* 'append' is 'Data.Map.union'
+
+* 'nub', 'nubBy',
+  'reverse', 'sort', 'sortBy', etc. are the identity function and don\'t
+  actually perform any computation
+
+* 'insert' is the same as 'cons'.
+
+* 'replicate' and 'genericReplicate' ignore the count and return a Map
+  with a single element.
+-}
+
+{- $notesbytestring
+
+Both strict and lazy ByteStreams can be used with 'ListLike'.
+
+Most 'ListLike' operations map directly to ByteStream options.  Notable
+exceptions:
+
+* 'map' uses the 'ListLike' implementation.  'rigidMap' is more efficient.
+  The same goes for 'concatMap' vs. 'rigidConcatMap'.
+
+* 'isInfixOf', 'sequence', 'mapM' and similar monad operations, 'insert', 
+  'union', 'intersect', 'sortBy', and similar functions are not implemented
+  in 'ByteStream' and use a naive default implementation.
+
+* The lazy ByteStream module implements fewer funtions than the strict
+  ByteStream module.  In some cases, default implementations are used.
+  In others, notably related to I\/O, the lazy ByteStreams are converted
+  back and forth to strict ones as appropriate.
+
+-}
diff --git a/src/Data/ListLike/Base.hs b/src/Data/ListLike/Base.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/Base.hs
@@ -0,0 +1,630 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike.Base
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Generic operations over list-like structures
+
+Written by John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.ListLike.Base 
+    (
+    ListLike(..),
+    InfiniteListLike(..),
+    zip, zipWith, sequence_
+    ) where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords)
+import qualified Data.List as L
+import Data.ListLike.FoldableLL
+import qualified Control.Monad as M
+import Data.Monoid
+import Data.Maybe
+
+{- | The class implementing list-like functions.
+
+It is worth noting that types such as 'Data.Map.Map' can be instances of
+'ListLike'.  Due to their specific ways of operating, they may not behave
+in the expected way in some cases.  For instance, 'cons' may not increase
+the size of a map if the key you have given is already in the map; it will
+just replace the value already there.
+
+Implementators must define at least:
+
+* singleton
+
+* head
+
+* tail
+
+* null or genericLength
+-}
+class (FoldableLL full item, Monoid full) =>
+    ListLike full item | full -> item where
+
+    ------------------------------ Creation
+    {- | The empty list -}
+    empty :: full
+    empty = mempty
+
+    {- | Creates a single-itement list out of an itement -}
+    singleton :: item -> full
+
+    ------------------------------ Basic Functions
+
+    {- | Like (:) for lists: adds an itement to the beginning of a list -}
+    cons :: item -> full -> full
+    cons item l = append (singleton item) l
+
+    {- | Adds an itement to the *end* of a 'ListLike'. -}
+    snoc :: full -> item -> full
+    snoc l item = append l (singleton item)
+
+    {- | Combines two lists.  Like (++). -}
+    append :: full -> full -> full 
+    append = mappend
+
+    {- | Extracts the first itement of a 'ListLike'. -}
+    head :: full -> item
+
+    {- | Extracts the last itement of a 'ListLike'. -}
+    last :: full -> item
+    last l = case genericLength l of
+                  (0::Integer) -> error "Called last on empty list"
+                  1 -> head l
+                  _ -> last (tail l)
+
+    {- | Gives all itements after the head. -}
+    tail :: full -> full 
+
+    {- | All elements of the list except the last one.  See also 'inits'. -}
+    init :: full -> full
+    init l
+        | null l = error "init: empty list"
+        | null xs = empty
+        | otherwise = cons (head l) (init xs)
+        where xs = tail l
+
+    {- | Tests whether the list is empty. -}
+    null :: full -> Bool
+    null x = genericLength x == (0::Integer)
+
+    {- | Length of the list.  See also 'genericLength'. -}
+    length :: full -> Int
+    length = genericLength
+
+    ------------------------------ List Transformations
+
+    {- | Apply a function to each element, returning any other
+         valid 'ListLike'.  'rigidMap' will always be at least
+         as fast, if not faster, than this function and is recommended
+         if it will work for your purposes.  See also 'mapM'. -}
+    map :: ListLike full' item' => (item -> item') -> full -> full'
+    map func inp  
+        | null inp = empty
+        | otherwise = cons (func (head inp)) (map func (tail inp))
+
+    {- | Like 'map', but without the possibility of changing the type of
+       the item.  This can have performance benefits for things such as
+       ByteStrings, since it will let the ByteString use its native
+       low-level map implementation. -}
+    rigidMap :: (item -> item) -> full -> full
+    rigidMap = map
+
+    {- | Reverse the elements in a list. -}
+    reverse :: full -> full 
+    reverse l = rev l empty
+        where rev rl a
+                | null rl = a
+                | otherwise = rev (tail rl) (cons (head rl) a)
+    {- | Add an item between each element in the structure -}
+    intersperse :: item -> full -> full
+    intersperse sep l
+        | null l = empty
+        | null xs = singleton x
+        | otherwise = cons x (cons sep (intersperse sep xs))
+        where x = head l
+              xs = tail l
+
+    ------------------------------ Reducing Lists (folds)
+    -- See also functions in FoldableLLL
+
+    ------------------------------ Special folds
+    {- | Flatten the structure. -}
+    concat :: (ListLike full' full, Monoid full) => full' -> full
+    concat = fold
+
+    {- | Map a function over the items and concatenate the results.
+         See also 'rigidConcatMap'.-}
+    concatMap :: (ListLike full' item') =>
+                 (item -> full') -> full -> full'
+    concatMap = foldMap
+
+    {- | Like 'concatMap', but without the possibility of changing
+         the type of the item.  This can have performance benefits
+         for some things such as ByteString. -}
+    rigidConcatMap :: (item -> full) -> full -> full
+    rigidConcatMap = concatMap
+
+    {- | True if any items satisfy the function -}
+    any :: (item -> Bool) -> full -> Bool
+    any p = getAny . foldMap (Any . p)
+
+    {- | True if all items satisfy the function -}
+    all :: (item -> Bool) -> full -> Bool
+    all p = getAll . foldMap (All . p)
+
+    {- | The maximum value of the list -}
+    maximum :: Ord item => full -> item
+    maximum = foldr1 max
+
+    {- | The minimum value of the list -}
+    minimum :: Ord item => full -> item
+    minimum = foldr1 min
+
+    ------------------------------ Infinite lists
+    {- | Generate a structure with the specified length with every element
+    set to the item passed in.  See also 'genericReplicate' -}
+    replicate :: Int -> item -> full
+    replicate = genericReplicate
+
+    ------------------------------ Sublists
+    {- | Takes the first n elements of the list.  See also 'genericTake'. -}
+    take :: Int -> full -> full
+    take = genericTake
+
+    {- | Drops the first n elements of the list.  See also 'genericDrop' -}
+    drop :: Int -> full -> full
+    drop = genericDrop
+
+    {- | Equivalent to @('take' n xs, 'drop' n xs)@.  See also 'genericSplitAt'. -}
+    splitAt :: Int -> full -> (full, full)
+    splitAt = genericSplitAt
+
+    {- | Returns all elements at start of list that satisfy the function. -}
+    takeWhile :: (item -> Bool) -> full -> full
+    takeWhile func l 
+        | null l = empty
+        | func x = cons x (takeWhile func (tail l))
+        | otherwise = empty
+        where x = head l
+
+    {- | Drops all elements form the start of the list that satisfy the
+       function. -}
+    dropWhile :: (item -> Bool) -> full -> full
+    dropWhile func l
+        | null l = empty
+        | func (head l) = dropWhile func (tail l)
+        | otherwise = l
+
+    {- | The equivalent of @('takeWhile' f xs, 'dropWhile' f xs)@ -}
+    span :: (item -> Bool) -> full -> (full, full)
+    span func l
+        | null l = (empty, empty)
+        | func x = (cons x ys, zs) 
+        | otherwise = (empty, l)
+       where (ys, zs) = span func (tail l)
+             x = head l
+    {- | The equivalent of @'span' ('not' . f)@ -}
+    break :: (item -> Bool) -> full -> (full, full)
+    break p = span (not . p)
+
+    {- | Split a list into sublists, each which contains equal arguments.
+       For order-preserving types, concatenating these sublists will produce
+       the original list. See also 'groupBy'. -}
+    group :: (ListLike full' full, Eq item) => full -> full'
+    group = groupBy (==)
+
+    {- | All initial segments of the list, shortest first -}
+    inits :: (ListLike full' full) => full -> full'
+    inits l
+        | null l = singleton empty
+        | otherwise =
+            append (singleton empty)
+                   (map (cons (head l)) theinits)
+            where theinits = asTypeOf (inits (tail l)) [l]
+
+    {- | All final segnemts, longest first -}
+    tails :: ListLike full' full => full -> full'
+    tails l
+        | null l = singleton empty
+        | otherwise = cons l (tails (tail l))
+
+    ------------------------------ Predicates
+    {- | True when the first list is at the beginning of the second. -}
+    isPrefixOf :: Eq item => full -> full -> Bool
+    isPrefixOf needle haystack
+        | null needle = True
+        | null haystack = False
+        | otherwise = (head needle) == (head haystack) && 
+                      isPrefixOf (tail needle) (tail haystack)
+
+    {- | True when the first list is at the beginning of the second. -}
+    isSuffixOf :: Eq item => full -> full -> Bool
+    isSuffixOf needle haystack = isPrefixOf (reverse needle) (reverse haystack)
+
+    {- | True when the first list is wholly containted within the second -}
+    isInfixOf :: Eq item => full -> full -> Bool
+    isInfixOf needle haystack = 
+        any (isPrefixOf needle) thetails
+        where thetails = asTypeOf (tails haystack) [haystack]
+
+    ------------------------------ Searching
+    {- | True if the item occurs in the list -}
+    elem :: Eq item => item -> full -> Bool
+    elem i = any (== i)
+
+    {- | True if the item does not occur in the list -}
+    notElem :: Eq item => item -> full -> Bool
+    notElem i = all (/= i)
+
+    {- | Take a function and return the first matching element, or Nothing
+       if there is no such element. -}
+    find :: (item -> Bool) -> full -> Maybe item
+    find f l = case findIndex f l of
+                    Nothing -> Nothing
+                    Just x -> Just (index l x)
+
+    {- | Returns only the elements that satisfy the function. -}
+    filter :: (item -> Bool) -> full -> full 
+    filter func l 
+        | null l = empty
+        | func (head l) = cons (head l) (filter func (tail l))
+        | otherwise = filter func (tail l)
+
+    {- | Returns the lists that do and do not satisfy the function.
+       Same as @('filter' p xs, 'filter' ('not' . p) xs)@ -}
+    partition :: (item -> Bool) -> full -> (full, full)
+    partition p xs = (filter p xs, filter (not . p) xs)
+
+    ------------------------------ Indexing
+    {- | The element at 0-based index i.  Raises an exception if i is out
+         of bounds.  Like (!!) for lists. -}
+    index :: full -> Int -> item
+    index l i 
+        | null l = error "index: index not found"
+        | i < 0 = error "index: index must be >= 0"
+        | i == 0 = head l
+        | otherwise = index (tail l) (i - 1)
+
+    {- | Returns the index of the element, if it exists. -}
+    elemIndex :: Eq item => item -> full -> Maybe Int
+    elemIndex e l = findIndex (== e) l
+
+    {- | Returns the indices of the matching elements.  See also 
+       'findIndices' -}
+    elemIndices :: (Eq item, ListLike result Int) => item -> full -> result
+    elemIndices i l = findIndices (== i) l
+
+    {- | Take a function and return the index of the first matching element,
+         or Nothing if no element matches -}
+    findIndex :: (item -> Bool) -> full -> Maybe Int
+    findIndex f = listToMaybe . findIndices f
+
+    {- | Returns the indices of all elements satisfying the function -}
+    findIndices :: (ListLike result Int) => (item -> Bool) -> full -> result
+    findIndices p xs = map snd $ filter (p . fst) $ thezips
+        where thezips = asTypeOf (zip xs [0..]) [(head xs, 0::Int)]
+
+    ------------------------------ Monadic operations
+    {- | Evaluate each action in the sequence and collect the results -}
+    sequence :: (Monad m, ListLike fullinp (m item)) =>
+                fullinp -> m full
+    sequence l = foldr func (return empty) l
+        where func litem results = 
+                do x <- litem
+                   xs <- results
+                   return (cons x xs)
+
+    {- | A map in monad space.  Same as @'sequence' . 'map'@ 
+         
+         See also 'rigidMapM' -}
+    mapM :: (Monad m, ListLike full' item') => 
+            (item -> m item') -> full -> m full'
+    mapM func l = sequence mapresult
+            where mapresult = asTypeOf (map func l) []
+
+    {- | Like 'mapM', but without the possibility of changing the type
+         of the item.  This can have performance benefits with some types. -}
+    rigidMapM :: Monad m => (item -> m item) -> full -> m full
+    rigidMapM = mapM
+            
+    {- | A map in monad space, discarding results.  Same as
+       @'sequence_' . 'map'@ -}
+    mapM_ :: (Monad m) => (item -> m b) -> full -> m ()
+    mapM_ func l = sequence_ mapresult
+            where mapresult = asTypeOf (map func l) []
+
+
+    ------------------------------ "Set" operations
+    {- | Removes duplicate elements from the list.  See also 'nubBy' -}
+    nub :: Eq item => full -> full
+    nub = nubBy (==)
+
+    {- | Removes the first instance of the element from the list.
+       See also 'deleteBy' -}
+    delete :: Eq item => item -> full -> full
+    delete = deleteBy (==)
+
+    {- | List difference.  Removes from the first list the first instance
+       of each element of the second list.  See '(\\)' and 'deleteFirstsBy' -}
+    deleteFirsts :: Eq item => full -> full -> full
+    deleteFirsts = foldl (flip delete)
+
+    {- | List union: the set of elements that occur in either list.
+         Duplicate elements in the first list will remain duplicate.
+         See also 'unionBy'. -}
+    union :: Eq item => full -> full -> full
+    union = unionBy (==)
+
+    {- | List intersection: the set of elements that occur in both lists.
+         See also 'intersectBy' -}
+    intersect :: Eq item => full -> full -> full
+    intersect = intersectBy (==)
+
+    ------------------------------ Ordered lists
+    {- | Sorts the list.  On data types that do not preserve ordering,
+         or enforce their own ordering, the result may not be what
+         you expect.  See also 'sortBy'. -}
+    sort :: Ord item => full -> full
+    sort = sortBy compare
+
+    {- | Inserts the itement at the last place where it is still less than or
+         equal to the next itement.  On data types that do not preserve 
+         ordering, or enforce their own ordering, the result may not
+         be what you expect.  On types such as maps, this may result in
+         changing an existing item.  See also 'insertBy'. -}
+    insert :: Ord item => item -> full -> full 
+    insert = insertBy compare
+
+    ------------------------------ Conversions
+
+    {- | Converts the structure to a list.  This is logically equivolent
+         to 'fromListLike', but may have a more optimized implementation. -}
+    toList :: full -> [item]
+    toList = fromListLike
+
+    {- | Generates the structure from a list. -}
+    fromList :: [item] -> full 
+    fromList [] = empty
+    fromList (x:xs) = cons x (fromList xs)
+
+    {- | Converts one ListLike to another.  See also 'toList'.
+         Default implementation is @fromListLike = map id@ -}
+    fromListLike :: ListLike full' item => full -> full'
+    fromListLike = map id
+
+    ------------------------------ Generalized functions
+    {- | Generic version of 'nub' -}
+    nubBy :: (item -> item -> Bool) -> full -> full
+    nubBy f l
+        | null l = empty
+        | otherwise =
+            cons (head l) (nubBy f (filter (\y -> not (f (head l) y)) (tail l)))
+
+    {- | Generic version of 'deleteBy' -}
+    deleteBy :: (item -> item -> Bool) -> item -> full -> full
+    deleteBy func i l
+        | null l = empty
+        | otherwise =
+            if func i (head l)
+               then tail l
+               else cons (head l) (deleteBy func i (tail l))
+
+    {- | Generic version of 'deleteFirsts' -}
+    deleteFirstsBy :: (item -> item -> Bool) -> full -> full -> full
+    deleteFirstsBy func = foldl (flip (deleteBy func))
+
+    {- | Generic version of 'union' -}
+    unionBy :: (item -> item -> Bool) -> full -> full -> full
+    unionBy func x y =
+        append x $ foldl (flip (deleteBy func)) (nubBy func y) x
+
+    {- | Generic version of 'intersect' -}
+    intersectBy :: (item -> item -> Bool) -> full -> full -> full
+    intersectBy func xs ys = filter (\x -> any (func x) ys) xs
+
+    {- | Generic version of 'group'. -}
+    groupBy :: (ListLike full' full, Eq item) => 
+                (item -> item -> Bool) -> full -> full'
+    groupBy eq l
+        | null l = empty
+        | otherwise = cons (cons x ys) (groupBy eq zs)
+                      where (ys, zs) = span (eq x) xs
+                            x = head l
+                            xs = tail l
+
+    {- | Sort function taking a custom comparison function -}
+    sortBy :: Ord item => (item -> item -> Ordering) -> full -> full 
+    sortBy cmp = foldr (insertBy cmp) empty
+
+    {- | Like 'insert', but with a custom comparison function -}
+    insertBy :: Ord item => (item -> item -> Ordering) -> item ->
+                full -> full 
+    insertBy cmp x ys
+        | null ys = singleton x
+        | otherwise = case cmp x (head ys) of
+                        GT -> cons (head ys) (insertBy cmp x (tail ys))
+                        _ ->  cons x ys
+
+    ------------------------------ Generic Operations
+    {- | Length of the list -}
+    genericLength :: Num a => full -> a
+    genericLength l = calclen 0 l
+        where calclen accum cl =
+                  if null cl
+                     then accum
+                     else calclen (accum + 1) (tail cl)
+
+    {- | Generic version of 'take' -}
+    genericTake :: Integral a => a -> full -> full
+    genericTake n l
+        | n <= 0 = empty
+        | null l = empty
+        | otherwise = cons (head l) (genericTake (n - 1) (tail l))
+
+    {- | Generic version of 'drop' -}
+    genericDrop :: Integral a => a -> full -> full
+    genericDrop n l 
+        | n <= 0 = l
+        | null l = l
+        | otherwise = genericDrop (n - 1) (tail l)
+
+    {- | Generic version of 'splitAt' -}
+    genericSplitAt :: Integral a => a -> full -> (full, full)
+    genericSplitAt n l = (genericTake n l, genericDrop n l)
+
+    {- | Generic version of 'replicate' -}
+    genericReplicate :: Integral a => a -> item -> full
+    genericReplicate count x 
+        | count <= 0 = empty
+        | otherwise = map (\_ -> x) [1..count]
+
+{-
+instance (ListLike full item) => Monad full where
+    m >>= k = foldr (append . k) empty m
+    m >> k = foldr (append . (\_ -> k)) empty m
+    return x = singleton x
+    fail _ = empty
+
+instance (ListLike full item) => M.MonadPlus full where
+    mzero = empty
+    mplus = append
+-}
+
+{- | An extension to 'ListLike' for those data types that are capable
+of dealing with infinite lists.  Some 'ListLike' functions are capable
+of working with finite or infinite lists.  The functions here require
+infinite list capability in order to work at all. -}
+class (ListLike full item) => InfiniteListLike full item | full -> item where
+    {- | An infinite list of repeated calls of the function to args -}
+    iterate :: (item -> item) -> item -> full
+    iterate f x = cons x (iterate f (f x))
+
+    {- | An infinite list where each element is the same -}
+    repeat :: item -> full
+    repeat x = xs
+        where xs = cons x xs
+
+    {- | Converts a finite list into a circular one -}
+    cycle :: full -> full
+    cycle xs 
+        | null xs = error "ListLike.cycle: empty list"
+        | otherwise = xs' where xs' = append xs xs'
+
+--------------------------------------------------
+-- This instance is here due to some default class functions
+
+instance ListLike [a] a where
+    empty = []
+    singleton x = [x]
+    cons x l = x : l
+    snoc l x = l ++ [x]
+    append = (++)
+    head = L.head
+    last = L.last
+    tail = L.tail
+    init = L.init
+    null = L.null
+    length = L.length
+    map f = fromList . L.map f
+    rigidMap = L.map
+    reverse = L.reverse
+    intersperse = L.intersperse
+    toList = id
+    fromList = id
+    -- fromListLike = toList
+    concat = L.concat . toList
+    -- concatMap func = fromList . L.concatMap func
+    rigidConcatMap = L.concatMap
+    any = L.any
+    all = L.all
+    maximum = L.maximum
+    minimum = L.minimum
+    -- fold
+    -- foldMap
+    replicate = L.replicate
+    take = L.take
+    drop = L.drop
+    splitAt = L.splitAt
+    takeWhile = L.takeWhile
+    dropWhile = L.dropWhile
+    span = L.span
+    break = L.break
+    group = fromList . L.group
+    inits = fromList . L.inits
+    tails = fromList . L.tails
+    isPrefixOf = L.isPrefixOf
+    isSuffixOf = L.isSuffixOf
+    isInfixOf = L.isInfixOf
+    elem = L.elem
+    notElem = L.notElem
+    find = L.find
+    filter = L.filter
+    partition = L.partition
+    index = (L.!!)
+    elemIndex = L.elemIndex
+    elemIndices item = fromList . L.elemIndices item
+    findIndex = L.findIndex
+    sequence = M.sequence . toList
+    -- mapM = M.mapM
+    mapM_ = M.mapM_
+    nub = L.nub
+    delete = L.delete
+    deleteFirsts = (L.\\)
+    union = L.union
+    intersect = L.intersect
+    sort = L.sort
+    groupBy func = fromList . L.groupBy func
+    unionBy = L.unionBy
+    intersectBy = L.intersectBy
+    sortBy = L.sortBy
+    insert = L.insert
+
+
+--------------------------------------------------
+-- These utils are here instead of in Utils.hs because they are needed
+-- by default class functions
+
+{- | Takes two lists and returns a list of corresponding pairs. -}
+zip :: (ListLike full item,
+          ListLike fullb itemb,
+          ListLike result (item, itemb)) =>
+          full -> fullb -> result
+zip = zipWith (\a b -> (a, b))
+
+{- | Takes two lists and combines them with a custom combining function -}
+zipWith :: (ListLike full item,
+            ListLike fullb itemb,
+            ListLike result resultitem) =>
+            (item -> itemb -> resultitem) -> full -> fullb -> result
+zipWith f a b
+    | null a = empty
+    | null b = empty
+    | otherwise = cons (f (head a) (head b)) (zipWith f (tail a) (tail b))
+
+{- | Evaluate each action, ignoring the results -}
+sequence_ :: (Monad m, ListLike mfull (m item)) => mfull -> m ()
+sequence_ l = foldr (>>) (return ()) l
+
+
diff --git a/src/Data/ListLike/FoldableLL.hs b/src/Data/ListLike/FoldableLL.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/FoldableLL.hs
@@ -0,0 +1,100 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+-}
+
+{- |
+   Module     : Data.ListLike.FoldableLL
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Generic tools for data structures that can be folded.
+
+Written by John Goerzen, jgoerzen\@complete.org
+
+-}
+module Data.ListLike.FoldableLL 
+    (-- * FoldableLL Class
+     FoldableLL(..),
+     -- * Utilities
+     fold, foldMap
+    ) where 
+import Prelude hiding (foldl, foldr, foldr1)
+import qualified Data.Foldable as F
+import Data.Monoid
+import Data.Maybe
+import qualified Data.List as L
+
+{- | This is the primary class for structures that are to be considered 
+foldable.  A minimum complete definition provides 'foldl' and 'foldr'.
+
+Instances of 'FoldableLL' can be folded, and can be many and varied.
+
+These functions are used heavily in "Data.ListLike". -}
+class FoldableLL full item | full -> item where
+    {- | Left-associative fold -}
+    foldl :: (a -> item -> a) -> a -> full -> a
+
+    {- | Strict version of 'foldl'. -}
+    foldl' :: (a -> item -> a) -> a -> full -> a
+    -- This implementation from Data.Foldable
+    foldl' f a xs = foldr f' id xs a
+        where f' x k z = k $! f z x
+
+    -- | A variant of 'foldl' with no base case.  Requires at least 1
+    -- list element.
+    foldl1 :: (item -> item -> item) -> full -> item
+    -- This implementation from Data.Foldable
+    foldl1 f xs = fromMaybe (error "fold1: empty structure")
+                    (foldl mf Nothing xs)
+           where mf Nothing y = Just y
+                 mf (Just x) y = Just (f x y)
+    {- | Right-associative fold -}
+    foldr :: (item -> b -> b) -> b -> full -> b
+
+    -- | Strict version of 'foldr'
+    foldr' :: (item -> b -> b) -> b -> full -> b
+    -- This implementation from Data.Foldable
+    foldr' f a xs = foldl f' id xs a
+        where f' k x z = k $! f x z
+
+    -- | Like 'foldr', but with no starting value
+    foldr1 :: (item -> item -> item) -> full -> item
+    -- This implementation from Data.Foldable
+    foldr1 f xs = fromMaybe (error "foldr1: empty structure")
+                    (foldr mf Nothing xs)
+           where mf x Nothing = Just x
+                 mf x (Just y) = Just (f x y)
+
+{- | Combine the elements of a structure using a monoid.
+     @'fold' = 'foldMap' id@ -}
+fold :: (FoldableLL full item, Monoid item) => full -> item
+fold = foldMap id
+
+{- | Map each element to a monoid, then combine the results -}
+foldMap :: (FoldableLL full item, Monoid m) => (item -> m) -> full -> m
+foldMap f = foldr (mappend . f) mempty
+
+instance FoldableLL [a] a where
+    foldl = L.foldl
+    foldl1 = L.foldl1
+    foldl' = L.foldl'
+    foldr = L.foldr
+    foldr1 = L.foldr1
+    foldr' = F.foldr'
+{-
+instance (F.Foldable f) => FoldableLL (f a) a where
+    foldl = F.foldl
+    foldl1 = F.foldl1
+    foldl' = F.foldl'
+    foldr = F.foldr
+    foldr1 = F.foldr1
+    foldr' = F.foldr'
+-}
diff --git a/src/Data/ListLike/IO.hs b/src/Data/ListLike/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/IO.hs
@@ -0,0 +1,125 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike.IO
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+String-like functions
+
+Written by John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.ListLike.IO
+    ( ListLikeIO(..)
+    )
+       where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords, putStr, getContents)
+import qualified System.IO as IO
+import Data.ListLike.Base
+
+{- | An extension to 'ListLike' for those data types that support
+I\/O.  These functions mirror those in "System.IO" for the most part.  They
+also share the same names; see the comments in "Data.ListLike" for help
+importing them.
+
+Note that some types may not be capable of lazy reading or writing.
+Therefore, the usual semantics of "System.IO" functions regarding laziness
+may or may not be available from a particular implementation.
+    
+Minimal complete definition:
+
+* hGetLine
+
+* hGetContents
+
+* hGet
+
+* hGetNonBlocking
+
+* hPutStr
+-}
+class (ListLike full item) => ListLikeIO full item | full -> item where
+    {- | Reads a line from the specified handle -}
+    hGetLine :: IO.Handle -> IO full
+
+    -- | Read entire handle contents.  May be done lazily like
+    -- 'System.IO.hGetContents'.
+    hGetContents :: IO.Handle -> IO full
+
+    -- | Read specified number of bytes.  See 'System.IO.hGet' for
+    -- particular semantics.
+    hGet :: IO.Handle -> Int -> IO full
+
+    -- | Non-blocking read.  See 'System.IO.hGetNonBlocking' for more.
+    hGetNonBlocking :: IO.Handle -> Int -> IO full
+
+    -- | Writing entire data.
+    hPutStr :: IO.Handle -> full -> IO ()
+
+    -- | Write data plus newline character.
+    hPutStrLn :: IO.Handle -> full -> IO ()
+    hPutStrLn fp x =
+        do hPutStr fp x
+           IO.hPutStrLn fp ""
+
+    -- | Read one line
+    getLine :: IO full
+    getLine = hGetLine IO.stdin
+
+    -- | Read entire content from stdin.  See 'hGetContents'.
+    getContents :: IO full
+    getContents = hGetContents IO.stdin
+
+    -- | Write data to stdout.
+    putStr :: full -> IO ()
+    putStr = hPutStr IO.stdout
+
+    -- | Write data plus newline character to stdout.
+    putStrLn :: full -> IO ()
+    putStrLn = hPutStrLn IO.stdout
+
+    -- | Interact with stdin and stdout by using a function to transform
+    -- input to output.  May be lazy.  See 'System.IO.interact' for more.
+    interact :: (full -> full) -> IO ()
+    interact func = 
+        do c <- getContents
+           putStr (func c)
+
+    -- | Read file.  May be lazy.
+    readFile :: FilePath -> IO full
+    readFile fn =
+        do fp <- IO.openFile fn IO.ReadMode
+           hGetContents fp
+
+    -- | Write data to file.
+    writeFile :: FilePath -> full -> IO ()
+    writeFile fn x =
+        do fp <- IO.openFile fn IO.WriteMode
+           hPutStr fp x
+           IO.hClose fp
+
+    -- | Append data to file.
+    appendFile :: FilePath -> full -> IO ()
+    appendFile fn x =
+        do fp <- IO.openFile fn IO.AppendMode
+           hPutStr fp x
+           IO.hClose fp
diff --git a/src/Data/ListLike/Instances.hs b/src/Data/ListLike/Instances.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/Instances.hs
@@ -0,0 +1,542 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike.Instances
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Instances of 'Data.ListLike.ListLike' and related classes.
+Re-exported by "Data.ListLike".
+
+Written by John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.ListLike.Instances () where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords)
+import qualified Data.List as L
+import Data.ListLike.Base
+import Data.ListLike.String
+import Data.ListLike.IO
+import Data.ListLike.FoldableLL
+import Data.Int
+import Data.Monoid
+import qualified Data.ByteString as BS
+import qualified Data.Foldable as F
+import qualified Data.Array.IArray as A
+import Data.Array.IArray((!), (//), Ix(..))
+import qualified Data.ByteString.Lazy as BSL
+import qualified System.IO as IO
+import Data.Word
+import qualified Data.Map as Map
+
+--------------------------------------------------
+-- []
+
+-- Basic list instance is in Base.hs
+-- FoldableLL instance implied by Foldable
+
+instance ListLikeIO String Char where
+    hGetLine = IO.hGetLine
+    hGetContents = IO.hGetContents
+    hGet h c = BSL.hGet h c >>= (return . toString)
+    hGetNonBlocking h i = BSL.hGetNonBlocking h i >>= (return . toString)
+    hPutStr = IO.hPutStr
+    hPutStrLn = IO.hPutStrLn
+    getLine = IO.getLine
+    getContents = IO.getContents
+    putStr = IO.putStr
+    putStrLn = IO.putStrLn
+    interact = IO.interact
+    readFile = IO.readFile
+    writeFile = IO.writeFile
+
+instance StringLike String where
+    toString = id
+    fromString = id
+
+instance InfiniteListLike [a] a where
+    iterate = L.iterate
+    repeat = L.repeat
+    cycle = L.cycle
+
+--------------------------------------------------
+-- ByteString
+
+instance FoldableLL BS.ByteString Word8 where
+    foldl = BS.foldl
+    foldl' = BS.foldl'
+    foldl1 = BS.foldl1
+    foldr = BS.foldr
+    foldr' = BS.foldr'
+    foldr1 = BS.foldr1
+
+instance ListLike BS.ByteString Word8 where
+    empty = BS.empty
+    singleton = BS.singleton
+    cons = BS.cons
+    snoc = BS.snoc
+    append = BS.append
+    head = BS.head
+    last = BS.last
+    tail = BS.tail
+    init = BS.init
+    null = BS.null
+    length = BS.length
+    -- map = BS.map
+    rigidMap = BS.map
+    reverse = BS.reverse
+    intersperse = BS.intersperse
+    concat = BS.concat . toList
+    --concatMap = BS.concatMap
+    rigidConcatMap = BS.concatMap
+    any = BS.any
+    all = BS.all
+    maximum = BS.maximum
+    minimum = BS.minimum
+    replicate = BS.replicate
+    take = BS.take
+    drop = BS.drop
+    splitAt = BS.splitAt
+    takeWhile = BS.takeWhile
+    dropWhile = BS.dropWhile
+    span = BS.span
+    break = BS.break
+    group = fromList . BS.group
+    inits = fromList . BS.inits
+    tails = fromList . BS.tails
+    isPrefixOf = BS.isPrefixOf
+    isSuffixOf = BS.isSuffixOf
+    --isInfixOf = BS.isInfixOf
+    elem = BS.elem
+    notElem = BS.notElem
+    find = BS.find
+    filter = BS.filter
+    --partition = BS.partition
+    index = BS.index
+    elemIndex = BS.elemIndex
+    elemIndices x = fromList . BS.elemIndices x
+    findIndex = BS.findIndex
+    findIndices x = fromList . BS.findIndices x
+    --sequence = BS.sequence
+    --mapM = BS.mapM
+    --mapM_ = BS.mapM_
+    --nub = BS.nub
+    --delete = BS.delete
+    --deleteFirsts = BS.deleteFirsts
+    --union = BS.union
+    --intersect = BS.intersect
+    sort = BS.sort
+    --insert = BS.insert
+    toList = BS.unpack
+    fromList = BS.pack
+    fromListLike = fromList . toList
+    --nubBy = BS.nubBy
+    --deleteBy = BS.deleteBy
+    --deleteFirstsBy = BS.deleteFirstsBy
+    --unionBy = BS.unionBy
+    --intersectBy = BS.intersectBy
+    groupBy f = fromList . BS.groupBy f
+    --sortBy = BS.sortBy
+    --insertBy = BS.insertBy
+    genericLength = fromInteger . fromIntegral . BS.length
+    genericTake i = BS.take (fromIntegral i)
+    genericDrop i = BS.drop (fromIntegral i)
+    genericSplitAt i = BS.splitAt (fromIntegral i)
+    genericReplicate i = BS.replicate (fromIntegral i)
+
+instance ListLikeIO BS.ByteString Word8 where
+    hGetLine = BS.hGetLine
+    hGetContents = BS.hGetContents
+    hGet = BS.hGet
+    hGetNonBlocking = BS.hGetNonBlocking
+    hPutStr = BS.hPutStr
+    hPutStrLn = BS.hPutStrLn
+    getLine = BS.getLine
+    getContents = BS.getContents
+    putStr = BS.putStr
+    putStrLn = BS.putStrLn
+    interact = BS.interact
+    readFile = BS.readFile
+    writeFile = BS.writeFile
+    appendFile = BS.appendFile
+
+instance StringLike BS.ByteString where
+    toString = map (toEnum . fromIntegral) . BS.unpack
+    fromString = BS.pack . map (fromIntegral . fromEnum)
+
+--------------------------------------------------
+-- ByteString.Lazy
+
+instance FoldableLL BSL.ByteString Word8 where
+    foldl = BSL.foldl
+    foldl' = BSL.foldl'
+    foldl1 = BSL.foldl1
+    foldr = BSL.foldr
+    --foldr' = BSL.foldr'
+    foldr1 = BSL.foldr1
+
+mi64toi :: Maybe Int64 -> Maybe Int
+mi64toi Nothing = Nothing
+mi64toi (Just x) = Just (fromIntegral x)
+
+instance ListLike BSL.ByteString Word8 where
+    empty = BSL.empty
+    singleton = BSL.singleton
+    cons = BSL.cons
+    snoc = BSL.snoc
+    append = BSL.append
+    head = BSL.head
+    last = BSL.last
+    tail = BSL.tail
+    init = BSL.init
+    null = BSL.null
+    length = fromIntegral . BSL.length
+    -- map = BSL.map
+    rigidMap = BSL.map
+    reverse = BSL.reverse
+    --intersperse = BSL.intersperse
+    concat = BSL.concat . toList
+    --concatMap = BSL.concatMap
+    rigidConcatMap = BSL.concatMap
+    any = BSL.any
+    all = BSL.all
+    maximum = BSL.maximum
+    minimum = BSL.minimum
+    replicate i = BSL.replicate (fromIntegral i)
+    take i = BSL.take (fromIntegral i)
+    drop i = BSL.drop (fromIntegral i)
+    splitAt i = BSL.splitAt (fromIntegral i)
+    takeWhile = BSL.takeWhile
+    dropWhile = BSL.dropWhile
+    span = BSL.span
+    break = BSL.break
+    group = fromList . BSL.group
+    inits = fromList . BSL.inits
+    tails = fromList . BSL.tails
+    isPrefixOf = BSL.isPrefixOf
+    --isSuffixOf = BSL.isSuffixOf
+    --isInfixOf = BSL.isInfixOf
+    elem = BSL.elem
+    notElem = BSL.notElem
+    find = BSL.find
+    filter = BSL.filter
+    --partition = BSL.partition
+    index l i = BSL.index l (fromIntegral i)
+    elemIndex i = mi64toi . BSL.elemIndex i 
+    --elemIndices x = fromList . L.map fromIntegral . BSL.elemIndices x
+    findIndex f = mi64toi . BSL.findIndex f
+    --findIndices x = fromList . L.map fromIntegral . BSL.findIndices x
+    --sequence = BSL.sequence
+    --mapM = BSL.mapM
+    --mapM_ = BSL.mapM_
+    --nub = BSL.nub
+    --delete = BSL.delete
+    --deleteFirsts = BSL.deleteFirsts
+    --union = BSL.union
+    --intersect = BSL.intersect
+    --sort = BSL.sort
+    --insert = BSL.insert
+    toList = BSL.unpack
+    fromList = BSL.pack
+    fromListLike = fromList . toList
+    --nubBy = BSL.nubBy
+    --deleteBy = BSL.deleteBy
+    --deleteFirstsBy = BSL.deleteFirstsBy
+    --unionBy = BSL.unionBy
+    --intersectBy = BSL.intersectBy
+    -- BSL.groupBy is broken. groupBy f = fromList . BSL.groupBy f
+    -- the below works on ghc but generates a type error on hugs
+    -- groupBy func = map fromList . L.groupBy func . toList
+    --sortBy = BSL.sortBy
+    --insertBy = BSL.insertBy
+    genericLength = fromInteger . fromIntegral . BSL.length
+    genericTake i = BSL.take (fromIntegral i)
+    genericDrop i = BSL.drop (fromIntegral i)
+    genericSplitAt i = BSL.splitAt (fromIntegral i)
+    genericReplicate i = BSL.replicate (fromIntegral i)
+
+strict2lazy :: BS.ByteString -> IO BSL.ByteString
+strict2lazy b = return (BSL.fromChunks [b])
+instance ListLikeIO BSL.ByteString Word8 where
+    hGetLine h = BS.hGetLine h >>= strict2lazy
+    hGetContents = BSL.hGetContents
+    hGet = BSL.hGet
+    hGetNonBlocking = BSL.hGetNonBlocking
+    hPutStr = BSL.hPut
+    --hPutStrLn = BSL.hPutStrLn
+    getLine = BS.getLine >>= strict2lazy
+    getContents = BSL.getContents
+    putStr = BSL.putStr
+    putStrLn = BSL.putStrLn
+    interact = BSL.interact
+    readFile = BSL.readFile
+    writeFile = BSL.writeFile
+    appendFile = BSL.appendFile
+
+instance StringLike BSL.ByteString where
+    toString = map (toEnum . fromIntegral) . BSL.unpack
+    fromString = BSL.pack . map (fromIntegral . fromEnum)
+
+--------------------------------------------------
+-- Map
+
+instance (Ord key) => FoldableLL (Map.Map key val) (key, val) where
+    foldr f start m = Map.foldWithKey func start m
+            where func k v accum = f (k, v) accum
+    foldl f start m = L.foldl f start (Map.toList m)
+
+l2m :: (Ord k, Ord k2) => ([(k, v)], [(k2, v2)]) -> (Map.Map k v, Map.Map k2 v2)
+l2m (l1, l2) = (Map.fromList l1, Map.fromList l2)
+instance (Ord key, Eq val) => ListLike (Map.Map key val) (key, val) where
+    empty = Map.empty
+    singleton (k, v) = Map.singleton k v
+    cons (k, v) m = Map.insert k v m
+    snoc = flip cons
+    append = Map.union
+    head = Map.elemAt 0
+    last m = Map.elemAt (Map.size m - 1) m
+    -- was deleteAt 0, but that is broken in GHC 6.6
+    tail = drop 1
+    -- broken in GHC 6.6: init m = Map.deleteAt (Map.size m - 1) m
+    init = Map.fromAscList . L.init . Map.toAscList
+    null = Map.null
+    length = Map.size
+    map f = fromList . map f . Map.toList
+    rigidMap f = Map.fromList . L.map f . Map.toList
+    reverse = id
+    intersperse i f
+        | Map.size f <= 1 = f
+        | otherwise = cons i f
+    -- concat
+    -- concatMap
+    -- rigidConcatMap
+    -- any
+    -- all
+    -- maximum
+    -- minimum
+    replicate = genericReplicate
+    take n = Map.fromAscList . L.take n . Map.toAscList
+    drop n = Map.fromAscList . L.drop n . Map.toAscList
+    splitAt n = l2m . L.splitAt n . Map.toList
+    takeWhile f = Map.fromAscList . L.takeWhile f . Map.toAscList
+    dropWhile f = Map.fromAscList . L.dropWhile f . Map.toAscList
+    span f = l2m . L.span f . Map.toList
+    break f = span (not . f)
+    group m
+        | null m = empty
+        | otherwise = cons (singleton (head m)) (group (tail m))
+    -- group
+    -- inits
+    -- tails
+    isPrefixOf f1 f2 = L.isPrefixOf (Map.toList f1) (Map.toList f2)
+    isSuffixOf f1 f2 = L.isSuffixOf (Map.toList f1) (Map.toList f2)
+    isInfixOf = Map.isSubmapOf
+    --elem = Map.member
+    --notElem = Map.notMember
+    -- find
+    filter f m = Map.filterWithKey func m
+            where func k v = f (k, v)
+    index = flip Map.elemAt
+    elemIndex (k, v) m =
+        case Map.lookupIndex k m of
+             Nothing -> fail "elemIndex: no matching key"
+             Just i -> if snd (Map.elemAt i m) == v
+                           then Just i
+                           else fail "elemIndex on Map: matched key but not value"
+    elemIndices i m = 
+        case elemIndex i m of
+             Nothing -> empty
+             Just x -> singleton x
+    -- findIndex
+    -- findIndices
+    -- sequence
+    -- mapM
+    -- rigidMapM
+    -- mapM_
+    nub = id
+    delete (k, v) m =
+        case Map.lookup k m of
+             Nothing -> m
+             Just x -> if x == v
+                          then Map.delete k m
+                          else m
+    union = Map.union
+    -- intersect
+    sort = id
+    insert = cons
+    toList = Map.toList
+    fromList = Map.fromList
+    nubBy func = Map.fromAscList . L.nubBy func . Map.toAscList
+    --deleteBy
+    deleteFirstsBy func m1 m2 = Map.fromAscList $ 
+                                L.deleteFirstsBy func (Map.toAscList m1)
+                                                 (Map.toAscList m2)
+    --deleteFirstsBy
+    unionBy func m1 m2 = Map.fromList $ 
+                            L.unionBy func (Map.toList m1) (Map.toList m2)
+    --intersectBy
+    --groupBy
+    sortBy _ = id
+    insertBy _ = insert
+    genericLength = fromIntegral . Map.size
+    genericTake n = Map.fromAscList . L.genericTake n . Map.toAscList
+    genericDrop n = Map.fromAscList . L.genericDrop n . Map.toAscList
+    genericSplitAt n = l2m . L.genericSplitAt n . Map.toList
+    genericReplicate count item
+        | count <= 0 = empty
+        | otherwise = singleton item
+
+--------------------------------------------------
+-- Arrays
+
+instance (Ix i) => FoldableLL (A.Array i e) e where
+    foldl = F.foldl
+    foldl1 = F.foldl1
+    foldl' = F.foldl'
+    foldr = F.foldr
+    foldr1 = F.foldr1
+    foldr' = F.foldr'
+
+instance (Integral i, Ix i) => Monoid (A.Array i e) where
+    mempty = A.listArray (0, -1) []
+    mappend l1 l2 =
+        A.array (blow, newbhigh)
+              (A.assocs l1 ++ zip [(bhigh + 1)..newbhigh] (A.elems l2))
+        where newlen = genericLength newelems
+              newelems = A.elems l2
+              newbhigh = bhigh + newlen
+              (blow, bhigh) = A.bounds l1
+
+instance (Integral i, Ix i) => ListLike (A.Array i e) e where
+    empty = mempty
+    singleton i = A.listArray (0, 0) [i]
+    cons i l = 
+        -- To add something to the beginning of an array, we must
+        -- change the bounds and set the first element.
+        (A.ixmap (blow - 1, bhigh) id l) // [(blow - 1, i)]
+        where (blow, bhigh) = A.bounds l
+    snoc l i = 
+        -- Here we must change the bounds and set the last element
+        (A.ixmap (blow, bhigh + 1) id l) // [(bhigh + 1, i)]
+        where (blow, bhigh) = A.bounds l
+    append = mappend
+    head l = l ! (fst (A.bounds l))
+    last l = l ! (snd (A.bounds l))
+    tail l = A.array (blow + 1, bhigh) (tail (A.assocs l))
+            where (blow, bhigh) = A.bounds l
+    init l = A.array (blow, bhigh - 1) (init (A.assocs l))
+            where (blow, bhigh) = A.bounds l
+    null l = genericLength l == (0::Integer)
+    length = genericLength
+    -- map
+    rigidMap = A.amap
+    reverse l = A.listArray (A.bounds l) (L.reverse (A.elems l)) 
+    -- intersperse
+    -- concat
+    -- concatMap
+    -- rigidConcatMap
+    any x = L.any x . A.elems
+    all x = L.all x . A.elems
+    maximum = L.maximum . A.elems
+    minimum = L.minimum . A.elems
+    replicate = genericReplicate
+    take = genericTake
+    drop = genericDrop
+    -- splitAt
+    -- takeWhile
+    -- dropWhile
+    -- span
+    -- break
+    -- group
+    -- inits
+    -- tails
+    isPrefixOf l1 l2 = L.isPrefixOf (toList l1) (toList l2)
+    isSuffixOf l1 l2 = L.isSuffixOf (toList l1) (toList l2)
+    isInfixOf l1 l2 = L.isInfixOf (toList l1) (toList l2)
+    elem i l = L.elem i (toList l)
+    -- notElem
+    filter f = fromList . L.filter f . toList
+    -- partition
+    index l i = l ! ((fromIntegral i) + offset)
+        where offset = (fst $ A.bounds l)
+    elemIndex i = L.elemIndex i . toList
+    elemIndices i = fromList . L.elemIndices i . toList
+    findIndex f = L.findIndex f . toList
+    findIndices f = fromList . L.findIndices f . toList
+    -- sequence = M.sequence . toList
+    -- mapM f = M.mapM f . toList
+    -- rigidMapM = mapM
+    -- mapM_ f = M.mapM_ f . toList
+    nub = fromList . L.nub . toList
+    -- delete
+    -- deleteFirsts
+    -- union
+    -- intersect
+    sort l = A.listArray (A.bounds l) (L.sort (A.elems l))
+    -- insert
+    toList = A.elems
+    fromList l = A.listArray (0, genericLength l - 1) l
+    -- fromListLike
+    nubBy f = fromList . L.nubBy f . toList
+    -- deleteBy
+    -- deleteFirstsBy
+    -- unionBy
+    -- intersectBy
+    -- groupBy
+    sortBy f l = A.listArray (A.bounds l) (L.sortBy f (A.elems l))
+    -- insertBy
+    genericLength l = fromIntegral (bhigh - blow + 1)
+        where (blow, bhigh) = A.bounds l
+    genericTake count l 
+        | count > genericLength l = l
+        | count <= 0 = empty
+        | otherwise = A.listArray (blow, blow + (fromIntegral count) - 1)
+                          (L.genericTake count (A.elems l))
+        where (blow, _) = A.bounds l
+    genericDrop count l = A.listArray (blow + (fromIntegral count), bhigh)
+                          (L.genericDrop count (A.elems l))
+        where (blow, bhigh) = A.bounds l
+    -- geneicSplitAt
+    genericReplicate count i = A.listArray (0, (fromIntegral count) - 1) 
+                                           (L.genericReplicate count i)
+
+
+instance (Integral i, Ix i) => StringLike (A.Array i Char) where
+    toString = toList
+    fromString = fromList
+    -- lines
+    -- words
+
+instance (Integral i, Ix i) => ListLikeIO (A.Array i Char) Char where
+    hGetLine h = IO.hGetLine h >>= (return . fromList)
+    hGetContents h = IO.hGetContents h >>= (return . fromList)
+    hGet h i = ((hGet h i)::IO String) >>= (return . fromList)
+    hGetNonBlocking h i = ((hGetNonBlocking h i):: IO String) >>= (return . fromList)
+    hPutStr h = hPutStr h . toString
+    hPutStrLn h = hPutStrLn h . toString
+    getLine = IO.getLine >>= (return . fromString)
+    getContents = IO.getContents >>= (return . fromString)
+    putStr = IO.putStr . toString
+    putStrLn = IO.putStrLn . toString
+    -- interact
+    -- readFile
+    -- writeFile
+    -- appendFile
diff --git a/src/Data/ListLike/String.hs b/src/Data/ListLike/String.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/String.hs
@@ -0,0 +1,78 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike.String
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+String-like functions
+
+Written by John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.ListLike.String
+    ( StringLike(..)
+    )
+       where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords)
+import qualified Data.List as L
+import Data.ListLike.Base
+
+{- | An extension to 'ListLike' for those data types that are similar
+to a 'String'.  Minimal complete definition is 'toString' and
+'fromString'. -}
+class StringLike s where
+    {- | Converts the structure to a 'String' -}
+    toString :: s -> String
+    
+    {- | Converts a 'String' to a list -}
+    fromString :: String -> s
+
+    {- | Breaks a string into a list of strings -}
+    lines :: (ListLike full s) => s -> full
+    --lines = map fromString . L.lines . toString 
+    lines = myLines
+
+    {- | Breaks a string into a list of words -}
+    words :: ListLike full s => s -> full
+    words = myWords
+
+    {- | Joins lines -}
+    unlines :: ListLike full s => full -> s
+    unlines = myUnlines
+
+    {- | Joins words -}
+    unwords :: ListLike full s => full -> s
+    unwords = myUnwords
+
+-- For some reason, Hugs required splitting these out into
+-- separate functions.
+myLines :: (StringLike s, ListLike full s) => s -> full
+myLines = map fromString . L.lines . toString
+
+myWords :: (StringLike s, ListLike full s) => s -> full
+myWords = map fromString . L.words . toString
+
+myUnlines :: (StringLike s, ListLike full s) => full -> s
+myUnlines = fromString . L.unlines . map toString
+
+myUnwords :: (StringLike s, ListLike full s) => full -> s
+myUnwords = fromString . L.unwords . map toString
diff --git a/src/Data/ListLike/Utils.hs b/src/Data/ListLike/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ListLike/Utils.hs
@@ -0,0 +1,63 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+{- |
+   Module     : Data.ListLike.Utils
+   Copyright  : Copyright (C) 2007 John Goerzen
+   License    : LGPL
+
+   Maintainer : John Goerzen <jgoerzen@complete.org>
+   Stability  : provisional
+   Portability: portable
+
+Utilities for 'Data.ListLike.ListLike' and friends.  More functions
+similar to 'Data.List' but not part of the main typeclass.
+
+Written by John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.ListLike.Utils
+    (and, or, sum, product, zip, zipWith, unzip, sequence_
+    ) where
+import Prelude hiding (length, head, last, null, tail, map, filter, concat, 
+                       any, lookup, init, all, foldl, foldr, foldl1, foldr1,
+                       maximum, minimum, iterate, span, break, takeWhile,
+                       dropWhile, reverse, zip, zipWith, sequence,
+                       sequence_, mapM, mapM_, concatMap, and, or, sum,
+                       product, repeat, replicate, cycle, take, drop,
+                       splitAt, elem, notElem, unzip, lines, words,
+                       unlines, unwords)
+import Data.ListLike.Base
+import Data.ListLike.FoldableLL
+import Data.Monoid
+
+-- | Returns True if all elements are True
+and :: ListLike full Bool => full -> Bool
+and = all (== True)
+
+-- | Returns True if any element is True
+or :: ListLike full Bool => full -> Bool
+or = any (== True)
+
+-- | The sum of the list
+sum :: (Num a, ListLike full a) => full -> a
+sum = getSum . foldMap Sum
+
+-- | The product of the list
+product :: (Num a, ListLike full a) => full -> a
+product = getProduct . foldMap Product
+
+------------------------------ Zipping
+-- zip, zipWith  in Base
+{- | Converts a list of pairs into two separate lists of elements -}
+unzip :: (ListLike full (itema, itemb),
+          ListLike ra itema,
+          ListLike rb itemb) => full -> (ra, rb)
+unzip inp = foldr convert (empty, empty) inp
+    where convert (a, b) (as, bs) = ((cons a as), (cons b bs))
diff --git a/testsrc/runtests.hs b/testsrc/runtests.hs
new file mode 100644
--- /dev/null
+++ b/testsrc/runtests.hs
@@ -0,0 +1,344 @@
+{-
+Copyright (C) 2007 John Goerzen <jgoerzen@complete.org>
+
+All rights reserved.
+
+For license and copyright information, see the file COPYRIGHT
+
+-}
+
+import Test.QuickCheck
+import Test.QuickCheck.Batch
+import qualified Data.ByteString as BS
+import qualified Data.Array as A
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.ListLike as LL
+import qualified Data.Map as Map
+import qualified Data.Array as A
+import qualified Data.Foldable as F
+import System.Random
+import qualified Test.HUnit as HU
+import System.IO
+import Text.Printf
+import Data.Word
+import Data.List
+import Data.Monoid
+import TestInfrastructure
+import Data.Foldable(foldr', fold, foldMap)
+import System.Info
+
+
+-- prop_singleton :: (Eq i,LL.ListLike f i) => f -> i -> Bool
+--prop_singleton :: (Eq i, LL.ListLike f i, Arbitrary f, Show f, Show i, Arbitrary i) => f -> i -> Bool
+prop_singleton f x = (LL.toList $ asTypeOf (LL.singleton x) f) == [x]
+
+prop_empty f = (LL.toList l == []) && (LL.null l) && (LL.length l == 0)
+    where l = asTypeOf LL.empty f
+
+prop_tofromlist f = 
+    LL.toList f == l && 
+    LL.length f == length l &&
+    f == (LL.fromList . LL.toList $ f)
+    where l = LL.toList f
+
+prop_length f = LL.length f == length (LL.toList f)
+prop_cons f i = llcmp (LL.cons i f) (i : (LL.toList f))
+prop_append f1 f2 = llcmp (LL.append f1 f2) (LL.toList f1 ++ LL.toList f2)
+prop_head f = not (LL.null f) ==> LL.head f == head (LL.toList f)
+prop_last f = not (LL.null f) ==> LL.last f == last (LL.toList f)
+prop_tail f = not (LL.null f) ==> llcmp (LL.tail f) (tail (LL.toList f))
+prop_init f = not (LL.null f) ==> llcmp (LL.init f) (init (LL.toList f))
+prop_null f = LL.null f == null (LL.toList f)
+prop_length2 f = checkLengths f (LL.toList f)
+prop_length3 f1 f2 = llcmp (LL.append f1 f2) (LL.toList f1 ++ LL.toList f2)
+
+prop_map :: forall full item. (TestLL full item, TestLL [item] item) => full -> (item -> item) -> Result
+prop_map f func = llcmp llmap (map func (LL.toList f))
+    where llmap = asTypeOf (LL.map func f) (LL.toList f)
+
+prop_rigidMap f func = llcmp (LL.rigidMap func f) (map func (LL.toList f))
+prop_reverse f = llcmp (LL.reverse f) (reverse (LL.toList f))
+prop_intersperse f i = llcmp (LL.intersperse i f) (intersperse i (LL.toList f))
+
+prop_concat f = 
+    llcmp (LL.concat f) (concat $ map LL.toList (LL.toList f))
+
+prop_concatmap :: forall full item. (TestLL full item, TestLL [item] item) => full -> (item -> [item]) -> Result
+prop_concatmap f func =
+    llcmp (LL.concatMap func f)
+          (concatMap func (LL.toList f))
+
+prop_rigidConcatMap f func =
+    llcmp (LL.rigidConcatMap func f)
+          (concatMap (LL.toList . func) (LL.toList f))
+
+prop_any f func = (LL.any func f) @?= (any func (LL.toList f))
+prop_all f func = (LL.all func f) @?= (all func (LL.toList f))
+prop_maximum f = not (LL.null f) ==> LL.maximum f @=? maximum (LL.toList f)
+prop_minimum f = not (LL.null f) ==> LL.minimum f @=? minimum (LL.toList f)
+prop_replicate f count i = 
+    llcmp res (replicate count i)
+    where res = asTypeOf (LL.replicate count i) f
+prop_take f count = llcmp (LL.take count f) (take count (LL.toList f))
+prop_drop f count = count >= 0 ==> llcmp (LL.drop count f) (drop count (LL.toList f))
+prop_splitAt f count = count >= 0 ==>
+    llcmp [(\(x, y) -> (LL.toList x, LL.toList y)) . LL.splitAt count $ f] 
+          [LL.splitAt count (LL.toList f)]
+prop_takeWhile f func = llcmp (LL.takeWhile func f) 
+                              (takeWhile func (LL.toList f))
+prop_dropWhile f func = llcmp (LL.dropWhile func f) 
+                              (dropWhile func (LL.toList f))
+prop_span f func = 
+    llcmp [(\(x, y) -> (LL.toList x, LL.toList y)) . LL.span func $ f]
+          [span func (LL.toList f)]
+prop_break f func = 
+    llcmp [(\(x, y) -> (LL.toList x, LL.toList y)) . LL.break func $ f]
+          [break func (LL.toList f)]
+prop_group f =
+    -- llcmp (map LL.toList (LL.group f)) (group (LL.toList f))
+    (map LL.toList (LL.group f)) @?= (group (LL.toList f))
+prop_inits f = (map LL.toList (LL.inits f)) @?= (inits (LL.toList f))
+prop_tails f = (map LL.toList (LL.tails f)) @?= (tails (LL.toList f))
+prop_isPrefixOf f1 f2 = LL.isPrefixOf f1 f2 @?= 
+    (isPrefixOf (LL.toList f1) (LL.toList f2))
+prop_isSuffixOf f1 f2 = LL.isSuffixOf f1 f2 @?=
+    (isSuffixOf (LL.toList f1) (LL.toList f2))
+prop_isInfixOf f1 f2 = LL.isInfixOf f1 f2 @?=
+    (isInfixOf (LL.toList f1) (LL.toList f2))
+prop_elem f i = LL.elem i f @?= elem i (LL.toList f)
+prop_notElem f i = LL.notElem i f @?= notElem i (LL.toList f)
+prop_find f func = LL.find func f @?= find func (LL.toList f)
+prop_filter f func = llcmp (LL.filter func f) (filter func (LL.toList f))
+prop_partition f func = 
+    (LL.toList f1, LL.toList f2) @?= partition func (LL.toList f)
+    where (f1, f2) = LL.partition func f
+prop_index f i = (i >= 0 && i < LL.length f) ==>
+    (LL.index f i @?= ((LL.toList f) !! i))
+prop_elemIndex f i = LL.elemIndex i f @?= elemIndex i (LL.toList f)
+prop_elemIndices f i = LL.elemIndices i f @?= elemIndices i (LL.toList f)
+prop_findIndex f func = LL.findIndex func f @?= findIndex func (LL.toList f)
+prop_findIndices f func =
+    LL.findIndices func f @?= findIndices func (LL.toList f)
+
+prop_sequence f =
+    case (llres, sequence testit) of
+         (Just ll, Just l) -> llcmp ll l
+         _ -> error "Error!"
+    where testit = map Just (LL.toList f)
+          llres = asTypeOf (LL.sequence testit) (Just f)
+
+prop_mapM :: forall full item. (TestLL full item, TestLL [item] item) => full -> (item -> Maybe item) -> Result
+prop_mapM f func = llmapM @?= (mapM func (LL.toList f))
+    where llmapM = asTypeOf (LL.mapM func f) (Just (LL.toList f))
+
+prop_rigidMapM :: forall full item. (TestLL full item, TestLL [item] item) => full -> (item -> Maybe item) -> Result
+prop_rigidMapM f func = 
+    case (LL.rigidMapM func f, mapM func (LL.toList f)) of
+         (Just ll, Just l) -> llcmp ll l
+         _ -> error "error in prop_rigidMapM"
+
+-- FIXME: can we test mapM_?
+
+prop_nub f = llcmp (LL.nub f) (nub (LL.toList f))
+prop_delete f i = llcmp (LL.delete i f) (delete i (LL.toList f))
+prop_deleteFirsts f1 f2 = llcmp (LL.deleteFirsts f1 f2) 
+    ((LL.toList f1) \\ (LL.toList f2))
+prop_union f1 f2 = llcmp (LL.union f1 f2) 
+    (union (LL.toList f1) (LL.toList f2))
+prop_intersect f1 f2 = llcmp (LL.intersect f1 f2) 
+    (intersect (LL.toList f1) (LL.toList f2))
+prop_sort f1 = llcmp (LL.sort f1) (sort (LL.toList f1))
+prop_insert f i = llcmp (LL.insert i f) (insert i (LL.toList f))
+prop_nubBy f func = llcmp (LL.nubBy func f) (nubBy func (LL.toList f))
+prop_deleteBy f func i = llcmp (LL.deleteBy func i f) 
+                         (deleteBy func i (LL.toList f))
+prop_deleteFirstsBy f1 f2 func = llcmp (LL.deleteFirstsBy func f1 f2)
+    (deleteFirstsBy func (LL.toList f1) (LL.toList f2))
+prop_unionBy f1 f2 func = llcmp (LL.unionBy func f1 f2)
+    (unionBy func (LL.toList f1) (LL.toList f2))
+prop_intersectBy f1 f2 func = llcmp (LL.intersectBy func f1 f2)
+    (intersectBy func (LL.toList f1) (LL.toList f2))
+prop_groupBy f func =
+    (map LL.toList (LL.groupBy func f)) @?= (groupBy func (LL.toList f))
+prop_sortBy1 f = llcmp (LL.sortBy compare f) (sortBy compare (LL.toList f))
+prop_sortBy2 f = llcmp (LL.sortBy func f) (sortBy func (LL.toList f))
+    where func x y = compare y x
+prop_sortBy f func = llcmp (LL.sortBy func f) (sortBy func (LL.toList f))
+prop_insertBy1 f i = llcmp (LL.insertBy compare i f)
+    (insertBy compare i (LL.toList f))
+prop_insertBy2 f i = llcmp (LL.insertBy func i f)
+    (insertBy func i (LL.toList f))
+    where func x y = compare y x
+prop_genericLength f =
+    LL.genericLength f @?= genericLength (LL.toList f)
+prop_genericTake f (i::Integer) = (i >= 0) ==>
+    llcmp (LL.genericTake i f) (genericTake i (LL.toList f))
+prop_genericDrop f (i::Integer) = (i >= 0) ==>
+    llcmp (LL.genericDrop i f) (genericDrop i (LL.toList f))
+prop_genericSplitAt f (i::Integer) = i >= 0 ==>
+    llcmp [(\(x, y) -> (LL.toList x, LL.toList y)) . LL.genericSplitAt i $ f]
+          [LL.genericSplitAt i (LL.toList f)]
+prop_genericReplicate f (count::Integer) i = count >= 0 ==>
+    llcmp res (genericReplicate count i)
+    where res = asTypeOf (LL.genericReplicate count i) f
+
+--prop_zip :: (LL.ListLike full item, LL.ListLike result (item, Int)) =>
+--    full -> Result
+prop_zip f = LL.zip f f2 @?= zip (LL.toList f) f2
+    where f2 = [(-5::Int)..]
+prop_zipWith f = 
+    LL.toList res @?= (zipWith func (LL.toList f) f2)
+    where f2 = [(100::Int)..(-100)]
+          func x y = (y + 5, x)
+          res = asTypeOf (LL.zipWith func f f2) [(5::Int, LL.head f)]
+--FIXME: prop_unzip 
+--FIXME: prop_and
+--FIXME: prop_or
+--FIXME: prop_sum
+--FIXME: prop_product
+prop_foldl f func (i::Int) = LL.foldl func i f @?= foldl func i (LL.toList f)
+prop_foldl' f func (i::Integer) =
+    LL.foldl' func i f @?= foldl' func i (LL.toList f)
+prop_foldl1 f func = not (LL.null f) ==> 
+    (LL.foldl1 func f) @?= (foldl1 func (LL.toList f))
+prop_foldr f func (i::Int) = LL.foldr func i f @?= foldr func i (LL.toList f)
+prop_foldr' f func (i::Integer) =
+    LL.foldr' func i f @?= foldr' func i (LL.toList f)
+prop_foldr1 f func = not (LL.null f) ==>
+    LL.foldl1 func f @?= foldl1 func (LL.toList f)
+prop_fold f = llcmp res resl
+    where res = LL.fold f
+          resl = fold (map LL.toList (LL.toList f))
+prop_foldMap :: (LL.ListLike full item, Eq full) => full -> (item -> [Int]) -> Result
+prop_foldMap f func = res @?= resl
+    where res = LL.foldMap func f
+          resl = foldMap func  (LL.toList f) -- asTypeOf (foldMap (LL.toList f)) (head f)
+
+prop_toString f =
+    ((LL.fromString . LL.toString $ f) == f)
+    where l = LL.toList f
+prop_fromString f x = 
+    LL.toString (asTypeOf (LL.fromString x) f) @?= x
+prop_lines f = map LL.toString res @?= lines (LL.toString f)
+    where res = asTypeOf (LL.lines f) [f]
+prop_words f = map LL.toString res @?= words (LL.toString f)
+    where res = asTypeOf (LL.words f) [f]
+
+allt = [apf "empty" (t prop_empty),
+        apf "length" (t prop_length),
+        apf "to/fromList" (t prop_tofromlist),
+        apf "singleton" (t prop_singleton),
+        apf "cons" (t prop_cons),
+        apf "append" (t prop_append),
+        apf "head" (t prop_head),
+        apf "last" (t prop_last),
+        apf "tail" (t prop_tail),
+        apf "init" (t prop_init),
+        apf "null" (t prop_null),
+        apf "length2" (t prop_length2),
+        apf "length3" (t prop_length3),
+        apf "map" (t prop_map),
+        apf "rigidMap" (t prop_rigidMap),
+        apf "reverse" (t prop_reverse),
+        apf "intersperse" (t prop_intersperse),
+        apw "concat" (LLWrap prop_concat),
+        apf "concatMap" (t prop_concatmap),
+        apf "rigidConcatMap" (t prop_rigidConcatMap),
+        apf "any" (t prop_any),
+        apf "all" (t prop_all),
+        apf "maximum" (t prop_maximum),
+        apf "minimum" (t prop_minimum),
+        apf "replicate" (t prop_replicate),
+        apf "take" (t prop_take),
+        apf "drop" (t prop_drop),
+        apf "splitAt" (t prop_splitAt),
+        apf "takeWhile" (t prop_takeWhile),
+        apf "dropWhile" (t prop_dropWhile),
+        apf "span" (t prop_span),
+        apf "break" (t prop_break),
+        apf "group" (t prop_group),
+        apf "inits" (t prop_inits),
+        apf "tails" (t prop_tails),
+        apf "isPrefixOf" (t prop_isPrefixOf),
+        apf "isSuffixOf" (t prop_isSuffixOf),
+        apf "isInfixOf" (t prop_isInfixOf),
+        apf "elem" (t prop_elem),
+        apf "notElem" (t prop_notElem),
+        apf "find" (t prop_find),
+        apf "filter" (t prop_filter),
+        apf "partition" (t prop_partition),
+        apf "index" (t prop_index),
+        apf "elemIndex" (t prop_elemIndex),
+        apf "elemIndices" (t prop_elemIndices),
+        apf "findIndex" (t prop_findIndex),
+        apf "findIndices" (t prop_findIndices),
+        apf "sequence" (t prop_sequence),
+        apf "mapM" (t prop_mapM),
+        apf "rigidMapM" (t prop_rigidMapM),
+        -- FIXME: mapM_ ?
+        apf "nub" (t prop_nub),
+        apf "delete" (t prop_delete),
+        apf "deleteFirsts" (t prop_deleteFirsts),
+        apf "union" (t prop_union),
+        apf "intersect" (t prop_intersect),
+        apf "sort" (t prop_sort),
+        apf "insert" (t prop_insert),
+        -- toList
+        -- fromList
+        -- fromListLike
+        apf "nubBy" (t prop_nubBy),
+        apf "deleteBy" (t prop_deleteBy),
+        apf "deleteFirstsBy" (t prop_deleteFirstsBy),
+        apf "unionBy" (t prop_unionBy),
+        apf "intersectBy" (t prop_intersectBy),
+        apf "groupBy" (t prop_groupBy),
+        apf "sortBy1" (t prop_sortBy1),
+        apf "sortBy2" (t prop_sortBy2),
+        apf "insertBy1" (t prop_insertBy1),
+        apf "insertBy2" (t prop_insertBy2),
+        apf "genericLength" (t prop_genericLength),
+        apf "genericTake" (t prop_genericTake),
+        apf "genericDrop" (t prop_genericDrop),
+        apf "genericSplitAt" (t prop_genericSplitAt),
+        apf "genericReplicate" (t prop_genericReplicate),
+        apf "zip" (t prop_zip),
+        apf "zipWith" (t prop_zipWith) 
+        -- apf "unzip" (t prop_unzip),
+        -- apf "and" (t prop_and),
+        -- apf "or" (t prop_or),
+        -- apf "sum" (t prop_sum),
+        -- apf "propduct" (t prop_product),
+        -- sequence_ 
+        ]
+
+allf = (if compilerName == "hugs" then [] else [ apf "foldl" (t prop_foldl),
+                                                 apf "foldr1" (t prop_foldr1),
+                                                 apf "foldl1" (t prop_foldl1)])
+       ++
+       [
+        apf "foldl'" (t prop_foldl'),
+        apf "foldr" (t prop_foldr),
+        apf "foldr'" (t prop_foldr'),
+        apw "fold" (LLWrap prop_fold),
+        apf "foldMap" (t prop_foldMap) 
+       ]
+
+alls = [
+        aps "toString" (t prop_toString),
+        aps "fromString" (t prop_fromString),
+        aps "lines" (t prop_lines),
+        aps "words" (t prop_words) 
+        -- FIXME: aps (t prop_unlines),
+        -- FIXME: aps (t prop_unwords)
+       ]
+allTests = HU.TestList $ reverse $
+                       [HU.TestLabel "ListLike" (HU.TestList allt),
+                        HU.TestLabel "FoldableLL" (HU.TestList allf),
+                        HU.TestLabel "StringLike" (HU.TestList alls)]
+
+testh = HU.runTestTT $ allTests
+testv = runVerbTestText (HU.putTextToHandle stderr True) $ allTests
+         
+main = 
+    do testv
+       return ()
