 <p>Like <a href="http://stackoverflow.com/a/26305824/414413">Christian Conkle</a> hinted at, we can determine if a type has an <code>Integral</code> or <code>Floating</code> instance using more advanced type system features. We will try to determine if the second argument has an <code>Integral</code> instance. Along the way we will use a host of language extensions, and still fall a bit short of our goal. I'll introduce the following language extensions where they are used</p> <pre><code>{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverlappingInstances #-} </code></pre> <h3>Convert Integral context to type</h3> <p>To begin with we will make a class that will try to capture information from the context of a type (whether there's an <code>Integral</code> instance) and convert it into a type which we can match on. This requires the <code>FunctionalDependencies</code> extension to say that the <code>flag</code> can be uniquely determined from the type <code>a</code>. It also requires <code>MultiParamTypeClasses</code>.</p> <pre><code>class IsIntegral a flag | a -&gt; flag </code></pre> <p>We'll make two types to use for the <code>flag</code> type to represent when a type does (<code>HTrue</code>) or doesn't (<code>HFalse</code>) have an <code>Integral</code> instance. This uses the <code>EmptyDataDecls</code> extension.</p> <pre><code>data HTrue data HFalse </code></pre> <p>We'll provide a default - when there isn't an <code>IsIntegral</code> instance for <code>a</code> that forces <code>flag</code> to be something other than <code>HFalse</code> we provide an instance that says it's <code>HFalse</code>. This requires the <code>TypeFamilies</code>, <code>FlexibleInstances</code>, and <code>UndecidableInstances</code> extensions.</p> <pre><code>instance (flag ~ HFalse) =&gt; IsIntegral a flag </code></pre> <p>What we'd really like to do is say that every <code>a</code> with an <code>Integral a</code> instance has an <code>IsIntegral a HTrue</code> instance. Unfortunately, if we add an <code>instance (Integral a) =&gt; IsIntegral a HTrue</code> instance we will be in the same situation Christian described. This second instance will be used by preference, and when the <code>Integral</code> constraint is encountered it will be added to the context with no backtracking. Instead we will need to list all the <code>Integral</code> types ourselves. This is where we fall short of our goal. (I'm skipping the <a href="http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#t:Integral" rel="nofollow">base</a> <code>Integral</code> types from <code>System.Posix.Types</code> since they aren't defined equally on all platforms).</p> <pre><code>import Data.Int import Data.Word import Foreign.C.Types import Foreign.Ptr instance IsIntegral Int HTrue instance IsIntegral Int8 HTrue instance IsIntegral Int16 HTrue instance IsIntegral Int32 HTrue instance IsIntegral Int64 HTrue instance IsIntegral Integer HTrue instance IsIntegral Word HTrue instance IsIntegral Word8 HTrue instance IsIntegral Word16 HTrue instance IsIntegral Word32 HTrue instance IsIntegral Word64 HTrue instance IsIntegral CUIntMax HTrue instance IsIntegral CIntMax HTrue instance IsIntegral CUIntPtr HTrue instance IsIntegral CIntPtr HTrue instance IsIntegral CSigAtomic HTrue instance IsIntegral CWchar HTrue instance IsIntegral CSize HTrue instance IsIntegral CPtrdiff HTrue instance IsIntegral CULLong HTrue instance IsIntegral CLLong HTrue instance IsIntegral CULong HTrue instance IsIntegral CLong HTrue instance IsIntegral CUInt HTrue instance IsIntegral CInt HTrue instance IsIntegral CUShort HTrue instance IsIntegral CShort HTrue instance IsIntegral CUChar HTrue instance IsIntegral CSChar HTrue instance IsIntegral CChar HTrue instance IsIntegral IntPtr HTrue instance IsIntegral WordPtr HTrue </code></pre> <h3>Matching on IsIntegral</h3> <p>Our end goal is to be able to provide appropriate instances for the following class</p> <pre><code>class (Num a, Num b) =&gt; Power a b where pow :: a -&gt; b -&gt; a </code></pre> <p>We want to match on types to choose which code to use. We'll make a class with an extra type to hold the flag for whether <code>b</code> is an <code>Integral</code> type. The extra argument to <code>pow'</code> lets type inference choose the correct <code>pow'</code> to use.</p> <pre><code>class (Num a, Num b) =&gt; Power' flag a b where pow' :: flag -&gt; a -&gt; b -&gt; a </code></pre> <p>Now we'll write two instances, one for when <code>b</code> is <code>Integral</code> and one for when it isn't. When <code>b</code> isn't <code>Integral</code>, we can only provide an instance when <code>a</code> and <code>b</code> are the same.</p> <pre><code>instance (Num a, Integral b) =&gt; Power' HTrue a b where pow' _ = (^) instance (Floating a, a ~ b) =&gt; Power' HFalse a b where pow' _ = (**) </code></pre> <p>Now, whenever we can determine if <code>b</code> is <code>Integral</code> with <code>IsIntegral</code> and can provide a <code>Power'</code> instance for that result, we can provide the <code>Power</code> instance which was our goal. This requires the <code>ScopedTypeVariables</code> extension to get the correct type for the extra argument to <code>pow'</code></p> <pre><code>instance (IsIntegral b flag, Power' flag a b) =&gt; Power a b where pow = pow' (undefined::flag) </code></pre> <p>Actually using these definitions requires the <code>OverlappingInstances</code> extension.</p> <pre><code>main = do print (pow 7 (7 :: Int)) print (pow 8.3 (7 :: Int)) print (pow 1.2 (1.2 :: Double)) print (pow 7 (7 :: Double)) </code></pre> <p>You can read another explanation of how to use <code>FunctionalDependencies</code> or <code>TypeFamilies</code> to avoid overlap in overlapping instances in the <a href="http://www.haskell.org/haskellwiki/GHC/AdvancedOverlap" rel="nofollow">Advanced Overlap</a> article on HaskellWiki.</p>