Thanks, Filipe

I clearly over-stated my case.  I'd like to break it into a number of different question.  Please see below.


On Thu, Sep 30, 2010 at 10:25 PM, Felipe Lessa <felipe.lessa@gmail.com> wrote:
I'll try to clarify some concepts.  Please correct me if I am
wrong, and please forgive me if I misunderstood you.

On Fri, Oct 1, 2010 at 12:54 AM, Russ Abbott <russ.abbott@gmail.com> wrote:
> In explaining fromIntegral, I want to say that it is really a collection of
> overloaded functions:
>
> Integer -> Double
> Int -> Float
> ...
>
> When GHC compiles a line of code with fromIntegral it in, it must decide at
> compile time which of these overloaded functions to compile to.  This is in
> contrast to saying that fromIngetral is a function of the type (Integral a,
> Num b) => a -> b.  In reality there is no (single) function of the type
> (Integral a, Num b) => a -> b because (among other things) every function
> must map between actual types, but (Integral a, Num b) => a -> b does not
> say which actual types are mapped between.
>
> Is the preceding a reasonable thing to say?

First of all, I do think that polymorphic functions are plain ol'
functions.  For example

 id :: a -> a
 id x = x

is a function.  Moreover, in GHC 'id' has only one
representation, taking a thunk and returning a thunk, so even at
the machine code level this is only one function.

Agree.  I over stated my case.  The same can be said for
  length  :: [a] -> Int
It doesn't matter what the type of element in the list is. length runs the same way no matter what. So this is pure polymorphism.
 

Now, maybe 'fromIntegral' has something more than polymorphism?
Well, it has typeclasses.  But we can represent those as
data types, so we could write

 fromIntegralD :: Integral a -> Num b -> a -> b
 fromIntegralD intrDictA numDictB =
   fromIntegral numDictB . toInteger intrDictA

I'm afraid I don't understand this. Moreover, I couldn't get the preceding to load without error.

Better yet, the compiler could write this code for us internally.
Now, using thunks we can get a single machine code for
'fromIntegralD' as well.

In sum, I think all functions are really just that, functions.

--

You may call functions that have typeclass constraints
"overloaded functions", but they still are functions.

Functions that are polymorphic but do not have constraints are
not really overloaded because of parametricity, which means that
they can't change the way they work based on the specific choices
of types you make.

I don't understand the preceding paragraph. Would you mind elaborating.

> If so, can I say the same sort of thing about constants like 1 and []? In
> particular there is no single value []. Instead [] is a symbol which at
> compile time must be compiled to the empty list of some particular type,
> e.g., [Int].  There is no such Haskell value as [] :: [a] since [a] (as
> type) is not an actual type. I want to say the same thing about 1, i.e.,
> that there is no such Haskell value as 1 :: (Num t) => t. When the symbol 1
> appears in a program, the compiler must decide during compilation whether it
> is intended to be 1::Int or 1::Integer or 1::Double, etc.

Well, [a] *is* an actual type, a polymorphic one.

Here is the example that raised that issue for me. Let's say I define null' as follows.

   null' xs = xs == [ ]

If I don't include a declaration in the file, Haskell (reasonably) concludes the following.

  > :t null'
  null' :: (Eq a) => [a] -> Bool

If I write the following at the top level, everything is fine.
  > null' [ ]
  True
 
But if I include the following in the file that defines null', I get an error message.

  test = null' [ ]

      Ambiguous type variable `a' in the constraint:
           `Eq a' arising from a use of `null'' at null.hs:6:17-24
         Probable fix: add a type signature that fixes these type variable(s)

Why is that? And how can it be fixed?  I know I can fix it as follows.

  test = null' ([ ] :: [Integer])

  > :reload
  > test
  True

That's what suggested to me that [ ] had to be compiled into a concrete value.


It seemed to me that similar reasoning applied to things like 1.  How is the following explained?

   Prelude> 111111111111111111111111111111111111111111
   111111111111111111111111111111111111111111
   it :: (Num t) => t
   Prelude> maxBound :: Int
   2147483647
   it :: Int
   Prelude> 111111111111111111111111111111111111111111 - (1::Int)
   -954437178
   it :: Int

Does it make sense to say that the long string of 1's is really of type (Num t) => t?

If so, what does the compiler think it's doing when it processes(?) it as an Int so that it can subtract 1 :: Int from it?  It didn't treat it as maxBound :: Int.  And yet it didn't generate an error message.

Thanks

-- Russ