diff --git a/Control/Arrow/Needle.hs b/Control/Arrow/Needle.hs
new file mode 100644
--- /dev/null
+++ b/Control/Arrow/Needle.hs
@@ -0,0 +1,63 @@
+{-|
+Module      : Control.Arrow.Needle
+Description : ASCII-fied arrow notation.
+Copyright   : (c) 2014 Josh Kirklin
+License     : MIT
+Maintainer  : jjvk2@cam.ac.uk
+
+Needle is a domain specific language for ASCII-fied arrow notation. This module enables the use of needle within Haskell by making use of Template Haskell.
+
+In needle, data travels along rails. There are three types of rail, and data travels in different directions on each:
+
+    [@=@] left to right
+    [@\\@] down
+    [@/@] up
+
+Data enters a rail with @}@, and exits with @>@.
+
+When rails are joined, their contents are concatenated. When rails are split, their contents are duplicated.
+
+An external arrow can be embedded in a rail by enclosing it between a @{@ and a @}@.
+
+Inputs and outputs of rails can be asigned labels with a @:@.
+
+Rails can cross one another, if one of the rails has gone \'underground\' by entering a \'tunnel\'. A tunnel entrance is specified by a @)@, and a tunnel exit is specified by a @(@.
+
+Most questions should be answered by a short example:
+
+> import Control.Arrow.Needle
+>
+> nTest :: (Int, Int, Int) -> (Int, (Int, Int, Int))
+> nTest = [nd|
+>                         aLabel:==={div 2}===\
+>     }====\                                  \
+>          {uncurry (+)}==\=================) \ (==>
+>     }====/              \                   \
+>                         \                   \=={nTriple}=>
+>                         \               
+>                      }=={uncurry (-)}====:aLabel
+> |]
+> 
+> nTriple = [nd|
+>     }==\==>
+>        \==>
+>        \==>
+> |]
+
+>>> nTest (3,2,1)
+(5,(-1,-1,-1))
+
+-}
+
+module Control.Arrow.Needle (
+    nd
+  , ndFile
+    -- * Reexported for your convenience
+  , module Control.Arrow
+) where
+
+
+import Control.Arrow
+
+import Control.Arrow.Needle.TH (nd, ndFile)
+
diff --git a/Control/Arrow/Needle/Internal/UnevenGrid.hs b/Control/Arrow/Needle/Internal/UnevenGrid.hs
new file mode 100644
--- /dev/null
+++ b/Control/Arrow/Needle/Internal/UnevenGrid.hs
@@ -0,0 +1,276 @@
+{-|
+Module      : Control.Arrow.Needle.Internal.UnevenGrid
+Description : Data structure used in parsing needle diagrams
+Copyright   : (c) Josh Kirklin
+License     : MIT
+Maintainer  : jjvk2@cam.ac.uk
+
+Implements a 'Grid' data structure which can be examined and moved around with a monad 'GridExamine', used in needle parsing.
+-}
+
+{-# LANGUAGE DeriveFunctor, OverloadedLists, GeneralizedNewtypeDeriving, TupleSections#-}
+
+module Control.Arrow.Needle.Internal.UnevenGrid (
+    -- * Grids
+    Grid ()
+  , grid
+  , prettyGrid
+  , height
+  , GridPosition
+  , (!!?)
+  , findPositions
+    -- * Examining
+  , GridExamine ()
+  , gridExamine
+  , branch
+  , getGrid
+  , getPosition
+  , putPosition
+  , modifyPosition
+  , width
+    -- ** Moving and accessing
+  , hereGet
+  , leftGet
+  , rightGet
+  , lUpGet
+  , lDownGet
+  , rUpGet
+  , rDownGet
+  ) where
+
+import Prelude as Pre
+
+import Data.Foldable as F
+
+import Data.Vector as V
+import Data.Maybe 
+import Data.List 
+import Data.Sequence as S
+
+import Control.Applicative
+import Control.Monad.State as St
+import Control.Monad.Reader
+
+import Control.Arrow
+
+-- | A 'Grid' is a list of rows, the elements of which are not necessarily 
+-- aligned, with the exception that the left edge of the grid is aligned.
+--
+-- So for example, the following is a valid shape for a 'Grid':
+--
+-- >  ____________
+-- > |___|___|_|__|
+-- > |__|_|_|__|_|__
+-- > |_|______|_____|
+-- >
+--
+
+newtype Grid e = Grid { rows :: Vector (GridRow e) }
+    deriving (Eq, Functor, Show)
+
+newtype GridRow e = GridRow { elements :: Vector (GridElem e) } 
+    deriving (Eq, Functor, Show)
+
+fromLeft :: GridRow e -> Int -> Maybe Int
+fromLeft (GridRow row) n = V.findIndex ((> n) . end) $ row
+
+data GridElem e = GridElem {
+    end :: Int
+  , unwrap :: e
+} deriving (Eq, Functor, Show)
+
+type GridPosition = (Int, Int)
+
+-- | Generate a 'Grid' from a list of rows of elements and widths.
+
+grid :: [[(e, Int)]] -> Grid e
+grid rs = Grid . V.fromList . Pre.map (GridRow . V.fromList) $ gridElems
+  where
+    widths = Pre.map (Pre.map snd) rs
+    es = Pre.map (Pre.map fst) rs
+    f a = Pre.zipWith (+) (0 : f a) a
+    ends = Pre.map f widths
+    gridElems = Pre.zipWith (Pre.zipWith GridElem) ends es
+
+-- | Pretty print a grid
+
+prettyGrid :: (e -> Int -> String) -> Grid e -> String
+prettyGrid eShow grid = unlines . Pre.map Pre.concat $ lShowns
+  where
+    untyped = V.map elements . rows $ grid
+    widths = V.map ((\a -> V.zipWith (-) a (0 `cons` a)) . V.map end) untyped
+    unwrappeds = V.map (V.map unwrap) untyped
+    showns = V.zipWith (V.zipWith eShow) unwrappeds widths
+    lShowns = Pre.map V.toList . V.toList $ showns
+
+-- | Get the grid height
+
+height :: Grid e -> Int
+height = V.length . rows
+
+-- | Get the element at a certain position in the grid. The top left block is
+-- in the (0,0) position.
+
+(!!?) :: Grid e -> GridPosition -> Maybe e
+(!!?) grid (rowPos, elemPos) = do
+    row <- rows grid !? rowPos
+    element <- elements row !? elemPos
+    return $ unwrap element
+
+-- | Return a list of positions satisfying a predicate in a grid, in reading order.
+
+findPositions :: (e -> Bool) -> Grid e -> [GridPosition]
+findPositions pred = V.toList . V.concat . V.toList . V.map (\(i,r) -> V.map (i,) . V.findIndices (pred . unwrap) . elements $ r) . indexed . rows
+
+
+-- | A monad for examining a grid.
+
+newtype GridExamine e a = GridExamine (StateT GridPosition (Reader (Grid e)) a)
+    deriving (Functor, Applicative, Monad)
+
+-- | Run a grid examination. A start position must be specified.
+
+gridExamine :: Grid e -> GridPosition -> GridExamine e a -> a
+gridExamine grid start (GridExamine s) = runReader (evalStateT s start) grid
+
+-- | Get the entire grid.
+
+getGrid :: GridExamine e (Grid e)
+getGrid = GridExamine (lift ask)
+
+-- | Get the current position in the grid.
+
+getPosition :: GridExamine e GridPosition
+getPosition = GridExamine get
+
+-- | Set the current position in the grid. 
+-- This is unsafe -- an in bounds check is NOT performed.
+
+putPosition :: GridPosition -> GridExamine e ()
+putPosition = GridExamine . put
+
+-- | Modify the current position in the grid.
+-- This is unsafe -- an in bounds check is NOT performed.
+
+modifyPosition :: (GridPosition -> GridPosition) -> GridExamine e ()
+modifyPosition f = GridExamine (St.modify f)
+
+here :: GridExamine e (Maybe (GridElem e))
+here = do
+    grid <- getGrid
+    (rowPos, elemPos) <- getPosition
+    return $ do
+        row <- rows grid !? rowPos
+        element <- elements row !? elemPos
+        return element
+
+-- | Get the element at the current position.
+
+hereGet :: GridExamine e (Maybe e)
+hereGet = do
+    element <- here
+    return $ fmap unwrap element
+
+-- | Move to and return the grid element to the left. If there is no element to 
+-- the left, returns Nothing.
+
+leftGet :: GridExamine e (Maybe e)
+leftGet = do
+    (rowPos, elemPos) <- getPosition
+    if elemPos > 0 
+        then putPosition (rowPos, elemPos - 1) >> (hereGet)
+        else return Nothing
+
+-- | Move to and return the grid element to the right. If there is no element to 
+-- the right, returns Nothing.
+
+rightGet :: GridExamine e (Maybe e)
+rightGet = do
+    grid <- getGrid
+    (rowPos, elemPos) <- getPosition
+    fromMaybe (return Nothing) $ do
+        row <- rows grid !? elemPos 
+        element <- elements row !? (elemPos + 1)
+        return $ putPosition (rowPos, elemPos + 1) >> return (Just $ unwrap element)
+
+-- | Move to and return the grid element above the column a certain distance 
+-- from the right edge of the current element. If there is no such element, returns 
+-- Nothing.
+
+rUpGet :: Int -> GridExamine e (Maybe e)
+rUpGet n = do
+    grid <- getGrid
+    mCurrentElement <- here
+    (rowPos, _) <- getPosition
+    fromMaybe (return Nothing) $ do
+        currentElement <- mCurrentElement
+        newRow <- rows grid !? (rowPos - 1)
+        i <- fromLeft newRow (end currentElement - 1 - n)
+        return $ putPosition (rowPos - 1, i) >> hereGet
+
+-- | Move to and return the grid element below the column a certain distance 
+-- from the right edge of the current element. If there is no such element, returns 
+-- Nothing.
+
+rDownGet :: Int -> GridExamine e (Maybe e)
+rDownGet n = do
+    grid <- getGrid
+    mCurrentElement <- here
+    (rowPos, _) <- getPosition
+    fromMaybe (return Nothing) $ do
+        currentElement <- mCurrentElement
+        newRow <- rows grid !? (rowPos + 1)
+        i <- fromLeft newRow (end currentElement - 1 - n)
+        return $ putPosition (rowPos + 1, i) >> hereGet
+
+-- | Move to and return the grid element above the column a certain distance 
+-- from the left edge of the current element. If there is no such element, returns 
+-- Nothing.
+
+lUpGet :: Int -> GridExamine e (Maybe e)
+lUpGet n = do
+    grid <- getGrid
+    (rowPos, elemPos) <- getPosition
+    fromMaybe (return Nothing) $ do
+        row <- rows grid !? rowPos
+        let start = fromMaybe 0 $ end <$> (elements row !? (elemPos - 1))
+        newRow <- rows grid !? (rowPos - 1)
+        i <- fromLeft newRow (start + n)
+        return $ putPosition (rowPos - 1, i) >> hereGet
+
+-- | Move to and return the grid element below the column a certain distance 
+-- from the left edge of the current element. If there is no such element, returns 
+-- Nothing.
+
+lDownGet :: Int -> GridExamine e (Maybe e)
+lDownGet n = do
+    grid <- getGrid
+    (rowPos, elemPos) <- getPosition
+    fromMaybe (return Nothing) $ do
+        row <- rows grid !? rowPos
+        let start = fromMaybe 0 $ end <$> (elements row !? (elemPos - 1))
+        newRow <- rows grid !? (rowPos + 1)
+        i <- fromLeft newRow (start + n)
+        return $ putPosition (rowPos + 1, i) >> hereGet
+
+-- | Branch an examination, i.e. perform it and then return to the original position.
+
+branch :: GridExamine e a -> GridExamine e a
+branch ge = do
+    pos <- getPosition
+    x <- ge
+    putPosition pos
+    return x
+
+-- | The width of the current element
+
+width :: GridExamine e (Maybe Int)
+width = do
+    grid <- getGrid
+    (rowPos, elemPos) <- getPosition
+    mh <- here
+    return $ do
+        h <- mh
+        row <- rows grid !? rowPos
+        let start = fromMaybe 0 $ end <$> (elements row !? (elemPos - 1))
+        return (end h - start)
diff --git a/Control/Arrow/Needle/Parse.hs b/Control/Arrow/Needle/Parse.hs
new file mode 100644
--- /dev/null
+++ b/Control/Arrow/Needle/Parse.hs
@@ -0,0 +1,308 @@
+{-|
+Module      : Control.Arrow.Needle.Parse
+Description : Parsing needle diagrams
+Copyright   : (c) 2014 Josh Kirklin
+License     : MIT
+Maintainer  : jjvk2@cam.ac.uk
+
+This module's main export is 'parseNeedle', which parses a needle diagram into a `NeedleArrow`.
+-}
+
+module Control.Arrow.Needle.Parse (
+  -- * Parsing needles
+    NeedleArrow (..)
+  , parseNeedle
+  -- * Errors
+  , NeedleError (..)
+  , presentNeedleError
+  ) where
+
+import qualified Data.Map.Strict as M
+
+import qualified Data.Text as T
+import Data.Maybe
+import Data.Either
+import Data.Monoid
+
+import Text.Parsec as P
+import Text.Parsec.Extra (natural)
+import Data.Char
+
+import Control.Monad
+import Control.Applicative ((<$>), (<*>))
+import Control.Monad.State
+import Control.Arrow
+
+import Control.Arrow.Needle.Internal.UnevenGrid as G
+
+--------------------------------
+-- Types
+--------------------------------
+
+-- | The datatype representing a generic needle arrow.
+
+data NeedleArrow = Input Int Int
+                 | Through NeedleArrow T.Text
+                 | Join [NeedleArrow]
+    deriving (Show, Read, Eq)
+
+-- | The grid element for the first round of parsing.
+
+data NeedleElem = None
+                | Track
+                | In Int Int
+                | Out
+                | LabelIn T.Text
+                | LabelOut T.Text
+                | ExtArrow T.Text
+                | Switch Direction
+                | TunnelEntrance
+                | TunnelExit
+    deriving (Show, Read, Eq)
+
+-- | Errors in parsing.
+
+data NeedleError = ParseError String
+                 | ConstructionError String
+
+instance Show NeedleError where
+    show = presentNeedleError
+
+-- | Present the error.
+
+presentNeedleError :: NeedleError -> String
+presentNeedleError (ParseError s) = "Needle parse error:\n"++s
+presentNeedleError (ConstructionError s) = "Needle construction error:\n"++s
+
+data Direction = Up | Down
+    deriving (Show, Read, Eq)
+
+type NeedleGrid = Grid NeedleElem
+
+--------------------------------
+-- String -> NeedleArrow
+--------------------------------
+
+-- | Parse a string to a needle
+
+parseNeedle :: String -> Either NeedleError NeedleArrow
+parseNeedle = parseNeedleGrid >=> gridArrow
+
+--------------------------------
+-- NeedleGrid -> NeedleArrow
+--------------------------------
+
+gridArrow :: NeedleGrid -> Either NeedleError NeedleArrow
+gridArrow grid = do
+        os <- mapM (arrowToPosition grid) $ outputPositions grid
+        maybe (Left $ ConstructionError "No outputs") return $ arrowJoin os
+
+outputPositions :: NeedleGrid -> [GridPosition]
+outputPositions = findPositions (== Out)
+
+findLabelOutPosition :: T.Text -> GridExamine NeedleElem (Maybe GridPosition)
+findLabelOutPosition t = do
+    grid <- getGrid
+    return $ listToMaybe (findPositions (== (LabelOut t)) grid)
+
+arrowJoin :: [NeedleArrow] -> Maybe NeedleArrow
+arrowJoin [] = Nothing
+arrowJoin [a] = Just a
+arrowJoin as = Just $ Join as
+
+arrowToPosition :: NeedleGrid -> GridPosition -> Either NeedleError NeedleArrow
+arrowToPosition grid pos = gridExamine grid pos go
+  where
+    err = return . Left . ConstructionError
+    success = return . Right
+    
+    tryPath path = branch $ do
+        mp <- path
+        case mp of
+            Nothing -> err "Nothing on this path"
+            Just _ -> go
+
+    go = do
+        mh <- hereGet
+        case mh of
+            Nothing -> err "Position not in grid"
+            Just h -> case h of
+                None -> err "Arrow from nothing"
+                Track -> do
+                    w <- fromJust <$> width
+                    ups <- forM [0 .. (w - 1)] $ \n -> tryPath $ do
+                        e <- lUpGet n
+                        return $ mfilter (== (Switch Down)) e
+                    downs <- forM [0 .. (w - 1)] $ \n -> tryPath $ do
+                        e <- lDownGet n
+                        return $ mfilter (== (Switch Up)) e
+                    left <- tryPath leftGet
+                    let paths = rights $ left : ups ++ downs
+                        mJoint = arrowJoin paths
+                    case mJoint of
+                        Nothing -> do
+                            (n, _) <- G.getPosition
+                            err $ "Track from nowhere on line " ++ show (n + 1)
+                        Just joint -> success joint
+                In n m -> success $ Input n m
+                Out -> do
+                    ml <- leftGet
+                    case ml of
+                        Just l -> go
+                        Nothing -> err "An output has no arrow going into it"
+                LabelIn t -> do
+                    mlo <- findLabelOutPosition t
+                    case mlo of
+                        Just lo -> putPosition lo >> go
+                        Nothing -> err $ "Found label-in '" ++ T.unpack t ++ "' with no label-out"
+                LabelOut t -> do
+                    ml <- leftGet
+                    case ml of
+                        Just l -> go
+                        Nothing -> err $ "Label-out '" ++ T.unpack t ++ "' has no arrow going into it"
+                ExtArrow t -> do
+                    left <- tryPath leftGet
+                    up <- tryPath $ do
+                        e <- lUpGet 0
+                        return $ mfilter (== (Switch Down)) e
+                    down <- tryPath $ do
+                        e <- lDownGet 0
+                        return $ mfilter (== (Switch Up)) e
+                    let paths = rights $ [left,up,down]
+                        mJoint = arrowJoin paths
+                    case mJoint of
+                        Nothing -> do
+                            (n, _) <- G.getPosition
+                            err $ "External arrow '" ++ T.unpack t ++ "' on line " ++ show (n + 1) ++ " has no arrow going into it"
+                        Just joint -> success $ Through joint t
+                Switch d -> do
+                    left <- tryPath leftGet
+                    continuing <- tryPath $ do
+                        e <- case d of
+                            Down -> lUpGet 0
+                            Up -> lDownGet 0
+                        return $ mfilter (== h) e
+                    let paths = rights $ [left, continuing]
+                        mJoint = arrowJoin paths
+                    case mJoint of
+                        Nothing -> do
+                            (n, _) <- G.getPosition
+                            err $ "Line switch from nowhere on line " ++ (show n)
+                        Just joint -> success joint
+                TunnelExit -> do
+                    let tunnel n = if n == 0 
+                            then go
+                            else do
+                                ml <- leftGet
+                                case ml of
+                                    Nothing -> do
+                                        (n,_) <- G.getPosition
+                                        err $ "Tunnel from nowhere on line " ++ (show n)
+                                    Just TunnelExit -> tunnel (n+1)
+                                    Just TunnelEntrance -> tunnel (n-1)
+                                    Just _ -> tunnel n
+                    tunnel 1
+                TunnelEntrance -> do
+                    ml <- leftGet
+                    case ml of
+                        Nothing -> do
+                            (n,_) <- G.getPosition
+                            err $ "Tunnel entrance has no arrow going into it on line " ++ (show n)
+                        Just _ -> go
+
+--------------------------------
+-- String -> NeedleGrid
+--------------------------------
+
+-- | Pretty print a needle grid
+
+prettyNeedleGrid :: NeedleGrid -> String
+prettyNeedleGrid = prettyGrid prettyElem
+  where
+    prettyElem None           n = replicate n ' '
+    prettyElem Track          n = replicate n '='
+    prettyElem (In _ _)       n = replicate (n-1) ' ' ++ "}"
+    prettyElem Out            n = ">" ++ replicate (n-1) ' '
+    prettyElem (LabelIn t)    n = replicate (n - 1 - length s) ' ' ++ s ++ ":"
+      where 
+        s = T.unpack t
+    prettyElem (LabelOut t)   n = ":" ++ s ++ replicate (n - 1 - length s) ' ' 
+      where 
+        s = T.unpack t
+    prettyElem (ExtArrow t)   n = "{" ++ s ++ replicate (n - 2 - length s) ' ' ++ "}"
+      where
+        s = T.unpack t
+    prettyElem (Switch Up)    n = replicate n '/'
+    prettyElem (Switch Down)  n = replicate n '\\'
+    prettyElem TunnelEntrance n = replicate n ')'
+    prettyElem TunnelExit     n = replicate n '('
+
+-- | Parse a needle grid
+
+parseNeedleGrid :: String -> Either NeedleError NeedleGrid
+parseNeedleGrid s = case result of
+    Left pe -> Left . ParseError $
+            "line " ++ (show . sourceLine . errorPos $ pe) ++ ":\n" ++
+            ls !! ((sourceLine . errorPos $ pe) - 1) ++ "\n" ++
+            replicate ((sourceColumn . errorPos $ pe) - 1) ' ' ++ "^"
+    Right x -> Right (grid x)
+  where
+    result = zipWithM parseLine ls [1..]
+    ls = lines s
+    parseLine l n = runParser (do
+        p <- P.getPosition
+        setPosition $ setSourceLine p n
+        es <- many (withWidth . choice . map try $ elemParsers n)
+        optional $ try (string "-- " >> many anyChar)
+        eof
+        return es) 0 "needle expression" l
+    withWidth p = do
+        c1 <- sourceColumn <$> P.getPosition
+        x <- p
+        c2 <- sourceColumn <$> P.getPosition
+        return (x, c2 - c1)
+
+    elemParsers n = [
+        do
+            many1 space
+            return None
+      , do
+            many1 (char '=')
+            return Track
+      , do
+            void (char '}')
+            m <- getState
+            modifyState (+1)
+            return $ In n m
+      , do
+            void (char '>')
+            return Out
+      , do
+            l <- many1 letter
+            spaces
+            void (char ':')
+            return $ LabelIn (T.pack l)
+      , do
+            void (char ':')
+            spaces
+            l <- many1 letter
+            return $ LabelOut (T.pack l)
+      , do
+            void (char '{')
+            f <- anyChar
+            l <- manyTill anyChar (char '}')
+            return $ ExtArrow (T.pack $ f : l)
+      , do
+            void (char '/')
+            return $ Switch Up
+      , do
+            void (char '\\')
+            return $ Switch Down
+      , do
+            void (char ')')
+            return TunnelEntrance
+      , do
+            void (char '(')
+            return TunnelExit
+      ]
+    
diff --git a/Control/Arrow/Needle/TH.hs b/Control/Arrow/Needle/TH.hs
new file mode 100644
--- /dev/null
+++ b/Control/Arrow/Needle/TH.hs
@@ -0,0 +1,109 @@
+{-|
+Module      : Control.Arrow.Needle.TH
+Description : Template Haskell for needle
+Copyright   : (c) 2014 Josh Kirklin
+License     : MIT
+Maintainer  : jjvk2@cam.ac.uk
+
+This module combines the parsing from "Control.Arrow.Needle.Parse" with Template Haskell.
+-}
+
+{-# LANGUAGE TemplateHaskell, TupleSections #-}
+
+module Control.Arrow.Needle.TH (
+    nd
+  , ndFile
+  ) where
+
+import Prelude as Pre
+
+import Control.Arrow.Needle.Parse
+import Control.Arrow
+
+import Control.Applicative
+
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+import Language.Haskell.Meta
+
+import Data.Either
+import Data.Text as T
+import Data.Map.Strict as M
+
+-- | The inline needle quasi-quoter.
+--
+-- > {-# LANGUAGE QuasiQuotes #-}
+-- > 
+-- > exampleArrow :: Num c => (a,b,c) -> (a,a,b,c,c)
+-- > exampleArrow = [nd|
+-- >    }===========>
+-- >       \========>
+-- >    }===========>
+-- >    }==={negate}>
+-- >       \========>
+-- >  |]
+
+nd :: QuasiQuoter
+nd = QuasiQuoter { 
+    quoteExp = \str -> case (parseNeedle str) of
+        Left e -> error . presentNeedleError $ e
+        Right n -> arrowQ n
+  , quotePat = error "Needles cannot be patterns."
+  , quoteDec = error "Needles cannot be declarations."
+  , quoteType = error "Needles cannot be types."
+  }
+
+-- | Load a needle from a file.
+--
+-- > {-# LANGUAGE TemplateHaskell #-}
+-- > 
+-- > exampleArrow :: Float -> Float
+-- > exampleArrow = $(ndFile "example.nd")
+
+ndFile :: FilePath -> ExpQ
+ndFile fp = do
+    str <- runIO $ readFile fp
+    case (parseNeedle str) of
+        Left e -> error . presentNeedleError $ e
+        Right n -> arrowQ n
+
+-- | Convert NeedleArrow to ExpQ
+
+arrowQ :: NeedleArrow -> ExpQ
+arrowQ arrow = do
+    let is = inputs arrow
+    iNameMap <- M.fromList <$> mapM (\(a,b) -> ((a,b),) <$> newName ("_" ++ show a ++ "_" ++ show b)) is
+
+    let iNames = Pre.map snd $ M.toList iNameMap
+        iName (Input a b) = iNameMap ! (a,b)
+
+        f i@(Input a b) = return 
+            $ AppE (VarE $ mkName "arr") 
+            $ LamE [TupP $ Pre.map VarP iNames] (VarE (iName i))
+
+        f (Through a t) = do
+            let ea = either error id $ parseExp . T.unpack $ t
+            b <- f a
+            return $ InfixE (Just b) (VarE $ mkName ">>>") (Just ea)
+
+        f (Join as) = do
+            aNames <- mapM (\n -> newName ("_" ++ show n)) [0..(Pre.length as - 1)]
+            
+            let tupleArrows [c] = f c
+                tupleArrows (c:cs) = [| $(f c) &&& $(tupleArrows cs) |]
+
+                tupleNames [n] = VarP n
+                tupleNames (n:ns) = TupP [VarP n, tupleNames ns]
+
+            b <- tupleArrows as
+
+            return $ InfixE (Just b) (VarE $ mkName ">>>") $ Just
+                $ AppE (VarE $ mkName "arr") 
+                $ LamE [tupleNames aNames] (TupE $ Pre.map VarE aNames)
+
+    f arrow
+
+inputs :: NeedleArrow -> [(Int, Int)]
+inputs (Input a b) = [(a,b)]
+inputs (Through a _) = inputs a
+inputs (Join as) = as >>= inputs
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Josh Kirklin
+
+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/needle.cabal b/needle.cabal
new file mode 100644
--- /dev/null
+++ b/needle.cabal
@@ -0,0 +1,34 @@
+name:                needle
+version:             0.1.0.0
+synopsis:            ASCII-fied arrow notation
+description:         Needle is a domain specific language for ASCII-fied arrow notation. See "Control.Arrow.Needle" for more information and an example.
+license:             MIT
+license-file:        LICENSE
+author:              Josh Kirklin
+maintainer:          Josh Kirklin <jjvk2@cam.ac.uk>
+copyright:           (c) 2014 Josh Kirklin
+category:            Control
+build-type:          Simple
+stability:           experimental
+bug-reports:         https://github.com/ScrambledEggsOnToast/needle/issues
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Control.Arrow.Needle
+                       Control.Arrow.Needle.Parse
+                       Control.Arrow.Needle.TH
+  other-modules:       Control.Arrow.Needle.Internal.UnevenGrid
+  build-depends:       base >=4.7 && <4.8
+                     , text >=1.2 && <1.3
+                     , parsec >= 3.1 && <3.2
+                     , parsec-extra >=0.1 && <0.2
+                     , containers >=0.5 && <0.6
+                     , mtl >=2.2 && <2.3
+                     , template-haskell
+                     , haskell-src-meta >=0.6 && <0.7
+                     , vector >= 0.10 && <0.11
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: git://github.com/ScrambledEggsOnToast/needle.git
