diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,11 @@
+shell-monad (0.1.0) UNRELEASED; urgency=medium
+
+  * Added positionalParameters and takeParameter.
+  * Shell functions can be called with parameters.
+  * String is now an instance of CmdArg
+
+ -- Joey Hess <id@joeyh.name>  Thu, 25 Dec 2014 12:17:23 -0400
+
 shell-monad (0.0.3) unstable; urgency=medium
 
   * Added Output data type, which alows cmd to accept (Output (Script ()))
diff --git a/Control/Monad/Shell.hs b/Control/Monad/Shell.hs
--- a/Control/Monad/Shell.hs
+++ b/Control/Monad/Shell.hs
@@ -18,10 +18,13 @@
 	cmd,
 	CmdArg,
 	Output(..),
+	Val(..),
 	comment,
 	newVar,
 	newVarContaining,
 	globalVar,
+	positionalParameters,
+	takeParameter,
 	func,
 	(-|-),
 	forCmd,
@@ -180,7 +183,7 @@
 -- >   name <- newVar "name"
 -- >   readVar name
 -- >   cmd "echo" "hello" name
-cmd :: (ShellCmd result) => L.Text -> result
+cmd :: (ShellCmd params) => L.Text -> params
 cmd c = cmdAll c []
 
 class CmdArg a where
@@ -190,6 +193,15 @@
 instance CmdArg L.Text where
 	toTextArg = getQ . quote
 
+-- | String arguments are automatically quoted.
+instance CmdArg String where
+	toTextArg = toTextArg . L.pack
+
+-- | Any value that can be shown can be passed to 'cmd'; just wrap it
+-- inside a Val.
+instance (Show v) => CmdArg (Val v) where
+	toTextArg (Val v) = L.pack (show v)
+
 -- | Var arguments cause the (quoted) value of a shell variable to be
 -- passed to the command.
 instance CmdArg Var where
@@ -199,6 +211,7 @@
 instance CmdArg (Quoted L.Text) where
 	toTextArg (Q v) = v
 
+-- | Allows passing the output of a command as a parameter.
 instance CmdArg Output where
 	toTextArg (Output s) = "\"$(" <> linearScript s <> ")\""
 
@@ -220,6 +233,9 @@
 -- > cmd "echo" "root's pwent" (Output (cmd "cat" "/etc/passwd" -|- cmd "grep" "root"))
 newtype Output = Output (Script ())
 
+-- | An arbitrary value.
+newtype Val v = Val v
+
 -- | Adds an Expr to the script.
 add :: Expr -> Script ()
 add expr = Script $ \env -> ([expr], env, ())
@@ -259,11 +275,54 @@
 globalVar :: L.Text -> Script Var
 globalVar name = Script $ \env -> let v = Var name in ([], modifyEnvVars env (S.insert v), v)
 
+-- | This special Var expands to whatever parameters were passed to the
+-- shell script.
+--
+-- Inside a func, it expands to whatever parameters were passed to the
+-- func.
+--
+-- (This is `$@` in shell)
+positionalParameters :: Var
+positionalParameters = Var "@"
+
+-- | Takes the first positional parameter, removing it from
+-- positionalParameters and returning a new Var that holds the value of the
+-- parameter.
+--
+-- If there are no more positional parameters, an error will be thrown at
+-- runtime.
+--
+-- For example:
+--
+-- > removefirstfile = script $ do
+-- >   cmd "rm" =<< takeParameter
+-- >   cmd "echo" "remaining parameters:" positionalParameters
+takeParameter :: Script Var
+takeParameter = do
+	p@(Var name) <- newVar "param"
+	Script $ \env -> ([Cmd (name <> "=\"$1\""), Cmd "shift"], env, p)
+
 -- | Defines a shell function, and returns an action that can be run to
 -- call the function.
 --
--- TODO parameter passing to the function
-func :: Script () -> Script (Script ())
+-- The action is variadic; it can be passed any number of CmdArgs.
+-- Typically, it will make sense to specify a more concrete type
+-- when defining the shell function.
+--
+-- For example:
+--
+-- > demo = script $ do
+-- >    hohoho <- mkHohoho
+-- >    hohoho (Val 1)
+-- >    echo "And I heard him exclaim, ere he rode out of sight ..."
+-- >    hohoho (Val 3)
+-- > 
+-- > mkHohoho :: Script (Val Int -> Script ())
+-- > mkHohoho = func $ do
+-- >    num <- takeParameter
+-- >    forCmd (cmd "seq" "1" num) $ \_n ->
+-- >       cmd "echo" "Ho, ho, ho!" "Merry xmas!"
+func :: ShellCmd callfunc => Script () -> Script callfunc
 func s = Script $ \env ->
 	let f = go env (0 :: Integer)
 	    env' = modifyEnvFuncs env (S.insert f)
@@ -279,8 +338,7 @@
 
 	definefunc (Func f) ls = (Cmd $ f <> " () { :") : map indent ls ++ [ Cmd "}" ]
 
-	callfunc :: Func -> Script ()
-	callfunc (Func f) = add $ Cmd f
+	callfunc (Func f) = cmd f
 
 -- | Pipes together two Scripts.
 (-|-) :: Script () -> Script () -> Script ()
diff --git a/examples/santa.hs b/examples/santa.hs
--- a/examples/santa.hs
+++ b/examples/santa.hs
@@ -8,18 +8,23 @@
 
 main :: IO ()
 main = T.writeFile "santa.sh" $ script $ do
-	hohoho <- func $
-		cmd "echo" "Ho, ho, ho!" "Merry xmas!"
-	hohoho
+	hohoho <- mkHohoho
+	hohoho (Val 1)
 
 	promptFor "What's your name?" $ \name -> pipeLess $ do
 		cmd "echo" "Let's see what's in" (val name <> quote "'s") "stocking!"
 		forCmd (cmd "ls" "-1" (quote "/home/" <> val name)) $ \f -> do
 			cmd "echo" "a shiny new" f
-			hohoho
+			hohoho (Val 1)
 
 	cmd "rm" "/table/cookies" "/table/milk"
-	hohoho
+	hohoho (Val 3)
+
+mkHohoho :: Script (Val Int -> Script ())
+mkHohoho = func $ do
+	num <- takeParameter
+	forCmd (cmd "seq" "1" num) $ \_n ->
+		cmd "echo" "Ho, ho, ho!" "Merry xmas!"
 
 pipeLess :: Script () -> Script ()
 pipeLess c = c -|- cmd "less"
diff --git a/shell-monad.cabal b/shell-monad.cabal
--- a/shell-monad.cabal
+++ b/shell-monad.cabal
@@ -1,5 +1,5 @@
 Name: shell-monad
-Version: 0.0.3
+Version: 0.1.0
 Cabal-Version: >= 1.8
 License: BSD3
 Maintainer: Joey Hess <id@joeyh.name>
