diff --git a/Data/XML/Types.hs b/Data/XML/Types.hs
new file mode 100644
--- /dev/null
+++ b/Data/XML/Types.hs
@@ -0,0 +1,184 @@
+-- Copyright (c) 2010 John Millikin
+--
+-- 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.
+
+module Data.XML.Types
+	( -- * Types
+	  -- ** Document prologue
+	  Document (..)
+	, Prologue (..)
+	, Instruction (..)
+	, Miscellaneous (..)
+	
+	-- ** Document body
+	, Node (..)
+	, Element (..)
+	, Attribute (..)
+	, Content (..)
+	, Name (..)
+	, Named (..)
+	
+	-- ** Doctypes
+	, Doctype (..)
+	, ExternalID (..)
+	, InternalSubset
+	
+	-- * Predicates
+	, isElement
+	, isInstruction
+	, isContent
+	, isComment
+	, isNamed
+	
+	-- * Filters
+	, nodeChildren
+	, elementChildren
+	, hasAttribute
+	, position
+	) where
+import Control.Monad ((>=>))
+import Data.Text.Lazy (Text)
+
+data Document = Document
+	{ documentPrologue :: Prologue
+	, documentRoot :: Element
+	, documentEpilogue :: [Miscellaneous]
+	}
+	deriving (Show, Eq)
+
+data Prologue = Prologue
+	{ prologueBefore :: [Miscellaneous]
+	, prologueDoctype :: Maybe Doctype
+	, prologueAfter :: [Miscellaneous]
+	}
+	deriving (Show, Eq)
+
+data Instruction = Instruction
+	{ instructionTarget :: Text
+	, instructionData :: Text
+	}
+	deriving (Show, Eq)
+
+data Miscellaneous
+	= MiscInstruction Instruction
+	| MiscComment Text
+	deriving (Show, Eq)
+
+data Node
+	= NodeElement Element
+	| NodeInstruction Instruction
+	| NodeContent Content
+	| NodeComment Text
+	deriving (Show, Eq)
+
+data Element = Element
+	{ elementName :: Name
+	, elementAttributes :: [Attribute]
+	, elementNodes :: [Node]
+	}
+	deriving (Show, Eq)
+
+data Attribute = Attribute
+	{ attributeName :: Name
+	, attributeContent :: [Content]
+	}
+	deriving (Show, Eq)
+
+data Content
+	= ContentText Text
+	| ContentEntity Text
+	deriving (Show, Eq)
+
+data Name = Name
+	{ nameLocalName :: Text
+	, nameNamespace :: Maybe Text
+	, namePrefix :: Maybe Text
+	}
+	deriving (Show)
+
+-- Ignore prefixes when comparing names
+instance Eq Name where
+	x == y = and
+		[ nameLocalName x == nameLocalName y
+		, nameNamespace x == nameNamespace y
+		]
+
+class Named a where
+	getName :: a -> Name
+
+instance Named Element where
+	getName = elementName
+
+instance Named Attribute where
+	getName = attributeName
+
+data Doctype = Doctype
+	{ doctypeName :: Text
+	, doctypeExternalID :: Maybe ExternalID
+	, doctypeInternalSubsets :: [InternalSubset]
+	}
+	deriving (Show, Eq)
+
+data ExternalID
+	= SystemID Text
+	| PublicID Text Text
+	deriving (Show, Eq)
+
+data InternalSubset = InternalSubset
+	-- TODO
+	deriving (Show, Eq)
+
+isElement :: Node -> [Element]
+isElement (NodeElement e) = [e]
+isElement _ = []
+
+isInstruction :: Node -> [Instruction]
+isInstruction (NodeInstruction i) = [i]
+isInstruction _ = []
+
+isContent :: Node -> [Content]
+isContent (NodeContent c) = [c]
+isContent _ = []
+
+isComment :: Node -> [Text]
+isComment (NodeComment t) = [t]
+isComment _ = []
+
+isNamed :: Named a => Name -> a -> [a]
+isNamed n x = [x | getName x == n]
+
+elementChildren :: Element -> [Element]
+elementChildren = elementNodes >=> isElement
+
+nodeChildren :: Node -> [Node]
+nodeChildren = isElement >=> elementNodes
+
+position :: Integer -> (a -> [b]) -> a -> [b]
+position n f e = safeHead n (f e)
+
+safeHead :: Integer -> [a] -> [a]
+safeHead _ [] = []
+safeHead 0 (x:_) = [x]
+safeHead n (_:xs) = safeHead (n - 1) xs
+
+hasAttribute :: (Attribute -> [Attribute]) -> Element -> [Element]
+hasAttribute f e = [e | not $ null (elementAttributes e >>= f)]
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/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2010 John Millikin
+
+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/xml-types.cabal b/xml-types.cabal
new file mode 100644
--- /dev/null
+++ b/xml-types.cabal
@@ -0,0 +1,26 @@
+name: xml-types
+version: 0.1
+synopsis: Basic types for representing XML
+license: MIT
+license-file: license.txt
+author: John Millikin <jmillikin@gmail.com>
+maintainer: jmillikin@gmail.com
+build-type: Simple
+cabal-version: >=1.6
+category: Text, XML
+stability: experimental
+bug-reports: mailto:jmillikin@gmail.com
+
+source-repository head
+  type: darcs
+  location: http://ianen.org/haskell/xml-types/
+
+library
+  ghc-options: -Wall -fno-warn-unused-do-bind
+
+  build-depends:
+      base >=3 && < 5
+    , text
+
+  exposed-modules:
+    Data.XML.Types
