Home > Net >  Checking String is a Palindrome Case Insensitive in Haskell
Checking String is a Palindrome Case Insensitive in Haskell

Time:02-17

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 overload String to Text

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)
  • Related