I'm trying to write a function that checks if a string is a Palindrome (case insensitive) in Haskell. To make sure capital case characters don't cause an issue, I first convert the string to lower case with toLower
(https://hackage.haskell.org/package/text-2.0/docs/Data-Text.html#v:toLower). Then I check if that is equal to the reversed version of the same thing. Here's my code:
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (toLower)
isPalindrome :: String -> Bool
isPalindrome xs = toLower xs == toLower $ reverse xs
I realized toLower
uses Text instead of String and we need to use {-# LANGUAGE OverloadedStrings #-}
to overload String to Text. But this does not compile.
I replicated this in GHCI as well. I first used :set -XOverloadedStrings
to make sure to overload strings. Then I run this:
Prelude Data.Text> toLower $ reverse "sdssSDSS"
<interactive>:92:11: error:
• Couldn't match expected type ‘Data.Text.Internal.Text’
with actual type ‘[Char]’
• In the second argument of ‘($)’, namely ‘reverse "sdssSDSS"’
In the expression: toLower $ reverse "sdssSDSS"
In an equation for ‘it’: it = toLower $ reverse "sdssSDSS"
I don't understand why this doesn't work. If I first run reverse "sdssSDSS"
and copy paste the result into toLower
it works without an issue.
CodePudding user response:
and we need to use
{-# LANGUAGE OverloadedStrings #-}
to overloadString
toText
no. That extension should arguably be called OverloadedStringLiterals
. It only changes the behaviour of literals such as "hello"
or "sdssSDSS"
, but doesn't convert between String
and Text
otherwise. Generally, automatic conversion never happen in Haskell, all that happens is that you can have polymorphic values, like numerical literals or string literals with that extension.
In modern Haskell, probably the best option is just to write it all in Text
and never introduce String
at all:
import qualified Data.Text as Txt
isPalindrome :: Txt.Text -> Bool
isPalindrome xs = Txt.toLower xs == Txt.toLower (Txt.reverse xs)
(note that you can't use $
there, because it has lower precedence than ==
).
But you don't need to use Text
at all, there's also single-character version of toLower
:
import Data.Char (toLower)
isPalindrome :: String -> Bool
isPalindrome xs = map toLower xs == map toLower (reverse xs)
BTW, in both cases it would be possible to use toLower
only once:
isPalindrome :: String -> Bool
isPalindrome xs = xsLC == reverse xsLC
where xsLC = map toLower xs
CodePudding user response:
leftaroundabout has already explained to you how to solve your problem but I have one small addition. If you want to use regular Char -> Char functions with a Text type, you can easily do it using Lens and unpacked ISO (it's convenient but slow :)).
{-# LANGUAGE OverloadedStrings #-}
import Control.Lens
import qualified Data.Text as T
useFun1 :: (String -> String) -> T.Text -> T.Text
useFun1 = over unpacked
useFun2 :: (Char -> Char) -> T.Text -> T.Text
useFun2 = over (unpacked . traversed)