Strange type signature in Haskell GHC 9.6.3

I’m new to Haskell. I start solving the Grains problem from Exercism. Here is the implementations of my function.

checkVal :: Integer -> Maybe Integer
checkVal n
  | n < 1 || n > 64 = Nothing
  | otherwise = Just n

ss :: Integer -> Integer
ss 1 = 1
ss 2 = 2
ss n = case (n - 1) `div` 2 of
  0 -> ss (n - 1) * ss (n - 1)
  _ -> 2 * ss (n - 1)

Even though ss has the signature of Integer -> Integer, I am able to fmap over it just like this.

ghci> :t (checkVal <$> ss)
(checkVal <$> ss) :: Integer -> Maybe Integer
ghci> :t (fmap checkVal ss)
(fmap checkVal ss) :: Integer -> Maybe Integer

while check the type of fmap checkVal, I get like this.

ghci> :t (fmap checkVal)
(fmap checkVal) :: Functor f => f Integer -> f (Maybe Integer)

Could anyone explain to me about this?

1 Like

You can fmap over Integer -> Integer because ((->) r) is a functor. The instance is quite simple:

instance Functor ((->) r) where
    fmap = (.)

so fmap g f can be rewritten as g . f.

You can verify yourself that functor laws are respected:

  • identity: fmap id = id;
  • composition: fmap (f ∘ g) = fmap f ∘ fmap g.

So checkVal <$> ss is nothing more than checkVal . ss, which indeed has type Integer -> Maybe Integer.

5 Likes

Thank you so much for your answer.

2 Likes

Further to @f-a’s answer, how to find out about fmap for yourself?

hoogle is your friend, type fmap in the search box, and click through to find the already-supported instances.

It’d be non-obvious, though, how to get from Integer -> Integer to instance Functor ((->) r).

Is that instance delivering what you want? Or are you surprised you can go ahead and fmap over ss? Is it the purpose of Exercism for you to ‘discover’ that you need an instance and figure out how to write it?

I’m asking because if Exercism is aimed at Haskell 2010, there wouldn’t have been an instance predefined; and I’d say you’ve missed out on an ‘aha’ learning moment.

2 Likes

Thank you for your suggestion.