"enable_if" in Haskell
How to write in Haskell something like the following:
showSquare :: (Show a, Num a) => a -> String showSquare x = "The square of " ++ (show x) ++ " is " ++ (show (x * x)) showSquare :: (Show a, not Num a) => a -> String showSquare x = "I don't know how to square " ++ (show x) Basically, something like boost :: enable_if in C ++.
GHC extensions are fine.
Why do you need this? Typechecker ensures that you never call showSquare what is not Num in the first case. There is no instanceof in Haskell , because everything is typed statically.
It does not work for arbitrary types: you can define your own class type , for example
class Mine a where foo :: a -> String instance (Num a) => Mine a where foo x = show x*x And you can add more instances for other classes, but you cannot write just instance Mine a for arbitrary a . An additional instance (Show a) => ... also not help, since overlapping instances are also unacceptable (the link describes how it works, but it requires quite a lot of additional equipment).
Firstly, providing a different type signature for different equations for the same function is not possible at all. Any function can have only one type, regardless of how many equations it has.
Secondly, negative restrictions do not (will not) have a sound meaning in Haskell. Recall what a class constraint means:
f :: Num a => a -> a -> a fxy = x + y Num a in type f means that we can apply any methods of a class of the Num type class to values of type a . We do not knowingly name a specific type in order to obtain general behavior. Essentially, we say: "We don't care that a accurate, but we know that the Num operations apply to it." Therefore, we can use the Num methods on x and y , but nothing more, that is, we can use nothing but the Num methods on x and y . This is what class class restrictions are and why they are needed. They define a common interface for the function.
Now consider your imaginary restriction not Num a . What information does this application provide? Well, we know that a should not be Num . However, this information is completely useless to us. Consider:
f :: not Num a => a -> a f = ??? What can you place instead ??? ? Obviously, we know that we cannot accommodate. But other than this, this signature has no more information than
f :: a -> a and the only operation f could be id (well, maybe undefined , but this is another story).
Finally, consider your example:
showSquare :: (Show a, not Num a) => a -> String showSquare x = "I don't know how to square " ++ (show x) I will not intentionally present the first part of your example; see the first sentence in my answer. You cannot have different equations with different types. But this function in itself is completely useless. You can safely remove the not Num a constraint here and it will not change anything.
The only use for such negative constraints in statically typed Haskell leads to compile-time errors when delivering, say, Int for the variable not Num a -constrainted. But I do not see the point in this.
If I really need something like this (and I don't believe that ever), I think this is the easiest approach in Haskell:
class Show a => ShowSquare a where showSquare :: a -> String showSquare a = "I don't know how to square " ++ (show a) instance ShowSquare Int where showSquare = showSquare' instance ShowSquare Double where showSquare = showSquare' -- add other numeric type instances as necessary -- make an instance for everything else instance Show a => ShowSquare a showSquare' :: (Show a, Num a) => a -> String showSquare' x = "The square of " ++ (show x) ++ " is " ++ (show (x * x)) This requires matching instances, obviously. Some people may complain about the required template, but it's pretty minimal. 5 or 6 copies will cover most numeric numeric types.
Perhaps you could use something using the ideas on the wiki page Advanced Overlap . Note that the technique still requires instances to be explicitly indicated, so that is better than this is probably a matter of taste.
It is also possible to approach the problem with the haskell pattern by writing TH splicing instead of a function. The connector would have to reify ''Num at the call site to determine if the Num instance is in scope, then select the appropriate function. However, there will likely be more problems for this job than just writing Num instances manually.
Depending on “not Num a,” it is very fragile in Haskell in a way that is not fragile in C ++.
In C ++, classes are defined in one placed (closed), while classes of type Haskell are open and can have instances declared in module C of data from module A and a class from module B.
Resolution (without extension) of class classes has a fundamental principle: importing a module such as "C" will never change the previous resolution of type classes.
Code that expects “not Num Custom” will change if some recursively imported module (for example, from another package) defines a “Num Custom instance”.
There is an additional problem with polymorphism. Consider the function in the module "D"
useSS :: Show a => a -> Int -> [String] useSS an = replicate n (showSquare a) data Custom = Custom deriving Show use1 :: Int -> String use1 = useSS Custom -- no Num Custom in scope Now consider the module "E" in another package that imports the above module "D"
instance Num Custom use2 :: Int -> String use2 = useSS Custom -- has a Num Custom now What should (use1 1) and (use2 1) evaluate? Do you want to work with a language with such traps? Haskell is trying to prevent the conceptual design of the existence of this trap.
This type of overload always takes place in C ++ resolution, but this is exactly what Haskell was designed to avoid. It is possible to do such things with the GHC extension, but you need to be careful not to create dangerous traps, and this is not recommended.