One other way to do this would be to use the logBase function with base 2.
isPot :: (RealFrac b, Floating b) => b -> Bool
isPot = ((==) <*> (fromInteger . round)) . logBase 2
Prelude> isPot 2048
True
Prelude> isPot 1023
False
Prelude> isPot 1
True
Edit:
OK before evaluating the code lets get the math behind it. The idea is simple. Any number which can be expressed as power of two means it's logarithm with respect to base 2 (logBase 2) is a whole number.
Accordingly we will use the logBase 2 function. If we check it's type
Prelude> :t logBase 2
logBase 2 :: Floating a => a -> a
we see that the input number should be a member of the Floating type class. The logBase 2 function gets composed with the previous function which is;
(==) <*> (fromInteger . round)
Now if we forget about the applicative operator <*> this can be rephrased as
\n -> n == (fromInteger. round) n
Now if we check the type of round function
Prelude> :t round
round :: (RealFrac a, Integral b) => a -> b
we see that the input also needs to be a member of RealFrac type class hence the (RealFrac b, Floating b) => constraint in the type declaration of the isPot function.
Now regarding applicative operator <*>, it's actually very handy when you have a two parameter function such as (==) and need to feed the left (first parameter) with x and right with g x. In other words f <*> g = \x -> f x (g x). The type signature of <*> is
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
I would advise you to read the Functors, Applicative Functors and Monoids part of the Learn You a Haskell book.