Потому что тип
foo "bar"
должно быть строкой, в соответствии с вашей подписью, но не является (2 не является строкой). В вашем коде foo является универсальным, поэтому он должен возвращать экземпляр того же типа, что и аргумент.
Сила системы типов haskell дает нам дополнительную информацию - все, что вы можете сделать с аргументом внутри foo, это проверить его равенство с чем-то другим, но я могу придумать любой новый тип (давайте назовем его Baz) и используйте для этого foo - у вас не может быть других экземпляров Baz, поэтому единственный способ вернуть экземпляр Baz - это вернуть тот же экземпляр, что и аргумент.
Если вы переписали foo следующим образом:
foo _ = True
он будет иметь подпись foo :: a -> Bool
, это в основном то, что вы пытались сделать, но с числами все усложняется.
В общем, ваша функция имеет подпись
foo :: (Num t1) => t -> t1
, что означает, что он возвращает экземпляр Num для любого заданного аргумента. (Это потому, что 2 может иметь много разных типов в haskell, в зависимости от того, что вам нужно, это может быть Int, Real или другое.)
Вам следует поиграться с типами в ghci, например:
:t foo
выдаст вам подпись типа infred для foo.
:t 2
дает вам (Num t) => t
, что означает, что 2 может быть экземпляром любого типа, который реализует Num.