diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2017 Clinton Mead
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/heterolist.cabal b/heterolist.cabal
new file mode 100644
--- /dev/null
+++ b/heterolist.cabal
@@ -0,0 +1,38 @@
+name:                 heterolist
+version:              0.2.0.0
+synopsis:             A heterogeneous list type
+description:
+  A heterogeneous list type (i.e. a list with different element types) which knows the type of it's elements.
+  There are also various functions for manipulating such lists, although this is not yet complete.
+  Some of the standard type classes, like Eq, Ord and Show are defined for these heterogeneous lists.
+
+  I suspect it could be particularly useful for testing libraries which may want to test against a variety of
+  types in a uniform fashion.
+license: MIT
+license-file: LICENSE
+copyright: Clinton Mead (2017)
+author:               Clinton Mead
+maintainer:           clintonmead@gmail.com
+category:             Data
+build-type:           Simple
+cabal-version:        >=1.10
+tested-with: GHC == 8.0.2
+bug-reports: https://github.com/clintonmead/heterolist/issues
+
+source-repository head
+  type: git
+  location: https://github.com/clintonmead/heterolist.git
+
+library
+  exposed-modules:      Data.HeteroList
+  build-depends:        base == 4.9.*, indextype == 0.2.*, constraint-manip == 0.1.*, polydata == 0.1.*
+  hs-source-dirs:       src
+  default-language:     Haskell2010
+
+Test-Suite tests
+  type: exitcode-stdio-1.0
+  main-is: Tests.hs
+  other-modules: Data.HeteroList
+  build-depends:       base == 4.9.*, indextype == 0.2.*, constraint-manip == 0.1.*, polydata == 0.1.*, hspec == 2.4.*
+  hs-source-dirs:       test, src
+  default-language:     Haskell2010
diff --git a/src/Data/HeteroList.hs b/src/Data/HeteroList.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/HeteroList.hs
@@ -0,0 +1,336 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
+
+{-|
+This module provides heterogenuous lists. There are a few functions on these lists which are an attempt
+to be parallels of the 'Data.Functor.Functor', 'Control.Applicative.Applicative' and 'Control.Monad.Monad' classes,
+although these are not part of type classes, and certainly not the above classes because the types don't match.
+
+I believe this sort of package has potential in testing generic libraries.
+
+For example, lets say you have @(n)@ sets of test data.
+
+And @(m)@ different functions that work on that data.
+
+And @(m)@ different "check functions" (perhaps just the functions above but one release back as a control).
+
+Making a complete test case for all this is tricky, because you may be testing different types and the functions you're
+testing on them may have different results.
+
+You can sometimes get situations where you just have to write @(n * m)@ test cases.
+
+This approach hopefully makes the work @(n + m)@ instead.
+
+It probably needs some work but I have included a functional example in the documentation for 'hap'.
+-}
+module Data.HeteroList (
+  HeteroList((:-), Nil),
+  (++), happend,
+  HeteroListConstraint,
+  IsHeteroList,
+  GetHead, GetTail,
+  GetHeteroListParam,
+  hconcat,
+  hmap,
+  hconcatMap,
+  HeteroMapConstraint,
+  hmapfl,
+  hap,
+  ConcatT,
+  toList,
+  IsHomogeneousList,
+  length,
+  ListLength
+)
+where
+
+import GHC.Exts (Constraint)
+import Data.Poly (Poly(Poly), IsPoly, GetPolyConstraint)
+--import Data.Poly.Functor (PolyFunctor, hmap, PolyFunctorConstraint)
+
+import Data.Proxy (Proxy(Proxy))
+import Control.IndexT.Constructor (IsData)
+
+import Control.IndexT.Function (IsFunc,ResultT)
+import Control.IndexT (IndexT)
+
+import Data.Poly.Function (Equal)
+
+import GHC.TypeLits (Nat, type (+))
+
+import Prelude (
+  Show, show,
+  Eq, (==), Bool(True, False), (&&),
+  Ord, compare, Ordering(GT, EQ, LT),
+  Int, (+),
+  seq
+  )
+import qualified Prelude
+
+import Data.Foldable (Foldable)
+
+data ShowRest a = ShowRest a
+
+instance Show (HeteroList '[]) where
+  show Nil = "[]"
+
+instance (Show a, Show (ShowRest (HeteroList as))) => Show (HeteroList (a:as)) where
+  show (x :- xs) = "[" Prelude.++ show x Prelude.++ (show (ShowRest xs)) Prelude.++ "]"
+
+instance (Show a, Show (ShowRest (HeteroList as))) => Show (ShowRest (HeteroList (a:as))) where
+  show (ShowRest (x :- xs)) = ", " Prelude.++ show x Prelude.++ show (ShowRest xs)
+
+instance Show (ShowRest (HeteroList '[])) where
+  show (ShowRest Nil) = ""
+{-|
+Heterogeneous list type. Construct like so:
+
+> x = (5 :: Int) :- 'a' :- True :- Nil
+-}
+data HeteroList (l :: [*]) where
+  (:-) :: a -> HeteroList as -> HeteroList (a:as)
+  Nil :: HeteroList '[]
+
+instance (HeteroListConstraint Eq a) => Eq (HeteroList a) where
+  (x :- xs) == (y :- ys) = x == y && xs == ys
+  Nil == Nil = True
+  _ == _ = False
+
+instance (HeteroListConstraint Eq a, HeteroListConstraint Ord a) => Ord (HeteroList a) where
+  compare (x :- xs) (y :- ys) =
+    let r = compare x y in
+      case r of
+        EQ -> compare xs ys
+        _ -> r
+  compare Nil Nil = EQ
+
+infixr 5 :-
+
+type family (a :: [*]) :++ (b :: [*]) where
+  '[] :++ b = b
+  (a:as) :++ b = a:(as :++ b)
+
+{-|
+Append two heterogeneous lists. Of course, as 'HeteroList's know their type, this produces a new type.
+-}
+(++) :: HeteroList a -> HeteroList b -> HeteroList (a :++ b)
+(++) x y = case x of
+  Nil -> y
+  (x :- xs) -> x :- (xs ++ y)
+
+{-|
+Synonym for '(++)'.
+-}
+happend :: HeteroList a -> HeteroList b -> HeteroList (a :++ b)
+happend = (++)
+
+{-|
+Get the type of the first element of a 'HeteroList'
+-}
+type family GetHead a where
+  GetHead (HeteroList (a:_)) = a
+
+{-|
+Get the type of the tail of a 'HeteroList'.
+-}
+type family GetTail a where
+  GetTail (HeteroList (_:as)) = as
+
+type IsHeteroListT a = a ~ HeteroList ((GetHead a):(GetTail a))
+
+{-|
+Constraint which requires the argument to be a 'HeteroList'
+-}
+class (IsHeteroListT a) => IsHeteroList a
+instance (IsHeteroListT a) => IsHeteroList a
+
+{-|
+Gets the types of the 'HeteroList'. Naturally this is a list of types of type @[*]@.
+-}
+type family GetHeteroListParam a where
+  GetHeteroListParam (HeteroList a) = a
+
+-- This is an attempt to define hmap as part of the 'Data.Poly.Functor' type class.
+-- It's causing more trouble than it's worth for the moment so it's commented out, and a non type class
+-- approach is used.
+{-
+type IsPolyFunctor c t = (PolyFunctor t, PolyFunctorConstraint c t)
+
+type instance PolyFunctorConstraint c (HeteroList (a:as) -> HeteroList (b:bs)) = (c (a -> b), PolyFunctorConstraint c (HeteroList as -> HeteroList bs))
+type instance PolyFunctorConstraint c (HeteroList '[] -> HeteroList '[]) = ()
+
+
+instance (IsHeteroList a, PolyFunctor (HeteroList (GetTail a) -> HeteroList bs)) => PolyFunctor (a -> HeteroList (b:bs)) where
+  hmap = hmap'
+
+instance (IsHeteroList b, PolyFunctor (HeteroList as -> HeteroList (GetTail b))) => PolyFunctor (HeteroList (a:as) -> b) where
+  hmap = hmap'
+
+instance {-# OVERLAPPING #-} PolyFunctor (HeteroList as -> HeteroList bs) => PolyFunctor (HeteroList (a:as) -> HeteroList (b:bs)) where
+  hmap = hmap'
+
+hmap' :: (c (a -> b), PolyFunctorConstraint c (HeteroList as -> HeteroList bs), PolyFunctor (HeteroList as -> HeteroList bs)) => Poly c -> HeteroList (a:as) -> HeteroList (b:bs)
+hmap' pf@(Poly f) (x :- xs) = (f x) :- hmap pf xs
+
+instance (a ~ HeteroList '[]) => PolyFunctor (a -> HeteroList '[]) where
+  hmap _ Nil = Nil
+
+instance (b ~ HeteroList '[]) => PolyFunctor (HeteroList '[] -> b) where
+  hmap _ Nil = Nil
+
+instance {-# OVERLAPPING #-} PolyFunctor (HeteroList '[] -> HeteroList '[]) where
+  hmap _ Nil = Nil
+-}
+
+type family HeteroListConstraintT c a :: Constraint where
+  HeteroListConstraintT c (a:as) = (c a, HeteroListConstraintT c as)
+  HeteroListConstraintT _ '[] = ()
+
+{-|
+Applies a constraint @c@ to a type of @HeteroList a@.
+So you want to write this:
+
+> (HeteroListConstraintT c t) => HeteroList t
+
+not
+
+> HeteroListConstraintT c (HeteroList t)  => HeteroList t
+-}
+class HeteroListConstraintT c a => HeteroListConstraint c a
+instance HeteroListConstraintT c a => HeteroListConstraint c a
+
+{-|
+The type of a concatenated list. Note, like 'HeteroListConstraintT' you need to apply this type function to
+the parameter to 'HeteroList'.
+-}
+type family ConcatT a where
+  ConcatT ((HeteroList a):as) = (a :++ ConcatT as)
+  ConcatT '[] = '[]
+
+{-|
+An analogue to 'Prelude.concat'
+-}
+hconcat :: (HeteroListConstraint IsHeteroList a) => HeteroList a -> HeteroList (ConcatT a)
+hconcat (x :- xs) = x ++ hconcat xs
+hconcat Nil = Nil
+
+type family HeadList a where
+  HeadList (a:_) = a
+
+type family TailList a where
+  TailList (_:as) = as
+
+type IsNonEmptyList a = (a ~ ((HeadList a):(TailList a)))
+
+type family HeteroMapConstraintT c a b :: Constraint where
+  HeteroMapConstraintT _ '[] b = (b ~ '[])
+  HeteroMapConstraintT c a b = (IsNonEmptyList a, IsNonEmptyList b, c ((HeadList a) -> (HeadList b)), HeteroMapConstraintT c (TailList a) (TailList b))
+
+{-|
+This constraints @c@, which is intended to be the argument of a 'Poly', to be a constraint which allows one
+to map between the two 'HeteroList's of type @a@ and @b@
+-}
+class HeteroMapConstraintT c a b => HeteroMapConstraint c a b
+instance HeteroMapConstraintT c a b => HeteroMapConstraint c a b
+
+{-|
+The analogue of 'Prelude.map'. Not you'll need to pass a 'Poly' as the function. A more complex example is
+included in the function 'hap'.
+-}
+hmap :: HeteroMapConstraint c a b => Poly c -> HeteroList a -> HeteroList b
+hmap pf@(Poly f) x = case x of
+  (x :- xs) -> (f x) :- (hmap pf xs)
+  Nil -> Nil
+
+{-|
+The analogue of 'Prelude.concatMap', or  '>>=' (\"bind\") from 'Control.Monad.Monad'.
+It's just 'hmap' followed by 'hconcat'.
+-}
+hconcatMap :: forall c a b. (HeteroMapConstraint c a b, HeteroListConstraint IsHeteroList b) => Poly c -> HeteroList a -> HeteroList (ConcatT b)
+hconcatMap f x = hconcat ((hmap f x) :: HeteroList b)
+
+{-|
+Applies every function in the 'HeteroList' of 'Poly's in the first argument to the 'HeteroList' which is the second argument.
+This is much like 'hap' except the result list is not \"flattened\" by 'hconcat'
+-}
+hmapfl :: forall f a b c. (HeteroMapConstraint c f b, c ~ WhatsC a, HeteroListConstraint IsHeteroList b) => HeteroList f -> HeteroList a -> HeteroList b
+hmapfl fs l = hmap g fs where
+  g :: Poly c
+  g = Poly (\f -> hmap f l)
+
+type WhatsCT a t = (IsFunc 1 t, IsPoly (IndexT 0 t), IsHeteroList (ResultT 1 t), HeteroMapConstraint (GetPolyConstraint (IndexT 0 t)) a (GetHeteroListParam (ResultT 1 t)))
+
+class WhatsCT a t => WhatsC a t
+instance WhatsCT a t => WhatsC a t
+
+{-|
+This is the analogue of 'Control.Monad.ap', or 'Control.Applicative.\<*\>. Arguments are the same as 'hmapfl', but the result is flattened.
+
+Here's an example usage:
+
+>>> triple = Poly @((IsHomoFunc 1) &&& ((Arg 0) `IxConstrainBy` Num)) (*3)
+>>> compareZero = Poly @((IsFunc 1) &&& ((Arg 0) `IxConstrainBy` (Num &&& Ord)) &&& ((Result 1) `IxIs` Ordering)) (`compare` 0)
+>>> greaterThanZero = Poly @((IsFunc 1) &&& ((Arg 0) `IxConstrainBy` (Num &&& Ord)) &&& ((Result 1) `IxIs` Bool)) (> 0)
+
+>>> f = triple :- compareZero :- greaterThanZero :- Nil
+>>> x = (42 :: Int) :- (-100 :: Float) :- (22 % 7 :: Rational) :- Nil
+
+>>> f `hap` x
+[126, -300.0, 66 % 7, GT, LT, GT, True, False, True]
+
+Note that
+-}
+hap :: forall f a b c. (HeteroMapConstraint c f b, c ~ WhatsC a, HeteroListConstraint IsHeteroList b) => HeteroList f -> HeteroList a -> HeteroList (ConcatT b)
+hap f x = hconcat ((hmapfl f x) :: HeteroList b)
+
+{-|
+> IsHomogeneousList a t
+
+constraints a 'HeteroList t' to have elements only of type a.
+
+i.e.
+
+> IsHomogeneousList Int t => HeteroList t
+
+means the 'HeteroList' only has elements of type @Int@.
+-}
+class HeteroListConstraint (Equal a) t => IsHomogeneousList a t
+instance HeteroListConstraint (Equal a) t => IsHomogeneousList a t
+
+{-|
+Naturally, if (and only if) a 'HeteroList' is actually homogeneous, we can turn it into an ordinary list.
+-}
+toList :: forall a t. (IsHomogeneousList a t) => HeteroList t -> [a]
+toList (x :- xs) = x:(toList xs)
+toList Nil = []
+
+{-|
+Type level length the paramter of a 'HeteroList'
+-}
+type family ListLength t :: Nat where
+  ListLength '[] = 0
+  ListLength (_:as) = 1 + ListLength as
+
+{-|
+Length of a 'HeteroList'. I'm not sure if this can be done in constant time as the type defines the length,
+but currently it just does the usual traverse the list and count.
+-}
+length :: forall t. HeteroList t -> Int
+length = go 0 where
+  go :: forall t. Int -> HeteroList t -> Int
+  go acc Nil = acc
+  go acc (_ :- xs) = let acc' = acc + 1 in acc' `seq` go acc' xs
diff --git a/test/Tests.hs b/test/Tests.hs
new file mode 100644
--- /dev/null
+++ b/test/Tests.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+module Main where
+
+import Data.Poly
+import Data.Poly.Functor
+import Data.Poly.Function
+import Control.IndexT.Constructor
+import Control.IndexT.Function
+import Control.IndexT
+
+import Control.ConstraintManip
+
+import Data.HeteroList
+
+import Test.Hspec (hspec, it, shouldBe)
+import Data.Ratio ((%), Rational)
+
+triple = Poly @((IsHomoFunc 1) &&& ((Arg 0) `IxConstrainBy` Num)) (*3)
+compareZero = Poly @((IsFunc 1) &&& ((Arg 0) `IxConstrainBy` (Num &&& Ord)) &&& ((Result 1) `IxIs` Ordering)) (`compare` 0)
+greaterThanZero = Poly @((IsFunc 1) &&& ((Arg 0) `IxConstrainBy` (Num &&& Ord)) &&& ((Result 1) `IxIs` Bool)) (> 0)
+
+f = triple :- compareZero :- greaterThanZero :- Nil
+x = (42 :: Int) :- (-100 :: Float) :- (22 % 7 :: Rational) :- Nil
+--f = triple :- Nil
+--x = (3 :: Integer) :- Nil
+main = hspec $ do
+  it "mkPolyFunc1 test" $ (f `hap` x) `shouldBe` (126 :- -300 :- 66 % 7 :- GT :- LT :- GT :- True :- False :- True :- Nil)
+
