
Hi Russ, you'll likely get more elaborate answers from the real experts here later on, but since I'm still pretty much a beginner in Haskell my perspective might be closer to yours and my answers eventually helpful.
So here are a couple of questions about this example.
What does it mean to say a Num instance for Char. Can you give me an example of what that means in a program that will execute. (Preferably the program would be as simple as possible.)
Well, "Num" is a typeclass. You can visualize it in GHCi like this: Prelude> :i Num class (Eq a, Show a) => Num a where (+) :: a -> a -> a (*) :: a -> a -> a (-) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a Or alternatively have a look at the online docs: http://hackage.haskell.org/packages/archive/base/3.0.3.1/doc/html/GHC-Num.ht... So, instantiating Char for Num would require you to provide implementations for (+), (*), (-), negate, abs, signum and fromInteger for Char. While syntactically possible, I don't see any semantically reasonable way to do so.
Is there a way to display a value along with its type during program execution? I know about Show; is there something similar like ShowWithType (or even ShowType) that (if implemented) will generate a string of a value along with its type?
AFAIK in GHCi you can do both, but not simultaneously: Prelude> let a = 5::Int Prelude> :t a a :: Int Prelude> a 5
It makes sense to me to say that [ ] is a function that takes a type as an argument and generates an value. If that's the case, it also makes sense to say that [ ] == [ ] can't be evaluated because there is simply no == for functions.
Prelude> :t [] [] :: [a] So [] is a list type, not a function type. Prelude> [] == [] True You can actually compare [] to itself. So your reasoning does not seem to be valid to me. At least not on the Haskell source code level (which is what you and your students work with). Daniel referred to the Core level when saying that [] was a function there. My reasoning would go somewhere along the following line: [] has a polymorphic type, specifically it's an empty list of elements of any type ([] :: [a], type variable a is not restricted) So, [] will compare to any empty list, no matter what its elements' type actually is. Given:
let xs = [] ys = 1:xs zs = 'a': xs
Then "tail ys == tail zs" will not type check. Neither "tail ys" nor "tail zs" are polymorphic: ys :: [Integer] zs :: [Char] So the expression "tail ys == tail zs" is invalid - the lhs and rhs must have the same type but they do not. Nothing will get compared, no tail will be determined - it will just plainly be rejected from the compiler (or interpreter). For comparison: "tail ys == []" is different. (tail ys) :: [Integer] [] :: [a] So we set (well, GHC does this for us) type variable a to Integer and have: tail ys :: [Integer] == [] :: [Integer] which is perfectly valid.
It also makes sense to me to say that == is a collection of more concrete functions from which one is selected depending on the type required by the expression within which == appears.
See above - [] is not a function.
Since the required type is known at compile time, it would seem that the selection of which == to use could be made at compile time. One shouldn't have to carry along a dictionary. (Perhaps someone said that earlier. But if so, why the long discussion about Dictionaries?) This seems like a standard definition of an overloaded function. So why is there an objection to simply saying that == is overloaded and letting it go at that?
(==) :: (Eq a) => a -> a -> Bool So, yes, (==) feels a little like an overloaded function. It is a function that accepts any Eq-comparable type. However, overloading IIRC does not behave identically. For example function arity is not required to be the same when overloading methods - and restrictions on types are very different, too. HTH, Thomas