One fundamental of Haskell is that a string is really a list of characters. Another fundamental is referential transparency, in that a pure function must always give the exact same result when given the same input.
However, these do not seem to be true in actual practice. For example, I created this function:
stringCharList :: String -> [Char] -> Bool
stringCharList a b
| a==b = True
| otherwise = False
if I enter (in the repl)
>stringCharList "hello" ['h','e','l','l','o']
I get back True, as expected. However, if I build this function, I get an error (shown below)
vowelz :: Char -> Bool
vowelz a
| a `elem` vowels = True
| otherwise = False
where vowels = "aeiou"
JWB2.hs:38:22: error:
• Ambiguous type variable ‘t0’ arising from the literal ‘"aeiou"’
prevents the constraint ‘(Data.String.IsString
(t0 Char))’ from being solved.
Relevant bindings include
vowels :: t0 Char (bound at JWB2.hs:38:13)
Probable fix: use a type annotation to specify what ‘t0’ should be.
These potential instances exist:
instance (a ~ Char) => Data.String.IsString [a]
-- Defined in ‘Data.String’
...plus two instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: "aeiou"
In an equation for ‘vowels’: vowels = "aeiou"
In an equation for ‘vowelz’:
vowelz a
| a `elem` vowels = True
| otherwise = False
where
vowels = "aeiou"
|
38 | where vowels = "aeiou"
| ^^^^^^^
Either of these two alternatives resolves the error
vowelz :: Char -> Bool
vowelz a
| a `elem` vowels = True
| otherwise = False
where vowels = "aeiou" :: String
-- OR.....
vowelz :: Char -> Bool
vowelz a
| a `elem` vowels = True
| otherwise = False
where vowels = ['a','e','i','o','u']
I have tracked this down to the declaration of overloaded strings at the top of my experimental/learning hs file:
{-# LANGUAGE OverloadedStrings #-}
if this line is present, I observe the issues described above. If the line is removed, the issues go away and the observed behavior is as expected.
It seems to me that the OverloadedStrings declaration breaks both those fundamentals I listed at the start, which makes it hard to understand how Haskell can be considered "pure". It also suggests that if I want to make modular code that can be incorporated into other projects, I must account for the various hacks and kludges (and perhaps future, not-yet-invented hacks and kludges) or my pure function might break.
Can anyone help me wrap my brain around this?