Function that works with both Fractional and Integral

I haven’t been able to figure this out. I see if I remove mod which requires Integral as I did in getFnA it works.

In other languages I know I would do if (typeof x) == Integral else ... kind of thing my understanding is you can do a runtime type check in Haskell

The error gives a suggestion “Probable fix: use a type annotation to specify what ‘a0’ should be.” but I have not been able to figure out how to do that.

getFn :: (Foldable t, Fractional a, Integral a) => t Char -> Either String (a -> a -> a, String)
getFn str
  | '+' `elem` str = Right ((+), "+")
  | '-' `elem` str = Right ((-), "-")
  | '*' `elem` str = Right ((*), "*")
  | '/' `elem` str = Right ((/), "/")
  | '%' `elem` str = Right (mod, "%")
  | otherwise = Left "Invalid operator. Must be one of +, -, *, /, %"

getFnA :: (Foldable t, Fractional a) => t Char -> Either String (a -> a -> a, String)
getFnA str
  | '+' `elem` str = Right ((+), "+")
  | '-' `elem` str = Right ((-), "-")
  | '*' `elem` str = Right ((*), "*")
  | '/' `elem` str = Right ((/), "/")
  | otherwise = Left "Invalid operator. Must be one of +, -, *, /, %"

simpleCalc :: IO ()
simpleCalc = do
  userInput <- getContents  -- expected: "1+2"
  let a = getFn userInput -- Error appears here
  ...

Here is the error

• Ambiguous type variable ‘a0’ arising from a use of ‘getFn’
prevents the constraint ‘(Fractional a0)’ from being solved.
Relevant bindings include
a :: (a0 → a0 → a0, String)
(bound at /home/klequis/projects/simpleCalc/src/Lib.hs:36:7)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Fractional Double – Defined in ‘GHC.Float’
instance Fractional Float – Defined in ‘GHC.Float’
…plus 9 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: getFn userInput
In an equation for ‘a’: a = getFn userInput
In the expression:
do userInput ← getContents
let a = getFn userInput
print "hi"typecheck(-Wdeferred-type-errors)

1 Like

You can implement that suggestion like this:

simpleCalc :: IO ()
simpleCalc = do
  userInput <- getContents  -- expected: "1+2"
  let
    a :: <your type here> 
    a = getFn userInput -- Error appears here
  ...

But in this case that is probably not the way to solve your issue.

The problem is that integral and fractional numbers are fundamentally incompatible. You can’t use mod with fractional numbers and you can’t use (/) with integral numbers.

There are two alternatives I can think of:

  1. You can use div instead of (/) to divide integral numbers, but this rounds the result down.
  2. You can use mod' instead of mod to work on fractional (actually Real) numbers.
2 Likes

Can you give a complete program in another language that uses this approach? I don’t see how that would work in this situation.

Edit: To clarify, in this case I believe you don’t want to dispatch based on the type, instead you want to dispatch on the operator used in the input. What if the type is an instance of Integral (Integral is a constraint, not a type) while the input operation is /?

A good question and I was seeing that Integral is a constraint, not a type by the errors I was getting while writing code, but it wasn’t clicking in my head till you said it.

As for a complete example in another language I can’t and don’t have to sort of. In JavaScript you can check if a variable is a string or number but since since number covers all the number types you can’t differentiate between types of numbers.

But I wrote it in JS anyway. It just doesn’t care.

const getFn = str => {
    switch (str) {
        case '+':
            return (a,b) => a + b
        case '-':
            return (a,b) => a - b
        case '*':
            return (a,b) => a * b
        case '/':
            return (a,b) => a / b
        case '%':
            return (a,b) => a % b
    }
}

['+', '-', '*', '/', '%'].forEach(x =>{
    const fn = getFn(x)
    console.log(fn.toString())
    console.log(fn(5,2))
})

output

function (a, b) {
        return a + b;
      }
7
function (a, b) {
        return a - b;
      }
3
function (a, b) {
        return a * b;
      }
10
function (a, b) {
        return a / b;
      }
2.5
function (a, b) {
        return a % b;
      }
1

I haven’t written C# since 2008 so I’m not even going to give it a try :slightly_smiling_face:

And a bit of a math lesson for me.

image

I might have know that 30 years ago in college.

Given that and how long I have been playing around with the larger exercise, I think I’m going to drop modulus, and finish-up the rest of the exercise and move on.

You could do something similar to what you probably would want in other languages - convert to some integer type, do the mod and then convert back to Double/Float.

Like this:

getFn :: (Foldable t, RealFrac a) => t Char -> Either String (a -> a -> a, String)
getFn str
  | '+' `elem` str = Right ((+), "+")
  | '-' `elem` str = Right ((-), "-")
  | '*' `elem` str = Right ((*), "*")
  | '/' `elem` str = Right ((/), "/")
  | '%' `elem` str = Right (\x y -> fromIntegral (truncate x `mod` truncate y), "%")
  | otherwise = Left "Invalid operator. Must be one of +, -, *, /, %"

here some example output:

ghci> Right (f, _) = getFn "+"
ghci> f 3.5 6.6
10.1
ghci> Right (f, _) = getFn "%"
ghci> f 19.0 7.0
5.0

of course truncate might not what you want but this is close to the usual type-casts in C-like languages.

Out of the box this will work with both Double and Float (those should be the instances of RealFrac you get with base).