Search code examples
functionhaskell

Function that takes a variable (of any type) and determines if said variable contains a number


From this same site I extracted this function that checks if a string is a number.

isNumeric :: String -> Bool
isNumeric ""  = False
isNumeric "." = False
isNumeric xs  =
  case dropWhile isDigit xs of
    ""       -> True
    ('.':ys) -> all isDigit ys
    _        -> False

Is there a way to modify the function so that instead of it checking that a string is a number, I can make it so that it checks a variable of any type? I try this:

isNumeric :: Eq a => a -> Bool
isNumeric ""  = False
isNumeric "." = False
isNumeric xs  =
  case dropWhile isDigit xs of
    ""       -> True
    ('.':ys) -> all isDigit ys
    _        -> False

Solution

  • The Haskell code I provided to stackoverflow has a few issues:

    1. The type signature Eq a => a -> Bool suggests that the function can take a value of any type that has an instance of the Eq type class. However, the function is clearly designed to work with strings, as it uses pattern matching with string literals like "" and ".". The type signature should be String -> Bool.

    2. The function isDigit is not in scope. You need to import it from the Data.Char module.

    3. The function isNumeric should handle negative numbers and numbers with a + sign as well.

    4. In addition, In Haskell, functions are strongly typed, and the type system is designed to ensure that functions receive data of the expected type. If you want a function that can take any type of data, you would typically use a type class to define a common interface for all the types you want to handle.

    5. However, Haskell's type system does not allow for a function to directly accept any type of data and then perform runtime type checking like you might do in dynamically typed languages. Instead, you would need to use a type class that all the types you want to handle would implement.

    6. For the specific case of checking if a value is numeric, you could use the Real type class, which encompasses all the types that can be treated as real numbers. Here's an example of how you might define such a function:

    In summary it would look like this:

    {-# LANGUAGE FlexibleInstances #-}
    
    import Data.Char (isDigit)
    import Data.List (isPrefixOf)
    
    class IsNumeric a where
      isNumeric :: a -> Bool
      -- Provide a default implementation that always returns True for numeric types
      isNumeric _ = True
    
    instance IsNumeric Char where
      isNumeric _ = False
    
    -- You can then have instances for all `Num` types without providing a specific implementation
    instance IsNumeric Int
    instance IsNumeric Integer
    instance IsNumeric Float
    instance IsNumeric Double
    -- ... and so on for other `Num` types
    
    -- For String, you provide a specific implementation
    instance IsNumeric String where
      isNumeric ""  = False
      isNumeric "." = False
      isNumeric xs
        | "-" `isPrefixOf` xs || "+" `isPrefixOf` xs = isNumeric (tail xs)
        | otherwise =
            case dropWhile isDigit xs of
              ""       -> True
              ('.':ys) -> all isDigit ys && not (null ys)
              _        -> False