Search code examples
haskelltypeclassnewtype

what is the "appropriate value of type `Integer`" and can I write one; also newtypes


Haskell 2010 report section 6.4.1 says

An integer literal represents the application of the function fromInteger to the appropriate value of type Integer.

What does that "appropriate value" look like? Can I write it in source Haskell? I could of course write

x :: Integer
x = 4

But that equation is equivalent to

x = (fromInteger 4) :: Integer

Edit: hmm to avoid infinite regress that should probably be

x = (fromInteger 4?) :: Integer

in which 4? is the mysterious value 4 at type Integer.

so it's selecting the Integer overloading of fromInteger. The type of the literal (in the original x = 4) is still 4 :: Num a => a, it's not type Integer.

I'm thinking about this wrt newtypes:

{-# LANGUAGE  GeneralisedNewtypeDeriving  #-}

newtype Age = MkAge Int  deriving (Num, Eq, Ord, Show)
                        -- fromInteger is in Num
y :: Age
y = 4  
z = (4 + 5 :: Age)      -- no decl for z, inferred :: Age

If I ask to show y I see MkAge 4; if I ask to show x I see plain 4. So is there some invisible constructor for Integers?

Supplementary q for newtypes: since I can write z = (4 + 5 :: Age) is the constructor MkAge really necessary?

mkAge2 :: Age -> Age
mkAge2 = id

w = mkAge2 4

mkAge3 :: Integer -> Age
mkAge3 = fromInteger

u = mkAge3 4

seems to work just as well, if I want something prefix.


Solution

  • Can I write it in source Haskell?

    Sort of.

    You can write 4 :: Integer but 4 is already an application of fromInteger to "an appropriate value". :: Integer only selects an appropriate overload for fromInteger. The application has type Integer though, so it can function like a magic monomorphic literal.

    newtype Age = MkAge Int  deriving (Num, Eq, Ord, Show)
    

    You can write 4 :: Age now and this is OK. This has nothing to do with what Show does. You can write your own Show instance that prints a plain 4 instead of MkAge 4. This is how Show instances for all the built-in types work. The following is GHC-specific, other implementations may have different details, but the general principles are likely to be the same.

    Prelude> :i Int
    data Int = GHC.Types.I# Int#    -- Defined in ‘GHC.Types’
    Prelude> :i Integer
    data Integer
      = integer-gmp-1.0.2.0:GHC.Integer.Type.S# Int#
      | integer-gmp-1.0.2.0:GHC.Integer.Type.Jp# {-# UNPACK #-}integer-gmp-1.0.2.0:GHC.Integer.Type.BigNat
      | integer-gmp-1.0.2.0:GHC.Integer.Type.Jn# {-# UNPACK #-}integer-gmp-1.0.2.0:GHC.Integer.Type.BigNat
        -- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
    

    As you can see, there are data constructors (and they are not that invisible!) for Int and Integer. We can use the one for Int no problem.

    Prelude> :set -XMagicHash
    Prelude> :t 3#
    3# :: GHC.Prim.Int#
    Prelude> :t GHC.Types.I# 3#
    GHC.Types.I# 3# :: Int
    Prelude> show 3
    "3"
    Prelude> show $ GHC.Types.I# 3#
    "3"
    

    OK we have built an Int with a constructor, which doesn't interfere with showing it as a plain 3 one little bit. It is an application of a bona fide constructor to an honest monomorphic literal. What about Integer?

    Prelude> GHC.Integer.Type.S# 3#
    
    <interactive>:16:1: error:
        Not in scope: data constructor ‘GHC.Integer.Type.S#’
        No module named ‘GHC.Integer.Type’ is imported.
    Prelude>
    

    Hmm.

    Prelude> :m + GHC.Integer.Type
    
    <no location info>: error:
        Could not load module ‘GHC.Integer.Type’
        it is a hidden module in the package ‘integer-gmp-1.0.2.0’
    

    So Integer constructors are hidden from the programmer (intentionally I suppose). But if you were writing GHC.Integer.Type itself you would be able to use GHC.Integer.Type.S# 3#. This has type Integer and is again an application of a bona fide constructor to an honest monomorphic literal.