diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Version history for hwk
 
+## 0.2.1 (2020-10-11)
+- add --line mode: takes a function on a String - applied to every line
+  - uses new ToString class (not ToList)
+- add --type-check: prints the type of a given function
+
 ## 0.2 (2020-10-10)
 - first release by Jens Petersen
 - uses hint library and Hwk configuration module
diff --git a/Config.hs b/Config.hs
new file mode 100644
--- /dev/null
+++ b/Config.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE FlexibleInstances #-}
+
+module Hwk where
+
+import Data.List (intercalate)
+
+-- | modules to be imported into hint
+-- these modules must be from installed packages
+userModules :: [String]
+userModules = [ "Prelude", "Data.List", "Data.Char", "Data.Bool"
+              -- , "Data.List.Extra", "Data.Tuple.Extra"
+              , "System.FilePath"
+              ]
+
+-- you can put user hwk functions here:
+
+
+int :: String -> Int
+int str = read str :: Int
+
+ints :: [String] -> [Int]
+ints = map int
+
+------ below here all required internal machinery ------
+
+-- | toString and toList are required by hwk
+-- They determine the types of functions hwk can interpret,
+-- allowing some simple polymorphism.
+
+-- ToString allows handling functions of type: ToString a => String -> a
+class ToString a where
+  toString :: a -> String
+instance ToString String where
+  toString x = x
+instance ToString [String] where
+  toString = unwords
+instance ToString [[String]] where
+  toString = intercalate "\t" . map unwords
+instance ToString Int where
+  toString x = show x
+instance ToString [Int] where
+  toString lst = unwords $ map show lst
+
+-- ToList allows handling functions of type: ToList a => [String] -> a
+class ToList a where
+  toList :: a -> [String]
+instance ToList String where
+  toList x = [x]
+instance ToList [String] where
+  toList = id
+instance ToList [[String]] where
+  toList = map (intercalate "\t")
+instance ToList Int where
+  toList x = [show x]
+instance ToList [Int] where
+  toList lst = map show lst
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -1,31 +1,55 @@
-import qualified Data.List as L
+{-# LANGUAGE CPP #-}
+
+#if !MIN_VERSION_simple_cmd_args(0,1,3)
+import Control.Applicative ((<|>))
+#endif
+import Control.Monad.Extra
+import qualified Data.List.Extra as L
+import Data.Version (showVersion)
 import Language.Haskell.Interpreter
 import SimpleCmdArgs
 import System.Directory
+import System.FilePath
+import System.IO (hPutStrLn, stderr)
 
 import Paths_hwk (getDataDir, version)
 
+data HwkMode = DefaultMode | WholeMode | LineMode | TypeMode
+  deriving Eq
+
 main :: IO ()
 main =
   simpleCmdArgs (Just version) "A Haskell awk/sed like tool"
     "Simple shell text processing with Haskell" $
-  runExpr <$> switchWith 'a' "all" "All input as a single string" <*> strArg "FUNCTION" {-<*> many (strArg "FILE...")-}
+  runExpr <$> modeOpt <*> strArg "FUNCTION" {-<*> many (strArg "FILE...")-}
+  where
+    modeOpt :: Parser HwkMode
+    modeOpt =
+      flagWith' TypeMode 't' "type-check" "Print out the type of the given function" <|>
+      flagWith' WholeMode 'a' "all" "Apply function once to the whole input" <|>
+      flagWith DefaultMode LineMode 'l' "line" "Apply function to each line"
 
-runExpr :: Bool -> String -> {-[FilePath] ->-} IO ()
-runExpr allinput stmt {-files-} = do
+runExpr :: HwkMode -> String -> {-[FilePath] ->-} IO ()
+runExpr mode stmt {-files-} = do
   --mapM_ checkFileExists files
   input <- getContents
-  usercfg <- getXdgDirectory XdgConfig "hwk"
+  userdir <- getXdgDirectory XdgConfig "hwk"
+  let usercfg = userdir </> "Hwk.hs"
   datadir <- getDataDir
---    copyFile (datadir </> "hwk.hs") usercfg
-  r <- runInterpreter (runHint [usercfg, datadir] input)
+  let versionCfg = usercfg ++ "-" ++ showVersion version
+  unlessM (doesFileExist versionCfg) $
+    copyFile (datadir </> "Config.hs") versionCfg
+  unlessM (doesFileExist usercfg) $ do
+    copyFile (datadir </> "Config.hs") usercfg
+    warn $ usercfg ++ " created"
+  r <- runInterpreter (runHint userdir input)
   case r of
     Left err -> putStrLn $ errorString err
     Right () -> return ()
   where
-    runHint :: [FilePath] -> String -> Interpreter ()
-    runHint cfgdirs input = do
-      set [searchPath := cfgdirs]
+    runHint :: FilePath -> String -> Interpreter ()
+    runHint cfgdir input = do
+      set [searchPath := [cfgdir]]
       loadModules ["Hwk"]
       setHwkImports ["Prelude"]
       imports <- do
@@ -34,25 +58,32 @@
           then interpret "userModules" infer
           else return ["Prelude", "Data.List"]
       setHwkImports imports
-      -- -- Not sure which is better: typechecking manually or polymorph string
-      -- etypchk <- typeChecksWithDetails stmt
-      -- case etypchk of
-      --   Left err -> error' err
-      --   Right typ ->
-      --     case typ of
-      poly <- do
-        havePoly <- typeChecks "polymorph"
-        if havePoly then do
-          s <- interpret "polymorph" infer
-          return $ if null s then "" else s ++ " . "
-          else return ""
-      if allinput
-        then do
-        fn <- interpret (poly ++ stmt) (as :: String -> [String])
-        liftIO $ mapM_ putStrLn (fn input)
+      if mode == TypeMode then do
+#if MIN_VERSION_hint(0,8,0)
+        etypchk <- typeChecksWithDetails stmt
+        case etypchk of
+          Left err -> liftIO $ mapM_ (putStrLn . errMsg) err
+          Right typ -> liftIO $ putStrLn (cleanupType typ)
+#else
+        typeOf stmt >>= liftIO . putStrLn . cleanupType
+#endif
         else do
-        fn <- interpret (poly ++ stmt) (as :: [String] -> [String])
-        liftIO $ mapM_ putStrLn (fn (lines input))
+        let polyList = "toList . "
+            polyString = "toString . "
+        case mode of
+          DefaultMode -> do
+            fn <- interpret (polyList ++ stmt) (as :: [String] -> [String])
+            liftIO $ mapM_ putStrLn (fn (lines input))
+          LineMode -> do
+            fn <- interpret (polyString ++ stmt) (as :: String -> String)
+            liftIO $ mapM_ (putStrLn . fn) (lines input)
+          WholeMode -> do
+            fn <- interpret (polyList ++ stmt) (as :: String -> [String])
+            liftIO $ mapM_ putStrLn (fn input)
+          TypeMode -> error "already handled earlier"
+      where
+        cleanupType :: String -> String
+        cleanupType = L.replace "[Char]" "String"
 
     -- FIXME use Set
     setHwkImports ms = setImports (L.nub (ms ++ ["Hwk"]))
@@ -63,8 +94,9 @@
     --     error' $ "file not found: " ++ file
 
     errorString :: InterpreterError -> String
-    errorString (WontCompile es) = L.intercalate "\n" (header : map unbox es)
-      where
-        header = "ERROR: Won't compile:"
-        unbox (GhcError e) = e
+    errorString (WontCompile es) =
+      unlines $ "ERROR: Won't compile:" : map errMsg es
     errorString e = show e
+
+warn :: String -> IO ()
+warn = hPutStrLn stderr
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -5,19 +5,19 @@
 
 <img align="right" alt="hwk" src="hwk.png" />
 
-**hwk** tries to demonstrate how a modern Haskell based stream manipulation tool could look like.
-It is similar to tools like **awk** or **sed**.
-`hwk` allows compact function sequences that operate on a list of strings. Because Haskell is lazy and has a powerful arsenal of functions, there is no need to invent another DSL and hopefully it encourages more people to think functionally.
+**hwk** (pronounced "hawk") is a simple Haskell-based text stream manipulation tool, somewhat similar to tools like **awk** or **sed**.
+`hwk` applies concisely composed pure functions to a list of strings from stdin. Because Haskell is lazy and has a powerful arsenal of functions, there is no need to invent another DSL. Hopefully this tool will also encourage more people to think functionally.
 
 ## Example
 
-Prepend a string to each line:
+Change and append a string to each line:
 ```bash
-$ seq 1 3 | hwk 'map (++ ".txt")'
+$ seq 0 2 | hwk --line '(++ ".txt") . show . (+1) . int'
 1.txt
 2.txt
 3.txt
 ```
+or without line-mode: `hwk 'map ((++ ".txt") . show . (+1) . int)'`.
 
 Sum all negative numbers:
 ```bash
@@ -26,27 +26,44 @@
 ```
 The ints function transforms a list of strings into a list of ints
 
+Factorials in your shell scripts!:
+```bash
+seq 10 12 | hwk --line 'let {fact 0 = 1; fact n = n * fact (n - 1)} in fact . int'
+3628800
+39916800
+479001600
+```
+
 Extract data from a file:
 ```bash
-$ cat /etc/passwd | hwk 'take 3 . map (filter (/= "x") . take 3 . splitOn ":")'
-root	0
-bin	1
-daemon	2
+$ cat /etc/passwd | hwk --line 'reverse . filter (/= "x") . take 3 . splitOn ":"' | head -3
+0 root
+1 bin
+2 daemon
 ```
 (a module defining `splitOn` from the extra or split library needs to be added to the Hwk.hs config file).
 
 The argument passed to `hwk` must be a valid Haskell function: a function that takes a list of strings and returns a new list or a single value.
 
-## Configuration
-It uses a configuration module `Hwk` which provides the context for the hint evaluation of the supplied function.
+Check where input contains a certain string:
+```
+$ cat /etc/passwd | hwk --all 'bool "no" "yes" . isInfixOf "1000"'
+yes
+```
 
-It searches for `Hwk.hs` in `~/.config/hwk`, then the package's installed data directory.
+## Configuration
+`hwk` uses a Haskell configuration file `~/.config/hwk/Hwk.hs` which provides the context for the hint evaluation of the supplied function.
 
 The default configuration [Hwk module](data/Hwk.hs) just sets
-the `Prelude`, `Data.List`, and `Data.Char` modules to be imported by default into the hint interpreter.
+the `Prelude`, `Data.List`, and `Data.Char` modules to be imported into the hint interpreter.
 
-If you want to use other modules or define your own functions, you can copy the installed `Hwk.hs` or source `data/Hwk.hs` file to `~/.config/hwk/` to configure hwk.
+The first time hwk is run it sets up `~/.config/hwk/Hwk.hs`.
 
+You can add other modules to import or define your own functions in
+`~/.config/hwk/Hwk.hs`.
+
+After a hwk version update you may wish/have to update up your Hwk.hs file to take account of new changes: a copy of the latest default Hwk.hs is also put in `~/.config/hwk/` with version suffix.
+
 ## Install
 Either use the `install.sh` script, or install by cabal-install or stack
 as described below:
@@ -57,25 +74,20 @@
 Then go to the source directory and run the `install.sh` script, which
 
 - first runs `stack install`
-- then moves the binary installed by `stack install` to `~/.local/bin/hwk-bin`, and sets up a wrapper script `~/.local/bin/hwk` which runs it.
-- and also copies the Hwk.hs configuration module to `~/.config/hwk/Hwk.hs` (backing up any existing file).
+- then moves the binary installed by `stack install` to `~/.local/lib/hwk`, and sets up a wrapper script `~/.local/bin/hwk` which runs it.
 
-You may wish to change the resolver in stack.yaml first, which is also use to determine the resolver used by the created `hwk` wrapper script.
+You may wish to change the resolver in stack.yaml first: it is also used to determine the resolver used by the created `hwk` wrapper script.
 
 ### cabal
 If you are on a Linux distro with a system installed ghc and Haskell libaries,
 you can install with `cabal install` to make use of them.
 
-If you install with a recent cabal the Hwk.hs config module probably lives somewhere like `~/.cabal/store/ghc-*/hwk-*/share/data/Hwk.hs`, or you can copy it from the source `data/Hwk.hs`.
-
 ### stack
 Installing by stack is better if you do not have a system ghc
 and/or global system Haskell libraries installed.
 
 Alternatively to install by hand: run `stack install`,
-and then run it with `stack exec hwk ...` using the same resolver,
-To customize hwk after a stack install it is probably easier just to copy
-the `data/Hwk.hs` source file.
+and then run it with `stack exec hwk ...` using the same resolver.
 
 ## How does `hwk` work?
 
diff --git a/data/Hwk.hs b/data/Hwk.hs
deleted file mode 100644
--- a/data/Hwk.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-{-# LANGUAGE FlexibleInstances #-}
-
-module Hwk where
-
-import Data.List (intercalate)
-
--- | modules to be imported into hint
--- these modules must be globally installed
-userModules :: [String]
-userModules = ["Prelude", "Data.List", "Data.Char"]
-
--- this string is used by hwk
--- | Determines the types of functions hwk can interpret
--- "toList" allows some simple polymorphism
--- use "id" or "" to allow only functions of type: [String] -> [String]
-polymorph :: String
-polymorph = "toList"
-
--- ToList allows handling functions of type: ToList a => [String] -> a
-class ToList a where
-  toList :: a -> [String]
-instance ToList String where
-  toList x = [x]
-instance ToList [String] where
-  toList = id
-instance ToList [[String]] where
-  toList = map (intercalate "\t")
-instance ToList Int where
-  toList x = [show x]
-instance ToList [Int] where
-  toList lst = map show lst
-
--- below here all user defined ------
-
-int :: String -> Int
-int str = read str :: Int
-
-ints :: [String] -> [Int]
-ints = map int
diff --git a/hwk.cabal b/hwk.cabal
--- a/hwk.cabal
+++ b/hwk.cabal
@@ -1,7 +1,10 @@
 name:                hwk
-version:             0.2.0
-synopsis:            A modern Haskell based AWK replacement
-description:         A simple Haskell-based replacement for awk/sed.
+version:             0.3
+synopsis:            Simple Haskell-based awk-like tool
+description:
+            A simple Haskell-based alternative to awk/sed.
+            It uses Hint to apply the function given on the commandline
+            to standard input and outputs the result.
 homepage:            https://github.com/juhp/hwk
 license:             MIT
 license-file:        LICENSE
@@ -11,7 +14,7 @@
                      2020 Jens Petersen
 category:            Development
 build-type:          Simple
-data-files:          data/Hwk.hs
+data-files:          Config.hs
 extra-source-files:  install.sh
 extra-doc-files:     README.md
                      ChangeLog.md
@@ -26,11 +29,10 @@
   other-modules:       Paths_hwk
   autogen-modules:     Paths_hwk
   build-depends:       base <5,
-                       directory,
+                       directory >= 1.2.3.0,
                        extra,
                        filepath,
                        hint,
-                       process,
                        simple-cmd-args >= 0.1.2
   default-language:    Haskell2010
   ghc-options:         -Wall
diff --git a/install.sh b/install.sh
--- a/install.sh
+++ b/install.sh
@@ -4,24 +4,17 @@
 
 stack install
 
-RESOLVER=$(grep -i "resolver:" stack.yaml | sed -e "s/.*: *//")
-
-CFGDIR=$HOME/.config/hwk
-mkdir -p $CFGDIR
+BINHWK=$HOME/.local/bin/hwk
+LIBHWK=$HOME/.local/lib/hwk
 
-if [ -f "$CFGDIR/Hwk.hs" ]; then
-    CFGHASH=$(cd $CFGDIR; md5sum Hwk.hs)
-fi
-if [ "$CFGHASH" != "$(cd data; md5sum Hwk.hs)" ]; then
-cp -p -v --backup=numbered data/Hwk.hs $CFGDIR/
-fi
+mv -f $BINHWK $LIBHWK
 
-mv -f ~/.local/bin/hwk ~/.local/bin/hwk-bin
+RESOLVER=$(grep -i "resolver:" stack.yaml | sed -e "s/.*: *//")
 
-cat > ~/.local/bin/hwk <<EOF
+cat > $BINHWK <<EOF
 #!/bin/sh
 
-stack --resolver $RESOLVER exec ~/.local/bin/hwk-bin "\$@"
+stack --resolver $RESOLVER exec -- $LIBHWK "\$@"
 EOF
 
-chmod u+x ~/.local/bin/hwk
+chmod u+x $BINHWK
