[bitc-dev] Type Classes for BitC

Mark P Jones mpj at cse.ogi.edu
Fri Jul 22 01:32:56 EDT 2005


| Let me give a more serious case.
| 
| (define counter
|     (let (c (mutable 10)) (lambda (x) (begin (set! c (+ c x)) c))))
| 
| with the intention that I should get various types (int 16, 
| 32, 64 etc) 
| of skip counters.
| 
| However, the type of counter is (somewhat) _Num -> _Num and 
| NOT Num->Num 
| because of the value restriction. So we will have to define a new 
| counter for every integer type. I don't think this is a very rare
| usage pattern.

I'm not sure what you are getting at with this example.  Here are
some of the things that have me puzzled:

 - Suppose that you didn't have overloading: then you would
   also need to define a different counter for each different
   type.  What is different here (or why would you expect it
   to be different)?

 - Were you wanting a single counter that could accept input values
   (x in the above) and return results with different types in
   different calls?  (I don't see how that would work.)

 - Wouldn't a more likely use case be that I wanted to have multiple
   (i.e., distinct) counters, possibly with the same type in each case?

In short, the example above seems to demonstrate exactly the behavior
I would both want and expect ...

One way to deal with an example like this would be to capture the
counter pattern in a definition like the following:

  (define makeCounter
    (lambda (init)
       (let (c (mutable init))
            (lambda (x)
               (begin (set! c (+ c x))
                      c)))))

(Please forgive syntactic snafu's on my part: my Scheme is very rusty
and I'm not up to speed with all the details of BitC syntax ...)

In qualified types notation, the type of makeCounter might be written:

  makeCounter :: forall a.  Num a => a -> a

And now you might define a bunch of counters as follows:

   (define aCounter       (makeCounter 0:int32))
   (define anotherCounter (makeCounter 2:int32))

   (define microCounter   (makeCounter 0:int64))

   (define countDracula   (makeCounter 0))

This defines a total of four distinct counters.  The first two
have type  nt32 -> int32, while the third has type int64 -> int 64.
The fourth is more interesting (assuming that you have overloaded
numeric literals as in Haskell and hobbit), because it gets a type
a -> a  (there is no forall quantifier here) that doesn't specify
a particular choice for the type a, although it does add a global
predicate/class constraint of the form (Num a) to the environment.
Only later, when you write a function that uses countDracula in a
specific context, will you fix the type of a.  For example:

    (define (multicount x)
       (+ (countDracula x) (aCounter x)))
    ; now we can see that countDracula must have type int32 -> int32

All the best,
Mark

PS. What I've said here assumes the ML/Scheme approach to mutation
and side effects; you probably know already, but Haskell takes a
very different approach based on "monads" ... maybe we'll get to
that later, but it's really a separate topic.



More information about the bitc-dev mailing list