[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: the forward method [dynamic vs. static typing]

On 2003-12-11T03:26:54-0500, FranklinChen@cmu.edu wrote:
> That printing code is not faithful to your intention, which was
>         data SuperList a = SuperList a (SuperList [a])
> The code you wrote reflects the intention
>         data SuperList a = SuperList a (Maybe (SuperList [a]))
> which is quite different.

If the printing code I gave is not faithful to my intention, then
neither is the data structure definition that you gave, even though
you explicitly asked me whether the definition you gave is faithful to
my intention.  A C++ pointer can be either null or non-null; non-null
pointers can be expressed in C++ as references.

> C++'s NULL is a value, not a type, so it is impossible, as you correct
> indicate, to write a function template using an encoding of Maybe in
> which NULL is a Nothing and a pointer is a Maybe.

Are you drawing a distinction between C++ and Haskell here, in that
C++'s null is a value where as Haskell's Nothing is a type?  But in
fact, Nothing in Haskell is a value, not a type.

In any case, I would be happy to switch as you did to the Haskell data
type definition

    data SuperList a = SuperList a (Maybe (SuperList [a]))

However, the two approaches to coding in C++ you presented are not as
expressive as Haskell type classes.  To be more concrete, let me provide
not just the above data type definition but also some Haskell code
manipulating that data type in a way that I (still) don't know how to
achieve with C++ (templates).

Here is a complete Haskell program incorporating the data type
definition above:

>   module Main where

>   data SuperList a = SuperList a (Maybe (SuperList [a])) deriving Show

>   make :: a -> Int -> SuperList a
>   make a 0 = SuperList a Nothing
>   make a n = SuperList a (Just (make [a,a] (n - 1)))

>   main :: IO ()
>   main = getLine >>= print . make True . read

The crucial point here is that the "make" function takes an arbitrary
integer and returns a SuperList of that depth.  The main program ("main"
above) simply reads an integer (say "n") from standard input and returns
a SuperList of that depth (namely "make True n").

Here are some sample sessions in which I use the same compiled program
to generate SuperLists of varying depth:

cruella:~$ ./a.out
SuperList True Nothing
cruella:~$ ./a.out
SuperList True (Just (SuperList [True,True] Nothing))
cruella:~$ ./a.out
SuperList True (Just (SuperList [True,True] (Just (SuperList [[True,True],[True,True]] Nothing))))
cruella:~$ ./a.out
SuperList True (Just (SuperList [True,True] (Just (SuperList [[True,True],[True,True]] (Just (SuperList [[[True,True],[True,True]],[[True,True],[True,True]]] Nothing))))))
cruella:~$ ./a.out
SuperList True (Just (SuperList [True,True] (Just (SuperList [[True,True],[True,True]] (Just (SuperList [[[True,True],[True,True]],[[True,True],[True,True]]] (Just (SuperList [[[[True,True],[True,True]],[[True,True],[True,True]]],[[[True,True],[True,True]],[[True,True],[True,True]]]] Nothing))))))))
cruella:~$ ./a.out
SuperList True (Just (SuperList [True,True] (Just (SuperList [[True,True],[True,True]] (Just (SuperList [[[True,True],[True,True]],[[True,True],[True,True]]] (Just (SuperList [[[[True,True],[True,True]],[[True,True],[True,True]]],[[[True,True],[True,True]],[[True,True],[True,True]]]] (Just (SuperList [[[[[True,True],[True,True]],[[True,True],[True,True]]],[[[True,True],[True,True]],[[True,True],[True,True]]]],[[[[True,True],[True,True]],[[True,True],[True,True]]],[[[True,True],[True,True]],[[True,True],[True,True]]]]] Nothing))))))))))

It does not suffice in C++ to simply define a Show class with a virtual
member function for printing to an ostream, because that solution does
not extend to

  - comparing two SuperLists of possibly different depth for
    equality (say by overloading operator==);

  - storing multiple SuperLists of possibly different depth in a single
    container, say vector.

Two SuperLists should be compatible for equality comparisons or storage
in a homogeneous array iff their root type ("a" above) is the same, even
if they may be of different depth.  Indeed, different branches of the
same SuperList may be of different depth, so a C++ solution that encodes
the depth or shape of a SuperList in its type is overly specific.  On
the other hand, two SuperLists whose root types are different should not
be eligible for equality comparisons, even if they are both printable to

Edit this signature at http://www.digitas.harvard.edu/cgi-bin/ken/sig
Remember 9/11 * "It's untidy. And freedom's untidy. And free people are free 
to make mistakes and commit crimes and do bad things." -- Donald Rumsfeld

Attachment: signature.asc
Description: Digital signature