Replacing part of a string with filler characters I came up with this function rangedReplace as an exercice to use recursion and try different ways to make it work (I made at least 3 completely different versions). I think this version is the best but I have a feeling I can improve the way the functions/arguments are handled especially in the censor and in the main functions. How can I get these functions to be more idiomatic Haskell-way of doing, ?

-- Replace the letters in the range [from:to[ ("to" not included) with chr
rangedReplace :: Int -&gt; Int -&gt; Char -&gt; String -&gt; String
rangedReplace from to chr str 
    | from == to = str
    | otherwise  = (take from' str) ++ rangedReplace' chr n (drop from' str)
    where from' = min from to
          to'   = max from to
          n     = to' - from'

-- Helper function
rangedReplace' :: Char -&gt; Int -&gt; String -&gt; String
rangedReplace' chr n str
    | n == 0    = str
    | str == "" = ""
    | otherwise = chr : (rangedReplace' chr (n-1) (tail str))


Usage example:

censor (from,to) = rangedReplace from to '*'

main = do
    putStrLn $ foldr id "Fork bombs" (map censor [(1,3),(6,8)])
Parameter design

To facilitate currying, you should arrange a function's parameters starting with the one that is the least likely to vary.  In this case, I would consider the fill character the parameter that is most likely to be fixed.

The next parameter, I think, should be the range, and it should be specified as a pair rather than as two separate parameters.  That would make rangedReplace fill (from, to) a filter that takes a string and produces a string.

Using inclusive bounds for from and exclusive bounds for to is the right way to go.

I don't think it's beneficial to automatically swap from and to.  That just encourages your caller to be sloppy.  Rather, I would expect a rangedReplace from 3 to 1 to act as a no-op.

With rangedReplace fill (from, to) string, your main function no longer needs id, map, and the censor helper function.

main = do
  putStrLn $ foldr (rangedReplace '*') "Fork bombs" [(1, 3), (6, 8)]


Implementation

The use of ++ should be considered a yellow flag, as it indicates a full list traversal.  You'll be doing three O(from') operations: take, drop, and ++.

I'd rather go with a recursive solution that does everything in one pass, and is fully lazy.  The implementation is shorter, too.

rangedReplace :: Char -&gt; (Int, Int) -&gt; String -&gt; String
rangedReplace _ _ [] = []
rangedReplace fill (from, to) s@(c:cs)
  | to &lt;= 0   = s
  | from &lt;= 0 = fill:cs'
  | otherwise = c:cs'
  where cs' = rangedReplace fill ((from - 1), (to - 1)) cs