Arc Forumnew | comments | leaders | submitlogin
4 points by sacado 5880 days ago | link | parent

Wow, I think you won the price of the longest post so far. And it is even a very clever one, actually. And I think your view and almkglor's are not so far from each other.

You state that there should only be the few basic types currently defined in Arc. Paul's idea was to eventually get rid of strings (they are a special kind of list) and even numbers (they are a special kind of list too...). But he finally didn't, and won't, at least for numbers. He also said that this view (as few basic types as possible) finally forced him to develop a basic type system (with annotate, type, rep and isa) to distinguish between the raw list '(a a a a a) as the number 5, as the string "aaaaa" or as an actual list of 5 symbols.

In a way or another, you need explicit types if you want some kind of dynamic typing. Assembly language work the opposite way : e.g. you state (explictly or not) the arg of your function is a number. If the user gave you what he considers a string, too bad for him, because you can't distinguish between them. That means your function can't be polymorphic and you are stuck in an even more contrived space than with user types.

You need an isa function (call it isa or hasa, never mind as for now). For example, car should have a list, and nothing else. To do so, you have to check its type. If you want to redefine car so as to take scanners, generators, ... into consideration, that's easy too : just define your own version of car : if arg is a list, call the original car, else arg is a generator à la Python, so funcall it :

  (let _ car car
    (def car (x)
      (if (isa x 'list)
        (_car x)
Ok, you're right until now cchooper, predefined types are enough for these situations, and that's how we should do in such cases. Now imagine we want to deal with lists, generators and arrays defined through FFI. You have to distinguish between the latter two, but how can you do that ? Encapsulating them in a cons whose car is a discriminant between both types will not work here, as a cons isa 'list. That's why you need a way to define these new types, and that is what annotate is for.

  (let _car car
    (def car (x)
      (if (isa x 'list)
          (_car x)
        (isa x 'generator)
          ((rep x))
        (isa x 'array)
          (a-get x 1)
          (err "Not a valid type for car : " type.x))))
Now, about the distinction between isa and hasa. The wonderfull thing about annotate is that it is very generic ! It does not provide you with a way to say your data is of a specific type, it lets you annotate your data with whatever data you want ! The fact that it works with isa is a side effect actually, annotate does not care about isa. You can annotate with a symbol for sure, but also a string, a number, a list, a macro, a closure, a continuation, whatever !

That means you can do something this way, for example :

  (annotate (listtab `((car . ,(fn (self) (self 'car))) (cdr . ,(fn (self) (self 'cdr))) x)

  (let _car car
    (def car (x)
      (if (isa x 'list)
        (_car x)
        (let tx type.x
          (tx!car rep.x)))))
And you've got an object system where the car function is embedded into the data when you don't apply it to lists. That's it, you used annotate as it is now defined to create an has-a behavior. Almkglor has got many other funny ideas with typing, and I think all of them can be implemented simply with annotate and encapsulating old definitions of core functions and axioms into usertype-aware ones.

Maybe Arc needs a few more facilities right there (for example, having to use rep on annotated data is, I think, the biggest mistake of that type system. Please, pg, correct this !), but I think we've got everything we need. Almkglor only proposes a few macros and discipline in librarys, but this can be done with the current language definition (and ignored by everybody but him :)

4 points by sacado 5880 days ago | link

Just tried that, it works :

  (= _car car _cdr cdr)

  (def hasa (obj typ)
    (if (no typ)
      (and (type.obj (car typ)) (hasa obj (cdr typ)))))

  (def car (x)
    (if (acons x)
        (_car x)
      (hasa x scanner)
        (let tx type.x
          (annotate tx (tx!car rep.x)))
        (err "bad object for car")))

  ; define scanner type-class
  (= scanner '(car cdr))

  (= mycar [_ 0] mycdr [cut _ 1])
  ; Say s can answer to car and cdr
  (= fns (listtab `((car ,mycar) (cdr ,mycdr))))
  (= s (annotate fns "abcde"))
  (car s)
  -> #3(tagged #hash((cdr . #<procedure: mycdr>) (car . #<procedure: mycar>)) #\a)
  (car rep.s)
  -> #\a
It's a bit ugly, probably it could get a little better (the display is ugly, IMHO) but those wanting it could start doing funny things.


4 points by cchooper 5880 days ago | link

> Wow, I think you won the price of the longest post so far.

Well someone has to load-test this thing!

> And it is even a very clever one, actually.

Thanks :)

> having to use rep on annotated data is, I think, the biggest mistake of that type system.

This is exactly this problem that got me thinking about types. I've been tempted to create a few types with annotate already, but each time I stopped because I didn't want to have to reimplement every function to work on my new type. Each time, I found a different solution to the problem that didn't require new types, which started me thinking "hey, perhaps we don't need new types after all!"

But you're right that you'll always need new types eventually. The solution you suggested is, I think, the right one. It's a bit like the object system in ANSI Common Lisp (pg even used hash tables to store the object's methods!) but it uses annotate to associate methods with objects.

So I'll modify my position and say that you should avoid creating new types, but if you have to do it, duck typing is the way to go.


2 points by almkglor 5880 days ago | link

For completeness ^^

  (redef hasa (obj typ)
    (if (and (isa obj 'cons) (is typ scanner))


1 point by sacado 5880 days ago | link

You could read this about annotate et al. :

I particularily like that one : "I expect type names will ordinarily be symbols, but they don't have to be. Either argument can be of any type. I can't imagine why users would want to have type labels other than symbols, but I also can't see any reason to prevent it."


1 point by cchooper 5880 days ago | link

You know, the first thing I thought when I read that x years ago was "You could pass around a load of functions as the type to do polymorphism"! I wonder if everyone has the same thought.