diff --git a/bioinformatics-toolkit.cabal b/bioinformatics-toolkit.cabal
--- a/bioinformatics-toolkit.cabal
+++ b/bioinformatics-toolkit.cabal
@@ -1,12 +1,12 @@
 name:                bioinformatics-toolkit
-version:             0.3.2
+version:             0.4.0
 synopsis:            A collection of bioinformatics tools
 description:         A collection of bioinformatics tools
 license:             MIT
 license-file:        LICENSE
 author:              Kai Zhang
 maintainer:          kai@kzhang.org
-copyright:           (c) 2014-2017 Kai Zhang
+copyright:           (c) 2014-2018 Kai Zhang
 category:            Bio
 build-type:          Simple
 extra-source-files:  README.md
diff --git a/src/Bio/Data/Fasta.hs b/src/Bio/Data/Fasta.hs
--- a/src/Bio/Data/Fasta.hs
+++ b/src/Bio/Data/Fasta.hs
@@ -25,7 +25,9 @@
     {-# MINIMAL fromFastaRecord #-}
 
 instance BioSeq s a => FastaLike (s a) where
-    fromFastaRecord (_, xs) = fromBS . B.concat $ xs
+    fromFastaRecord (_, xs) = case fromBS (B.concat xs) of
+        Left err -> error err
+        Right x -> x
     {-# INLINE fromFastaRecord #-}
 
 instance FastaLike Motif where
diff --git a/src/Bio/GO/GREAT.hs b/src/Bio/GO/GREAT.hs
--- a/src/Bio/GO/GREAT.hs
+++ b/src/Bio/GO/GREAT.hs
@@ -14,6 +14,7 @@
 import qualified Data.IntervalMap      as IM
 import           Data.List             (foldl', sortBy)
 import           Data.List.Ordered     (nubSort)
+import           Data.Maybe            (fromJust, isNothing)
 
 import           Bio.Data.Bed
 import           Bio.Utils.Misc        (readInt)
@@ -35,22 +36,22 @@
 -- | Given a gene list and the rule, compute the rulatory domain for each gene
 getRegulatoryDomains :: AssocRule -> [Gene a] -> [(BED3, a)]
 getRegulatoryDomains _ [] = error "No gene available for domain assignment!"
-getRegulatoryDomains (BasalPlusExtension up dw ext) genes = (extendTail r ext, a) : rs
+getRegulatoryDomains (BasalPlusExtension up dw ext) genes = zip
+    (loop $ [Nothing] ++ map Just basal ++ [Nothing]) names
   where
-    (rs, Just (r,a)) = foldl' f ([], Nothing) $ sortBy (compareBed `on` fst) basal
-    f (acc, Nothing) (b,x) = (acc, Just (extendHead b ext, x))
-    f (acc, Just (b', x')) (b,x)
-        | chrom b' /= chrom b = ( (extendTail b' ext, x') : acc
-                                , Just (extendHead b ext, x) )
-        | chromEnd b' >= chromStart b = ((b',x') : acc, Just (b,x))
-        | otherwise = let ext' = min ext $ (chromStart b - chromEnd b') `div` 2
-                      in ((extendTail b' ext', x') : acc, Just (extendHead b ext', x))
-    extendHead (BED3 chr s e) l | s - l >= 0 = BED3 chr (s-l) e
-                                | otherwise = BED3 chr 0 e
-    extendTail (BED3 chr s e) l = BED3 chr s (e+l)
-    basal = flip map genes $ \((chr, tss, str), x) ->
-        if str then (BED3 chr (tss - up) (tss + dw), x)
-               else (BED3 chr (tss - dw) (tss + up), x)
+    loop (a:b:c:rest) = fn a b c : loop (b:c:rest)
+    loop _            = []
+    fn left (Just (BED3 chr s e)) right = BED3 chr leftPos rightPos
+      where
+        leftPos
+            | isNothing left || chr /= chrom (fromJust left) = max (s - ext) 0
+            | otherwise = min s $ max (s - ext) $ chromEnd $ fromJust left
+        rightPos
+            | isNothing right || chr /= chrom (fromJust right) = e + ext   -- TODO: bound check
+            | otherwise = max e $ min (e + ext) $ chromStart $ fromJust right
+    (basal, names) = unzip $ sortBy (compareBed `on` fst) $ flip map genes $
+        \((chr, tss, str), x) -> if str then (BED3 chr (tss - up) (tss + dw), x)
+                                        else (BED3 chr (tss - dw) (tss + up), x)
 getRegulatoryDomains _ _ = undefined
 {-# INLINE getRegulatoryDomains #-}
 
diff --git a/src/Bio/Motif.hs b/src/Bio/Motif.hs
--- a/src/Bio/Motif.hs
+++ b/src/Bio/Motif.hs
@@ -104,7 +104,7 @@
 
 -- | Convert pwm to consensus sequence, see D. R. Cavener (1987).
 toIUPAC :: PWM -> DNA IUPAC
-toIUPAC (PWM _ pwm) = fromBS . B.pack . map f $ M.toRows pwm
+toIUPAC (PWM _ pwm) = unsafeFromBS . B.pack . map f $ M.toRows pwm
   where
     f v | snd a > 0.5 && snd a > 2 * snd b = fst a
         | snd a + snd b > 0.75             = iupac (fst a, fst b)
diff --git a/src/Bio/Seq.hs b/src/Bio/Seq.hs
--- a/src/Bio/Seq.hs
+++ b/src/Bio/Seq.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE BangPatterns          #-}
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
 module Bio.Seq
     (
     -- * Alphabet
@@ -23,6 +24,7 @@
 import           Data.Char8            (toUpper)
 import qualified Data.HashMap.Strict   as M
 import qualified Data.HashSet          as S
+import           Data.Proxy            (Proxy (..))
 import           Prelude               hiding (length)
 
 -- | Alphabet defined by http://www.chem.qmul.ac.uk/iupac/
@@ -54,54 +56,54 @@
 
 class BioSeq' s where
     toBS :: s a -> B.ByteString
+    unsafeFromBS :: B.ByteString -> s a
 
     slice :: Int -> Int -> s a -> s a
 
     length :: s a -> Int
     length = B.length . toBS
-    {-# MINIMAL toBS, slice #-}
-
+    {-# MINIMAL toBS, slice, unsafeFromBS #-}
 
 instance BioSeq' DNA where
     toBS (DNA s) = s
+    unsafeFromBS = DNA
     slice i l (DNA s) = DNA . B.take l . B.drop i $ s
 
 instance BioSeq' RNA where
     toBS (RNA s) = s
+    unsafeFromBS = RNA
     slice i l (RNA s) = RNA . B.take l . B.drop i $ s
 
 instance BioSeq' Peptide where
     toBS (Peptide s) = s
+    unsafeFromBS = Peptide
     slice i l (Peptide s) = Peptide . B.take l . B.drop i $ s
 
-class BioSeq' s => BioSeq s a where
-    alphabet :: s a -> S.HashSet Char
-    fromBS :: B.ByteString -> s a
+class BioSeq' seq => BioSeq seq alphabet where
+    alphabet :: Proxy (seq alphabet) -> S.HashSet Char
+    fromBS :: B.ByteString -> Either String (seq alphabet)
+    fromBS input = case B.mapAccumL fun Nothing input of
+        (Nothing, r) -> Right $ unsafeFromBS r
+        (Just e, _)  -> Left $ "Bio.Seq.fromBS: unknown character: " ++ [e]
+      where
+        fun (Just e) x = (Just e, x)
+        fun Nothing x = let x' = toUpper x
+                        in if x' `S.member` alphabet (Proxy :: Proxy (seq alphabet))
+                            then (Nothing, x')
+                            else (Just x', x')
+    {-# MINIMAL alphabet #-}
 
 instance BioSeq DNA Basic where
     alphabet _ = S.fromList "ACGT"
-    fromBS = DNA . B.map (f . toUpper)
-      where
-        f x | x `S.member` alphabet (undefined :: DNA Basic) = x
-            | otherwise = error $ "Bio.Seq.fromBS: unknown character: " ++ [x]
 
 instance BioSeq DNA IUPAC where
     alphabet _ = S.fromList "ACGTNVHDBMKWSYR"
-    fromBS = DNA . B.map (f . toUpper)
-      where
-        f x | x `S.member` alphabet (undefined :: DNA IUPAC) = x
-            | otherwise = error $ "Bio.Seq.fromBS: unknown character: " ++ [x]
 
 instance BioSeq DNA Ext where
-    alphabet = undefined
-    fromBS = undefined
+    alphabet _ = undefined
 
 instance BioSeq RNA Basic where
     alphabet _ = S.fromList "ACGU"
-    fromBS = RNA . B.map (f . toUpper)
-      where
-        f x | x `S.member` alphabet (undefined :: RNA Basic) = x
-            | otherwise = error $ "Bio.Seq.fromBS: unknown character: " ++ [x]
 
 -- | O(n) Reverse complementary of DNA sequence.
 rc :: DNA alphabet -> DNA alphabet
@@ -112,7 +114,7 @@
         'C' -> 'G'
         'G' -> 'C'
         'T' -> 'A'
-        _ -> x
+        _   -> x
 
 -- | O(n) Compute GC content.
 gcContent :: DNA alphabet -> Double
@@ -130,13 +132,13 @@
                 'B' -> x + 0.75
                 'S' -> x + 1
                 'W' -> x
-                _ -> x + 0.5     -- "NMKYR"
+                _   -> x + 0.5     -- "NMKYR"
         in (x', n+1)
 
 -- | O(n) Compute single nucleotide frequency.
-nucleotideFreq :: BioSeq DNA a => DNA a -> M.HashMap Char Int
+nucleotideFreq :: forall a . BioSeq DNA a => DNA a -> M.HashMap Char Int
 nucleotideFreq dna = B.foldl' f m0 . toBS $ dna
   where
-    m0 = M.fromList . zip (S.toList $ alphabet dna) . repeat $ 0
+    m0 = M.fromList . zip (S.toList $ alphabet (Proxy :: Proxy (DNA a))) . repeat $ 0
     f m x = M.adjust (+1) x m
 {-# INLINE nucleotideFreq #-}
diff --git a/src/Bio/Seq/IO.hs b/src/Bio/Seq/IO.hs
--- a/src/Bio/Seq/IO.hs
+++ b/src/Bio/Seq/IO.hs
@@ -70,7 +70,7 @@
                 ">" ++ show chrSize
             else do
                 hSeek h AbsoluteSeek $ fromIntegral $ headerSize + chrStart + start
-                (Right . fromBS) <$> B.hGet h (end - start)
+                fromBS <$> B.hGet h (end - start)
     _ -> return $ Left $ "Bio.Seq.getSeq: Cannot find " ++ show chr
 {-# INLINE getSeq #-}
 
diff --git a/tests/Tests/ChIPSeq.hs b/tests/Tests/ChIPSeq.hs
--- a/tests/Tests/ChIPSeq.hs
+++ b/tests/Tests/ChIPSeq.hs
@@ -17,9 +17,10 @@
 
 tests :: TestTree
 tests = testGroup "Test: Bio.ChIPSeq"
-    [ testCase "rpkm" testRPKM
+    [ 
     ]
 
+{-
 testRPKM :: Assertion
 testRPKM = do regions <- peaks
               r1 <- tags $$ rpkmBed regions
@@ -27,3 +28,4 @@
               let r1' = map (printf "%0.6f") . V.toList $ r1 :: [String]
                   r2' = map (printf "%0.6f") r2
               r1' @=? r2'
+              -}
diff --git a/tests/Tests/GREAT.hs b/tests/Tests/GREAT.hs
--- a/tests/Tests/GREAT.hs
+++ b/tests/Tests/GREAT.hs
@@ -16,12 +16,14 @@
         , (("chr1", 6000, True), 1)
         , (("chr2", 6000, True), 2)
         , (("chr2", 2000000, False), 3)
+        , (("chr2", 2020000, True), 4)
         ]
 
 results = [ (BED3 "chr1" 0 2000, 0)
           , (BED3 "chr1" 1000 1007000, 1)
-          , (BED3 "chr2" 0 1003000, 2)
-          , (BED3 "chr2" 1003000 3005000, 3)
+          , (BED3 "chr2" 0 1007000, 2)
+          , (BED3 "chr2" 999000 2015000, 3)
+          , (BED3 "chr2" 2005000 3021000, 4)
           ]
 
 loops = [ (BED3 "chr1" 900 1200, BED3 "chr1" 10000 11000)
@@ -35,6 +37,7 @@
             , (BED3 "chr1" 10000 11000, 0)
             , (BED3 "chr1" 10000 11000, 1)
             , (BED3 "chr2" 0 100, 2)
+            , (BED3 "chr2" 2015000 2021000,4)
             ]
 
 tests :: TestTree
diff --git a/tests/Tests/Motif.hs b/tests/Tests/Motif.hs
--- a/tests/Tests/Motif.hs
+++ b/tests/Tests/Motif.hs
@@ -18,7 +18,9 @@
 import qualified Data.Vector.Unboxed as U
 
 dna :: DNA Basic
-dna = fromBS $ B.pack $ map f $ take 5000 $ randomRs (0, 3) (mkStdGen 2)
+dna = case fromBS (B.pack $ map f $ take 5000 $ randomRs (0, 3) (mkStdGen 2)) of
+    Left msg -> error msg
+    Right x -> x
   where
     f :: Int -> Char
     f x = case x of
diff --git a/tests/Tests/Seq.hs b/tests/Tests/Seq.hs
--- a/tests/Tests/Seq.hs
+++ b/tests/Tests/Seq.hs
@@ -1,10 +1,11 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Tests.Seq (tests) where
 
-import Bio.Seq
+import           Bio.Seq
+import           Data.Either
 import qualified Data.HashMap.Strict as M
-import Test.Tasty
-import Test.Tasty.HUnit
+import           Test.Tasty
+import           Test.Tasty.HUnit
 
 tests :: TestTree
 tests = testGroup "Test: Bio.Seq"
@@ -13,7 +14,7 @@
 
 testNuclFreq :: Assertion
 testNuclFreq = do
-    let dna = fromBS "ACTTCCCGGGD" :: DNA IUPAC
+    let dna = fromRight undefined $ fromBS "ACTTCCCGGGD" :: DNA IUPAC
         test = M.lookupDefault undefined 'C' $ nucleotideFreq dna
         expected = 4
     test @=? expected
