Algebraic types and subclassing

Nick Kallen phantom@earthlink.net
Wed, 11 Mar 1998 15:01:17 -0800


>We have started research to investigate the possibility to add conditions
add certain points with the idea that the "compiler" can proof the
correctness automatically.
>However, as you said, you hit on undecidability very soon.
>Stil I have some hope that we can work out a workable tool.
>This will take some years though....


I wouldn't mind just having the compiler ignore the conditions, like it does
with the concurrency constructs and such (I think). I like to put this stuff
in for my own purposes, and would like just a syntax checker, rather than
leave it to imprecise comments. It would be nice too if you could flag the
compiler to abort the program if any conditions aren't met. Inelegant, but
great for debugging!

>>- The ability to define functions of arity exceeding 1 in more convenient
>>ways:
>>    You can define:
>>        Add :: Int Int -> Int
>>        Add x = \y -> x + y
>>    But it would be nice to be able to define
>>        Add = (+)
>>    Sure, you can do this by defining Add :: (Int Int -> Int), but that's
>>not the same.
>
>What is the difference? It does not matter how one defines Add, one can
used it curried as >one like?

The difference is that:

Add :: Int Int -> Int
Add = (+)

Doesn't work, although I think it ought to.

> This is convenient when one wants to define (>>=) = (`bind`),
>>or even:
>>        myParser :: Parser foo bar
>>        myParser x = ...
>>    Instead of
>>        myParser = p
>>            where p x = ...

These two examples are precise demonstrations of the annoyance of the arity
syntax rule. Why must

everyParser = p where p = ...
be used instead of
everyParser = ...

Why shouldn't I be able to define operators like

(>>=) = (`bind`). I mean
instead of
(>>=) a b = a `bind` b?

Sure this is just a syntax issue, but this confusing and annoying quirk has
been the subject of a confused user's question on this mailing list (several
months ago).

>>- The use of more classes in the standard library
>
>In the new StdEnv more functions are defined overloaded.
>Any suggestions / contributions for library extensions are welcomed...

My best suggestion is to make the class functor as defined in the refman.

>>- Language level support for ADTs. Data hiding is pretty tricky without
>>this. And you *can't* hide types without this.
>
>It is possible to hide types using the module structure of Clean (exporting
the type >constructor, but not its definition.

The idea is to not allow the programmer to construct types himself. RGB
1092348 1024980918 24096834 is obviously invalid. If you had an ADT, your
makeRGB function could throw exceptions with such parameters. The idea is to
prevent  these sort of things from being even possible.
(Note that this example could be satisfied by my proposed class
"assertions:"

assert
    ForAll RGB x y z :: Color
        x < 255 && y < 255 && z < 255
)

>>- Make the language more orthogonal. Clean is already beautifully small,
but
>>I think it could be a bit smaller. An example is the case construct.
Perhaps
>>I'm missing something, but couldn't case be defined like:
>>    case` :: a [(a, b)] -> b | Eq a
>>    case` x l = snd (hd (filter (((==) x) o fst) l))
>
>The case construct is indeed an extension a programmer could define (eg by
defining a local function using pattern matching).
>Like "if" the case construct is added for convenience.

I know, but having library/user defined functions is not necessarily less
convenient. It makes the language less of a burden on memory, and allows
other things as well. (e.g., I had a use for a curried case function
recently)

>>- OK, this is a pipe dream, but I'd like to see first class types (i.e.,
>>type can be passed as arguments to functions). This eliminates the need
for
>>a special construct for dynamic types, special constructs for parametered
>>types (e.g., :: Parser s r :== [s] -> [([s],r)]), etc.
>
>This is indeed an option.
>For the time being we have chosen for incorporating dynamic types instead
>stressing the combination of a type and an object of that type.
>However, you can play with dynamic types as such because "w = w" is a legal
>representative of an object of any type.

I think you should consider this for future releases. I've seen some really
cool things done with first-class-ish types. A good example is LIFE's first
class classes. LIFE has classes a la OO, and functions can return classes,
not just objects. It can do some really neat things.

>>- Another pipe dream: reflection. I think reflection might be a useful
>>feature in some contexts.

Reflection is when constructs in the language are actually first class.
Functions can return things like function definitions (types of functions),
keywords, etc. This makes it very easy to implement development
environments/compilers for the language and do neat things like change the
syntax of the language easily (e.g., for purposes of making the use of
language idioms less verbose). Lisp/Scheme has this, and it has been proven
to be useful for AI applications and a wide variety of other things.