The “abc” is substituted as the str
argument:
isJustSpaces str = not (any (isSpace . (: [])) str)
isJustSpaces "abc" = not (any (isSpace . (: [])) "abc")
So “abc” becomes the second argument to any
. any
has this type:
any :: (a -> Bool) -> [a] -> Bool
Since any
accepts a list, you can think of “abc” as a list of characters here. Then the function argument to any
- (isSpace . (: []))
- must be a predicate on characters - Char -> Bool
.
So in this case the type of any
is specialised to
any :: (Char -> Bool) -> [Char] -> Bool
So you’re right that it’s sending the characters to that function one at a time. This is due to the definition of any
, which checks the predicate for each element of the list it’s given.
Since (isSpace . (: []))
is a composition of two functions, each character is first sent to (: [])
, then the output of that goes to isSpace
. (: [])
is a function which puts an item in a singleton list, so it here has the type Char -> [Char]
aka Char -> String
. So it converts each character to a single character string. Then that string is passed to the isSpace
function you defined, which returns a Bool
. If any
finds that (isSpace . (: []))
is true for any of the characters in the string, it returns True
, otherwise it returns False
. Then that result is finally negated by not
.
This definition is slightly overcomplicated for a few reasons. The first is that there’s no need to convert a character to a string to check whether it’s a single space:
isSpace :: Char -> Bool
isSpace char = char /= ' '
I’m also only noticing just now that this actually does the opposite of checking whether it is a space haha. I think you meant (==)
rather than (/=)
isSpace :: Char -> Bool
isSpace char = char == ' '
This allows us to eliminate the function which converts a char to a singleton string. We’ll also have to add a not
composed with isSpace
since we changed (/=)
to (==)
.
Now we have:
isJustSpaces :: [Char] -> Bool
isJustSpaces str = not (any (not . isSpace) str)
This can be simplified further by noticing that using any
here is a bit circuitous. What we actually want is for a condition to be true of all
elements of a list, and that function exists.
isJustSpaces :: [Char] -> Bool
isJustSpaces str = all isSpace str
If you want to, you can make this point free:
isJustSpaces :: [Char] -> Bool
isJustSpaces = all isSpace
At this point you might even consider not defining isJustSpaces
at all. Since its definition is so simple, it might actually make the code clearer to just inline the definition wherever it is called.
The last thing is that a function like your isSpace
already exists in Data.Char
, so you don’t need to define it yourself. The difference is that it works on other forms of whitespace as well, like tabs and newlines.
import Data.Char (isSpace)