diff --git a/hsini.cabal b/hsini.cabal
--- a/hsini.cabal
+++ b/hsini.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name:          hsini
-version:       0.5.2.1
+version:       0.5.2.2
 synopsis:      ini configuration files
 description:
   Library for reading and writing configuration files in INI format (see <https://en.wikipedia.org/wiki/INI_file>).
diff --git a/src/Data/Ini.hs b/src/Data/Ini.hs
--- a/src/Data/Ini.hs
+++ b/src/Data/Ini.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE ImportQualifiedPost #-}
+
 {- |
 Module    : Data.Ini
 Copyright : 2011-2014 Magnus Therning
@@ -13,10 +15,10 @@
 module Data.Ini where
 
 -- {{{1 imports
-import qualified Data.Map as M
-import Data.Maybe
+import Data.Map qualified as M
+import Data.Maybe (isJust)
 
-import Data.Ini.Types
+import Data.Ini.Types (Config, OptionName, OptionValue, Section, SectionName)
 
 -- {{{1 configurations
 
diff --git a/src/Data/Ini/Reader.hs b/src/Data/Ini/Reader.hs
--- a/src/Data/Ini/Reader.hs
+++ b/src/Data/Ini/Reader.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE ImportQualifiedPost #-}
+
 {- |
 Module    : Data.Ini.Reader
 Copyright : 2011-2014 Magnus Therning
@@ -11,18 +13,14 @@
     IniParseResult,
 ) where
 
-import Control.Monad.Except
-import qualified Text.ParserCombinators.Parsec as P
+import Control.Monad.Except (throwError)
+import Text.ParserCombinators.Parsec qualified as P
 
-import Data.Ini.Reader.Internals
-import Data.Ini.Types
+import Data.Ini.Reader.Internals (IniParseResult, IniReaderError (..), buildConfig, iniParser)
+import Data.Ini.Types (Config)
 
 -- | Parser for a configuration contained in a 'String'.
 parse :: String -> IniParseResult Config
-parse s =
-    let
-        pr = P.parse iniParser "ini" s
-     in
-        case pr of
-            Left e -> throwError . IniParserError $ show e
-            Right is -> buildConfig is
+parse s = case P.parse iniParser "ini" s of
+    Left e -> throwError . IniParserError $ show e
+    Right is -> buildConfig is
diff --git a/src/Data/Ini/Reader/Internals.hs b/src/Data/Ini/Reader/Internals.hs
--- a/src/Data/Ini/Reader/Internals.hs
+++ b/src/Data/Ini/Reader/Internals.hs
@@ -9,11 +9,12 @@
 -}
 module Data.Ini.Reader.Internals where
 
-import Control.Monad (void)
 import Control.Monad.Except (MonadError (throwError))
 import Control.Monad.State (evalState, get, put)
+import Data.Functor (($>))
 import Text.Parsec as P (
     anyChar,
+    between,
     char,
     choice,
     many,
@@ -25,8 +26,10 @@
  )
 import Text.Parsec.String (Parser)
 
-import Data.Ini
-import Data.Ini.Types
+import Data.Char (isSpace)
+import Data.Ini (emptyConfig, setOption)
+import Data.Ini.Types (Config)
+import Data.List (dropWhileEnd)
 
 data IniReaderError
     = IniParserError String
@@ -81,18 +84,12 @@
 surrounded by any number of white space characters (see 'eatWhiteSpace').
 -}
 secParser :: Parser IniFile
-secParser =
-    let
-        validSecNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "._-/@\" "
-     in
-        do
-            void $ char '['
-            void eatWhiteSpace
-            sn <- many1 $ oneOf validSecNameChrs
-            void eatWhiteSpace
-            void $ char ']'
-            void $ manyTill anyChar newline
-            return $ SectionL sn
+secParser = SectionL <$> between sectionNameOpen sectionNameClose sectionName
+  where
+    sectionNameOpen = char '[' *> eatWhiteSpace
+    sectionNameClose = eatWhiteSpace *> char ']' *> manyTill anyChar newline
+    sectionName = many1 $ oneOf validSecNameChrs
+    validSecNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "._-/@\" "
 
 {- | Parser for a single line of an option.  The line must start with an option
 name, then a @=@ must be found, and finally the rest of the line is taken as
@@ -100,18 +97,12 @@
 space characters (see 'eatWhiteSpace').
 -}
 optLineParser :: Parser IniFile
-optLineParser =
-    let
-        validOptNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "_-/@ "
-     in
-        do
-            void eatWhiteSpace
-            on <- many1 $ oneOf validOptNameChrs
-            void eatWhiteSpace
-            void $ char '='
-            void eatWhiteSpace
-            ov <- manyTill anyChar newline
-            return $ OptionL on ov
+optLineParser = OptionL <$> optionName <*> (optionEqual *> optionValue)
+  where
+    optionName = dropWhileEnd isSpace <$> (eatWhiteSpace *> many1 (oneOf validOptNameChrs))
+    optionEqual = eatWhiteSpace *> char '=' *> eatWhiteSpace
+    optionValue = manyTill anyChar newline
+    validOptNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "_-/@ "
 
 {- | Parser for an option-value continuation line.  The line must start with
 either a space or a tab character (\"@ \t@\").  Everything else on the line,
@@ -119,12 +110,9 @@
 value.
 -}
 optContParser :: Parser IniFile
-optContParser = do
-    void $ oneOf " \t"
-    void eatWhiteSpace
-    oc <- noneOf " \t"
-    ov <- manyTill anyChar newline
-    return $ OptionContL $ oc : ov
+optContParser = OptionContL <$> value
+  where
+    value = (:) <$> (oneOf " \t" *> eatWhiteSpace *> noneOf " \t") <*> manyTill anyChar newline
 
 {- | Parser for "noise" in the configuration file, such as comments and empty
 lines.  (Note that lines containing only space characters will be
@@ -133,12 +121,10 @@
 noiseParser :: Parser IniFile
 noiseParser =
     let
-        commentP = do
-            void $ oneOf "#;"
-            manyTill anyChar newline
-        emptyL = newline >> return ""
+        commentP = oneOf "#;" *> manyTill anyChar newline
+        emptyL = (newline $> "")
      in
-        choice [commentP, emptyL] >> return CommentL
+        choice [commentP, emptyL] $> CommentL
 
 iniParser :: Parser [IniFile]
 iniParser =
diff --git a/src/Data/Ini/Types.hs b/src/Data/Ini/Types.hs
--- a/src/Data/Ini/Types.hs
+++ b/src/Data/Ini/Types.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE ImportQualifiedPost #-}
+
 {- |
 Module    : Data.Ini.Types
 Copyright : 2011-2014 Magnus Therning
@@ -6,7 +8,7 @@
 module Data.Ini.Types where
 
 import Control.Arrow (second)
-import qualified Data.Map as M
+import Data.Map qualified as M
 
 type Config = M.Map SectionName Section
 
diff --git a/tst/ReaderI.hs b/tst/ReaderI.hs
--- a/tst/ReaderI.hs
+++ b/tst/ReaderI.hs
@@ -10,7 +10,7 @@
 import Test.Tasty (TestTree)
 import Test.Tasty.HUnit (Assertion, testCase, (@=?))
 import Test.Tasty.TH (testGroupGenerator)
-import Text.ParserCombinators.Parsec as P (parse, Parser)
+import Text.ParserCombinators.Parsec as P (Parser, parse)
 
 import Data.Ini.Reader.Internals (
     IniFile (CommentL, OptionContL, OptionL, SectionL),
@@ -23,7 +23,7 @@
 
 -- Convenience function that translates a parser result to something that's
 -- easier to check.
-p2E :: Parser a  -> String -> String -> Either String a
+p2E :: Parser a -> String -> String -> Either String a
 p2E p s t =
     let
         res = P.parse p s t
@@ -162,8 +162,16 @@
      in
         expected @=? actual
 
-case_optLineParserDropSpace :: Assertion
-case_optLineParserDropSpace =
+case_optLineParserDropSpace1 :: Assertion
+case_optLineParserDropSpace1 =
+    let
+        expected = Right $ OptionL "foo" "bar"
+        actual = p2E optLineParser "optLine" " foo =  bar\n"
+     in
+        expected @=? actual
+
+case_optLineParserDropSpace2 :: Assertion
+case_optLineParserDropSpace2 =
     let
         expected = Right $ OptionL "foo" "bar"
         actual = p2E optLineParser "optLine" " \tfoo\t \t=\t \t bar\n"
