Haskell, comparing a generic to a Char

Viewed 189

I need to make a Haskell function that has a generic input but outputs a boolean depending on if the generic matches certain digits/characters something along the lines of:

checkFunct :: a -> Bool
checkFunct x = (elem x ['a','b','c'])

How this wont work because you cant compare Char to generic. I've looked for ages and I'm new to Haskell so have no clue how to fix it. Note: It it specifically stated the input must be a -> Bool

For example this code works perfectly:

checkFunct :: Char -> String
p x = elem x ["a","b","c"]

However i need 'a' insteaed of Char. Sorry for any confusion. (Apparently 'a' isnt a generic)

3 Answers

One possible interpretation of the question is: you want the function to tell if the argument is a Char and in the list. You can't do that with the signature a -> Bool alone, because type information is not available to functions, however you can require type information explicitly: there's a dedicated class for this.

import Data.Typeable

checkFunct :: Typeable a => a -> Bool
checkFunct x = case cast x of
    Just xc  -> xc `elem` ['a','b','c']
    Nothing  -> False

(Note that unlike in many other languages, cast does not really convert types, it just matches whether it was the target type in the first place.)

*Main> checkFunct 'b'
True
*Main> checkFunct 'f'
False
*Main> checkFunct 37
False
*Main> checkFunct "a"
False

On its own, a function of type a -> Bool, which is short for forall a. a -> Bool, isn’t much use, since it must work “for all a” that the caller specifies, so it’s not allowed to know anything about a. That’s why you’re getting responses saying basically “you can’t get there from here”.

(In fact, there are only two pure total functions with this type: alwaysTrue _ = True and alwaysFalse _ = False.)

But I’m guessing, based on this comment of yours:

I need a function that has and input of :: a -> Bool and it needs to calculate if a is a item in a specified list.

That your question is in the context of a function like this:

contains :: (a -> Bool) -> [a] -> Bool

Now we can do something useful! See, this type is short for forall a. (a -> Bool) -> [a] -> Bool, which is to say, it also operates on values of any type a, but again, the caller of this function is the one who chooses a, and passes in the predicate of type a -> Bool for some specific a, as well as a list of a values. So contains doesn’t know anything about a, but it does know that it can apply the predicate to the elements of the list, since they’re guaranteed to be of the same type.

So for example you can call this function with an argument of type Char -> Bool, which sets a to Char, or one of type Int -> Bool, which sets a to Int, and so on:

isABC :: Char -> Bool
isABC x = x `elem` "abc"

-- contains :: (a -> Bool) -> [a] -> Bool
-- contains isABC :: [Char] -> Bool
-- contains isABC "yeah" == True
-- contains isABC "nope" == False

is123 :: Int -> Bool
is123 x = x >= 1 && x <= 3

-- contains is123 :: [Int] -> Bool
-- contains is123 [] == False
-- contains is123 [0] == False
-- contains is123 [0, 1] == True

If you want to write this contains function yourself, you can use a recursive definition, with this general structure:

contains :: (a -> Bool) -> [a] -> Bool
contains predicate list = case list of

  -- How can you test the current element with the predicate?
  -- What should you do when the predicate returns true?
  -- How can you test the rest of the list?
  current : rest -> _

  -- What should you return if you’ve reached the end?
  [] -> _

The underscores are called typed holes, which you can include in your program to ask GHC to tell you what type it’s expecting there and give you some suggestions for functions and variables in scope that might fit. As you figure out how to fill in more details of your program, you can use more holes to ask for the types of more specific parts, e.g. you might replace the first hole with if _ then _ else _.

Something that may be helpful here is the TypeApplications extension (enabled in GHCi with :set -XTypeApplications or in a source file with {-# LANGUAGE TypeApplications #-}), which lets you write out the type arguments explicitly, e.g. in GHCi:

> :set -XTypeApplications

> contains :: (a -> Bool) -> [a] -> Bool; contains = error "TODO: implement 'contains'"

> :type contains @Char
contains @Char :: (Char -> Bool) -> [Char] -> Bool

> :type contains @Int
contains @Int :: (Int -> Bool) -> [Int] -> Bool

> :t map
map :: (a -> b) -> [a] -> [b]

> :t map @Double
map @Double :: (Double -> b) -> [Double] -> [b]

> :t map @Double @Int
map @Double @Int :: (Double -> Int) -> [Double] -> [Int]

And what woule you like to do if a is something like b -> FreeT (PipeF a b)?

Your type says I can hand you function anything at all and you will come up with a Bool there are only two ways to write this function. checkFunct _ = True and checkFunct _ = False

You need to tell the compiler a little more about what you expect as input.

From your example implementation, I'll guess that you might want to see if the string representation of x is in a list?

You could write:

checkFunct :: Show a => a -> Bool
checkFunct x = (elem (show x) ["a","b","c"])

the Show a => says that whatever I give to as input to this function, show a must return a string.

Or perhaps you want to write your own class

class ToChar a where
  toChar :: a -> Char

You could then write:

checkFunct :: ToChar a=> a -> Bool
checkFunct x = (elem (toChar x) ['a','b','c'])

To use 'checkFunct' you would need to provide an implementation of toChar for any type you wanted to use with that function.

import Data.Char
instance ToChar Int where
  toChar = chr
Related