diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) Stefan Kersten 2008
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/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/Text/ARFF.hs b/Text/ARFF.hs
new file mode 100644
--- /dev/null
+++ b/Text/ARFF.hs
@@ -0,0 +1,211 @@
+-- | Generate Attribute-Relation File Format (ARFF) files.
+--
+-- ARFF files are used by the WEKA data mining and machine learning framework.
+--
+-- <http://www.cs.waikato.ac.nz/~ml/weka/>
+--
+module Text.ARFF (
+    -- * ARFF relations
+    Relation,
+    relation,
+    encode,
+    -- * Attribute constructors
+    Attribute,
+    a_string,
+    a_real,
+    a_int,
+    a_nominal,
+    a_nominalFromTo,
+    a_dateFormat,
+    a_date,
+    -- * Value constructors
+    Value,
+    missing,
+    string,
+    real,
+    int,
+    nominal,
+    date
+) where
+
+import Control.Arrow                    ((<<<))
+import Data.Binary.Put                  (Put, runPut)
+import Data.Maybe                       (mapMaybe)
+import Data.ByteString.Lazy             (ByteString)
+import Data.ByteString.Lex.Double       (readDouble)
+import Data.Time.Format                 (FormatTime, formatTime)
+import System.Locale                    (defaultTimeLocale)
+import Text.Show.ByteString             (putAscii, putAsciiStr, showp, unlinesP, unwordsP)
+import qualified Text.Show.ByteString   as B
+import Prelude                          hiding (Show)
+import Prelude                          as P
+
+-- | Show a 'Value' in the 'Put' monad.
+type Putter = Value -> Put
+
+-- | ARFF attribute type.
+data Type =
+    StringAttribute  { put :: Putter }
+  | RealAttribute    { put :: Putter }
+  | IntegerAttribute { put :: Putter }
+  | forall a . (Enum a, B.Show a) => NominalAttribute { put :: Putter, enum :: [a] }
+  | DateAttribute    { put :: Putter, format :: String }
+
+-- | Attribute with associated type and name.
+data Attribute = Attribute { attrType :: Type, attrName :: String }
+
+-- | ARFF value type.
+data Value =
+    MissingValue
+  | StringValue  { fromStringValue  :: String }
+  | RealValue    { fromRealValue    :: Double }
+  | IntegerValue { fromIntegerValue :: Integer }
+  | forall a . (Enum a, B.Show a) => NominalValue { fromNominalValue :: a }
+  | forall a . (FormatTime a) =>     DateValue    { fromDateValue    :: a }
+
+-- | ARFF relation.
+data Relation = Relation
+  { name       :: String,
+    attributes :: [Attribute],
+    values     :: [[Value]] }
+
+-- | Construct a relation from a name, attributes and values.
+relation :: String -> [Attribute] -> [[Value]] -> Relation
+relation = Relation
+
+-- | Convert a 'Relation' to its textual representation.
+encode :: Relation -> ByteString
+encode = runPut . putRelation
+
+intersperseP :: Put -> [Put] -> Put
+intersperseP _ []     = return ()
+intersperseP _ (x:[]) = showp x
+intersperseP d (x:xs) = x >> d >> intersperseP d xs
+
+uncommaP :: [Put] -> Put
+uncommaP = intersperseP (putAscii ',')
+
+-- encloseP :: Put -> Put -> Put
+-- encloseP e p = e >> p >> e
+
+defaultDateFormat :: String
+defaultDateFormat = "yyyy-MM-dd'T'HH:mm:ss"
+
+fromISODateFormat :: String -> String
+fromISODateFormat fmt | fmt == defaultDateFormat = "%Y-%m-%dT%X"
+fromISODateFormat _ = error "BUG[fromISODateFormat]: ISO date formats not yet implemented"
+
+putType :: Type -> Put
+putType (StringAttribute _)     = putAsciiStr "string"
+putType (RealAttribute _)       = putAsciiStr "real"
+putType (IntegerAttribute _)    = putAsciiStr "integer"
+putType (NominalAttribute _ es) = putAscii '{' >> (uncommaP <<< map showp) es >> putAscii '}'
+putType (DateAttribute _ fmt)   = unwordsP [putAsciiStr "date", showp fmt]
+
+putAttribute :: Attribute -> Put
+putAttribute (Attribute atype name) = unwordsP [putAsciiStr "@attribute", putAsciiStr name, putType atype]
+
+putMissingValue :: Put
+putMissingValue = putAscii '?'
+
+instance P.Show (Value) where
+    show (StringValue _)  = "String"
+    show (RealValue _)    = "Real"
+    show (IntegerValue _) = "Integer"
+    show (NominalValue _) = "Nominal"
+    show (DateValue _)    = "Date"
+
+typeError :: String -> String -> Value -> Put
+typeError attrName attrType value =
+    error $ "Text.ARFF.encode: type mismatch -- attribute \"" ++ attrName ++ "\""
+         ++ " has type " ++ attrType ++ ","
+         ++ " but got " ++ (show value)
+
+putString :: String -> Value -> Put
+putString _ (StringValue x)   = showp x
+putString _ MissingValue      = putMissingValue
+putString a v                 = typeError a "String" v
+
+putReal :: String -> Value -> Put
+putReal _ (RealValue x)       = showp x
+putReal _ MissingValue        = putMissingValue
+putReal a v                   = typeError a "Real" v
+
+putInteger :: String -> Value -> Put
+putInteger _ (IntegerValue x) = showp x
+putInteger _ MissingValue     = putMissingValue
+putInteger a v                = typeError a "Integer" v
+
+putNominal :: String -> Value -> Put
+putNominal _ (NominalValue x) = showp x
+putNominal _ MissingValue     = putMissingValue
+putNominal a v                = typeError a "Nominal" v
+
+putDate :: String -> String -> Value -> Put
+putDate fmt a (DateValue d)   = showp (formatTime defaultTimeLocale fmt d)
+putDate _   a MissingValue    = putMissingValue
+putDate _   a v               = typeError a "Date" v
+
+putRelation :: Relation -> Put
+putRelation (Relation name attrs values) =
+    unlinesP $
+           [ unwordsP [putAsciiStr "@relation", putAsciiStr name] ]
+        ++ map putAttribute attrs
+        ++ [ putAsciiStr "@data" ]
+        ++ map (uncommaP . zipWith ($) putters) values
+     where putters = map (put . attrType) attrs
+
+-- | String attribute constructor.
+a_string :: String -> Attribute
+a_string a = Attribute (StringAttribute (putString a)) a
+
+-- | Real attribute constructor.
+a_real :: String -> Attribute
+a_real a = Attribute (RealAttribute (putReal a)) a
+
+-- | Integer attribute constructor.
+a_int :: String -> Attribute
+a_int a = Attribute (IntegerAttribute (putInteger a)) a
+
+-- | Nominal attribute constructor.
+a_nominal :: (Enum a, B.Show a) => [a] -> String -> Attribute
+a_nominal xs a = Attribute (NominalAttribute (putNominal a) xs) a
+
+-- | Nominal attribute constructor.
+a_nominalFromTo :: (Enum a, B.Show a) => a -> a -> String -> Attribute
+a_nominalFromTo lo hi = a_nominal (enumFromTo lo hi)
+
+-- | Date attribute constructor.
+-- Currently only supports a default date format, since we're lacking an
+-- ISO-8601 format string parser.
+a_dateFormat :: String -> String -> Attribute
+a_dateFormat fmt a = Attribute (DateAttribute (putDate (fromISODateFormat fmt) a) fmt) a
+
+-- | Construct a date attribute with the default date format
+-- @yyyy-MM-dd'T'HH:mm:ss@.
+a_date :: String -> Attribute
+a_date = a_dateFormat defaultDateFormat
+
+-- | Missing value.
+missing :: Value
+missing = MissingValue
+
+-- | String value constructor.
+string :: String -> Value
+string = StringValue
+
+-- | Real value constructor.
+real :: Double -> Value
+real = RealValue
+
+-- | Integer value constructor.
+int :: Integer -> Value
+int = IntegerValue
+
+-- | Nominal value constructor.
+nominal :: (Enum a, B.Show a) => a -> Value
+nominal = NominalValue
+
+-- | Date value constructor
+date :: (FormatTime a) => a -> Value
+date = DateValue
diff --git a/arff.cabal b/arff.cabal
new file mode 100644
--- /dev/null
+++ b/arff.cabal
@@ -0,0 +1,33 @@
+name:               arff
+version:            0.0.1
+synopsis:           Generate Attribute-Relation File Format (ARFF) files
+description:        Generate Attribute-Relation File Format (ARFF) files.
+                    .
+                    ARFF files are used by the WEKA data mining and machine
+                    learning framework (<http://www.cs.waikato.ac.nz/~ml/weka/>)
+license:            BSD3
+license-file:       LICENSE
+category:           Data, Text
+copyright:          Copyright (c) Stefan Kersten 2008
+author:             Stefan Kersten
+maintainer:         Stefan Kersten
+stability:          provisional
+homepage:           http://code.haskell.org/~StefanKersten/code/arff
+tested-with:        GHC == 6.10.1
+build-type:         Simple
+cabal-version:      >= 1.2
+
+library
+  exposed-modules:  Text.ARFF
+
+  build-depends:    base >= 3,
+                    binary == 0.4.*,
+                    bytestring == 0.9.*,
+                    bytestring-lexing == 0.2,
+                    bytestring-show == 0.2.*,
+                    old-locale >= 1,
+                    time >= 1
+
+  extensions:       ExistentialQuantification
+  
+  ghc-options:      -O2 -funbox-strict-fields 
