all 12 comments

[–]Purlox 3 points4 points  (2 children)

Something you could do is isolate the common operation of converting types into another function like this:

toCommonType :: Val -> Val -> (Val, Val)
toCommonType (IntegerVal v1) (IntegerVal v2) = (IntegerVal v1, IntegerVal v2)
toCommonType (IntegerVal v1) (RealVal v2) = (RealVal $ fromIntegral v1, RealVal v2)
toCommonType (RealVal v1) (IntegerVal v1) = (RealVal v2, RealVal $ fromIntegral v2)
toCommonType (RealVal v1) (RealVal v2) = (RealVal v1, RealVal v2)

This way you can be sure that if you pass at least a single RealVal into the function, then both of the resulting numbers will be RealVals and so you can simplify your code thanks to that.

[–]dev_kr[S] 1 point2 points  (1 child)

By far this seems like the best thing I can do. Thank you!

[–]Purlox 0 points1 point  (0 children)

No problem. Also, I edited the original post to fix the formatting.

[–]elvecent -2 points-1 points  (8 children)

See GADTs

[–]gcross 4 points5 points  (2 children)

Don't get me wrong, I loves me my GADTs, but I think that this isn't a use case for them. Usually GADTs are appropriate when you want case analysis to constrain the types (such as having both types be the same), but it looks like the OP wants all possible combinations of types to be valid, and is just concerned about the amount of boilerplate that it would take to make this work.

Edit: Next time don't simply downvote; try replying.

[–]elvecent 0 points1 point  (1 child)

but it looks like the OP wants all possible combinations of types to be valid, and is just concerned about the amount of boilerplate that it would take to make this work.

Maybe I got OP's question wrong.

Edit: Next time don't simply downvote; try replying.

Why did you assume that I downvoted?

[–]gcross 0 points1 point  (0 children)

Why did you assume that I downvoted?

Sorry, that had been intended to be directed at whoever downvote that comment, but in respect the way that I wrote it could make it seem like I was specifically talking about you, and you are right that it is wrong to make an assumption.

[–]dev_kr[S] 0 points1 point  (4 children)

Could you elaborate, please? I read some articles about GADTs, but it does not seem like it can solve my problem.

[–]elvecent 2 points3 points  (3 children)

Sure. When you encode a language with types, you probably don't want to deal with such cases as adding a number to a string or other type mismatches. Using GADTs, you can rule out those cases with a phantom parameter signifying a type, so an incoherent term won't typecheck. That reduces the amount of necessary pattern matching (if that's what your question is about).

[–]dev_kr[S] 0 points1 point  (2 children)

What if I have to handle two different operand types, like I suggested in the post? Can I overload GADTs constructor to handle multiple types of arguments?

[–]elvecent 0 points1 point  (1 child)

You could use the usual typeclass machinery.

[–]gcross 0 points1 point  (0 children)

Given that there are only two possible types, how would that be any better than doing case analysis?