Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell a better way a replacing a character in a string

Tags:

list

haskell

I made this function

replace :: Char -> Char -> [Char] -> [Char]
replace _ _ [] = []
replace a b (c:r)
    | a == c = b : replace a b r
    | otherwise = c : replace a b r

and was looking for a better way to write it when i found this :

replace :: Char -> Char -> [Char] -> [Char]
replace a b  = map $ \c -> if c == a then b else c

There not even the third argument written, and i don't understand the $ and the \c symbols, but it work and I want to know what is happening here

like image 590
Jonathan Layduhur Avatar asked Nov 17 '25 02:11

Jonathan Layduhur


1 Answers

The $ operator applies a function to an argument. We can rewrite your example as

replace :: Char -> Char -> [Char] -> [Char]
replace a b  = map (\c -> if c == a then b else c)

Pragmatically, Haskellers often use $ to avoid parentheses. It's rarely used for other reasons.

About the \c -> ...: this is an anonymous function, also called a "lambda". It stands for the function taking c as argument and returning the ... part. In your case, the function takes c and checks if it's equal to a, in which case it returns b, otherwise it returns c. We could rewrite the code without the lambda as follows:

replace :: Char -> Char -> [Char] -> [Char]
replace a b  = map myFun
   where
   myFun :: Char -> Char
   myFun = \c -> if c == a then b else c

or, moving the argument c to the left of =, as follows:

replace :: Char -> Char -> [Char] -> [Char]
replace a b  = map myFun
   where
   myFun :: Char -> Char
   myFun c = if c == a then b else c

About the "missing third argument": the type Char -> Char -> [Char] -> [Char] can be read in multiple ways:

  • the type of functions taking one argument (Char) and returning a function (Char -> [Char] -> [Char])
  • the type of functions taking two arguments (Char and Char) and returning a function ([Char] -> [Char])
  • the type of functions taking three arguments (Char, Char, and [Char]) and returning a list ([Char])

All these three interpretations are compatible, thanks to currying. Indeed, the "two arguments" function

foo :: A -> B -> B
foo x y = y

and the function

foo :: A -> B -> B
foo x = id              -- id is the identity function B -> B

are the same.

In your example, if you want, you can add the missing argument to both sides of = as follows:

replace :: Char -> Char -> [Char] -> [Char]
replace a b xs = map myFun xs
   where
   myFun :: Char -> Char
   myFun c = if c == a then b else c

In this expanded code, you can see that map myFun xs uses map (library function) to apply myFun to all the elements of list xs, and return the list of all the results. This effectively achieves the substitution you want.

However, without adding the third argument,

replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
   where ...

we can still interpret map myFun as transforming the function myFun :: Char -> Char into a function [Char] -> [Char]. The latter is indeed the return type of replace if we interpret that as a "two arguments" function. That is, replace 'a' 'b' is the function [Char] -> [Char] which takes a string and replaces every 'a' in it with a 'b'.

like image 107
chi Avatar answered Nov 20 '25 09:11

chi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!