Arc Forumnew | comments | leaders | submitlogin
New types in Arc?
2 points by thefifthman 6184 days ago | 18 comments
Hi,

I am/was from the interactive theorem proving community, and I like powerful languages :-) I used to program a lot in SML (Standard ML).

Now, what I miss from Lisp and Arc is the ability of defining "safe" types; I don't mind if they are dynamic...

For example, I would like to introduce a type "theorem", and then have operations on this type that ensure that I can create only "true" theorems. Like, say I have a theorem A saying "P", and another one, B, saying "P -> Q", then I can combine them with "(modus_ponens A B)", resulting in a theorem saying "Q".

Any idea of how this concept could be integrated in Arc ? I mean, one could certainly hard code such a type, but how do I define my own?

I don't know too much about Lisp, so maybe there is already a way of achieving it, I'd like to hear about this, too!



2 points by sacado 6184 days ago | link

Well, this is Lisp, so the solution is probably to use a list :)

First, you would implement theorems as a pair of lists : a list for preconditions, one for consequences. Thus, A would be coded as '(nil . (P)) (no precondition, one consequence, i.e. a fact) and B as '((P) . (Q)) (one condition, one consequence) :

  (= A '(nil . (P)))
  (= B '((P) . (Q)))
  (modus-ponens A B)
After that, a list called facts* would contain the elements P and Q :

  facts*
  ==> (P Q)
That's how you would do while prototyping. Of course, you will eventually want to encapsulate everything, with dedicated functions, for example :

  (= A (fact 'P))
  (= B (theorem '(P) '(Q)))
But now, you want to add a kind of strong typing, since you don't want to mistake a theorem from another list. To achieve this you can define a new type with the 'annotate function :

  (def fact (name)
    (annotate 'theorem (cons nil (list name))))

  (def theorem (pre post)
    (annotate 'theorem (cons pre post)))

  (= A (fact 'P))
  (= B (theorem '(P) '(Q)))
  (type A)
  ==> theorem

  (modus-ponens A (cons 'x 'y))
  ==> Type error
You now have opaque 'theorem objects. To access their implementation, just use the 'rep function :

  (rep A)
  ==> '(nil . (P))

-----

2 points by thefifthman 6183 days ago | link

It seems to me that anyone can now create theorem's, even if they are not theorems! What I had in mind is more like, ok, I have these 5 operations here on theorems, only these can modify and build new theorems. Then I can somehow close up the type of theorems, and if someone later wants to create a theorem, he has to go through these 5 operations. Here it seems to me that later one can just call (annotate 'theorem something_which_is_not_a_theorem) and get away with it.

-----

4 points by almkglor 6183 days ago | link

What you could do is, you could create an "opaque" function which gives access to your theorem data. Then wrap that function in an 'annotate form.

  (def theorem (f)
    (if (check-that-f-is-valid-theorem f)
        (annotate 'theorem
          (fn (cmd)
            (case cmd
              get
                f
              something-else
                (do-something-on f))))
        (error "Not a valid theorem")))
For that matter, since Arc is around "exploratory programming", in principle Arc will not allow anything that limits what the user can do.

In practice, most users are too busy working on stuff to bother doing something as stupid as (annotate 'theorem something_which_is_not_a_theorem) anyway.

In short: don't worry, be happy ^^

-----

1 point by thefifthman 6183 days ago | link

lol Exploratory programming is fine, but I don't need to explore anymore what a theorem is :-)

> "Arc will not allow anything that limits what the user can do"

Obviously this is not true, as Arc limits me such that I cannot create a safe theorem type. What this means is that I cannot let the user directly interact with Arc when building an interactive theorem prover; I have to wrap some kind of protective layer around it, which would probably hinder lots of the exploring!

-----

1 point by almkglor 6183 days ago | link

> lol Exploratory programming is fine, but I don't need to explore anymore what a theorem is :-)

Which is an admitted problem in current Arc: it forces you to explore well-explored areas sometimes.

> I have to wrap some kind of protective layer around it, which would probably hinder lots of the exploring!

Standard answer: macros.

As for safety: Read: http://paulgraham.com/langdes.html point 3.

-----

2 points by sacado 6183 days ago | link

Well, that's not really the spirit of the language. almkglor's solution with checks in the functions with 'check-that-f-is-valid-theorem is what you should eventually do.

If you really want a stronger encapsulation, there is only one solution (I think) : using closures to keep a list of known, certified theorems.

  (def theorems-factory ()
    (let known* nil
      (def mk-theorem (pre post)
        (let result (annotate 'theorem (cons pre post))
          (push result known*)
          result))
      (def is-valid (theorem)
        (mem theorem known*))
      (def modus-ponens (a b)
        (unless (and (is-valid a) (is-valid b))
           (err "Type error"))
        (do-the-work-with a b))))

  (theorems-factory)
  (= A (mk-theorem nil '(P)))
  (= B (mk-theorem '(P) '(Q)))
Now, once you call (theorems-factory), two things happen :

- the list of certified theorems is made (and empty)

- a bunch of functions (the only ones authorized to deal with your type) are defined.

Then, you can call the theorem constructor 'mk-theorem and other functions on these built constructors.

The trick here is that you can still create bogus theorem objects (for example by doing (annotate 'theorem 'bogus)), but as they are not added to the 'known* list, they are refused by all your functions. And the user cannot access the 'known* list, so he cannot cheat this way either.

-----

2 points by thefifthman 6183 days ago | link

Hmmh, I don't really think it is against the spirit of the language. After all, I think you cannot just annotate arbitrary things to be of type integer (or can you?).

The above solution would surely work in principle, but in practice I create millions of intermediate theorems, I do not want to store all of them!

-----

2 points by almkglor 6183 days ago | link

> Hmmh, I don't really think it is against the spirit of the language. After all, I think you cannot just annotate arbitrary things to be of type integer (or can you?).

Yes, you can: (annotate 'int #\newline). It won't be useful, but yes, you can.

> The above solution would surely work in principle, but in practice I create millions of intermediate theorems, I do not want to store all of them!

Standard answer: Garbage collection

In any case: you can always destructure the annotation wrapper and work with the raw data (probably by using some sort of higher-order function, like combinators), only wrapping it in the annotation type. Sort of like explicit monads.

-----

1 point by sacado 6183 days ago | link

Garbage collection does not work here if you keep a track of all the defined theorems in a list as 'known*. We would need weak references to achieve this.

-----

1 point by almkglor 6183 days ago | link

I'm not sure we need a list of defined theorems: apparently the proposed 'modus-ponens function is supposed to accept both source theorems anyway.

-----

1 point by sacado 6183 days ago | link

You're right. It creates lots of never-freed objects, so that's obviously not a viable solution.

What are you trying to prevent ? If you're trying to prevent the use of malicious data into one of your functions (e.g. a theorem object you got from an untrusted network) you could use some kind of cryptographic mark in your objects (something that proves they have been made by your constructor function, e.g. implement theorems as closures whose actual data can only be decyphered when called with a given private key), but Arc is certainly not the language you should uyse in this case (better use Java in such situations).

If, and that's what I guess you want, you just want to protect the user from blowing his own leg off by providing wrong data to functions, almkglor's wrapper solution, with macros to simplify coding, is the way to go. You can end up with a very transparent solution if you do it right. Of course, a determined user will eventually be able to break it down if he really wants to, but that's the philosophy of the language.

Look at the way macros are implemented : they are nothing more than functions annotated with the 'mac symbol. You can access to the underlying function if you want, by doing (rep while) or do (= mymac (annotate 'mac #\space)) and try to call (mymac). Sure, and then bad things can happen. But nobody does it, because this is senseless, and noone ever got trapped because of this. And having access to this is great because it makes the language simple and hackable.

-----

2 points by tokipin 6183 days ago | link

i'm not sure i understand, but there is obj and deftem:

http://arcfn.com/doc/table.html#obj http://arcfn.com/doc/template.html

macros are pretty good for language-type thingies since they let you parse the raw input. this could be done as an interface layer on top of the actual mechanisms so you could write things like:

  (logic A -> B
         A)
or whatever the notation is

-----

1 point by thefifthman 6183 days ago | link

Thx for all of your answers, it about confirmed what I thought is possible in Lisp/Arc and what not.

Why not introduce a safe type definition mechanism as follows:

(def_opaque_type MYTYPE mytype_operation_definition_1 mytype_operation_definition_2 .... mytype_operation_definition_n)

The idea is that I can use (annotate 'MTYPE ...) only in the mytype_operation_definitions, but nowhere else. def_opaque_type MYTYPE would also fail if (annote 'MYTYPE) has been called before.

-----

1 point by sacado 6183 days ago | link

Actually, you could probably do that (if you really want to) by overriding the 'annotate function and using a 'locked* list, so that you can only annotate an object with a type name that is not already in the locked* list :

  (redef annotate (type obj)
    (if (mem type locked*)
      (err "This type is locked")
      (old type obj)))
Here, 'old is a "keyword" of 'redef that calls the old version of 'annotate (the unprotected one, that cannot be accessed from outside anymore).

-----

2 points by almkglor 6182 days ago | link

And how do you unlock it for the constructors that are supposed to do the proper locking?

Hmm.

  (let real-annotate annotate
    (def theorem (f)
      (if (check-f-is-valid-theorem f)
          (real-annotate 'theorem f)
          (err "Not a valid theorem")))
    (= annotate 
       (fn (type obj)
         (if (is type 'theorem)
             (err "Spank!  Spank!  Under the boots of your master!!")
             (real-annotate tpye obj)))))
It does have the drawback that you can't, say, reload the file, but hey, you did say you didn't want anyone else to create theorems except by that method, so even redefining that method won't work!

-----

2 points by almkglor 6182 days ago | link

Okay, here's a better way of presenting it:

  (def-B&D-type theorem create-theorem (f)
    (if (check-f-is-valid-theorem f)
        (annotate 'theorem f)
        (err "Not a valid theorem")))
Where def-B&D-type is:

  (mac def-B&D-type (type constructor . body)
    (w/uniq real-annotate
      `(let ,real-annotate annotate
         (let annotate ,real-annotate
           (def ,constructor ,@body))
         (= annotate
            (fn (type obj)
              (if (is type ',type)
                  (err "Spank!  Spank!  Under the boots of your master!!")
                  (,real-annotate type obj)))))))
Note however that even under this, it's still possible to redefine 'check-f-is-valid-theorem.

For that matter, it's still possible to defeat "safety" in language X: just implement a version of language X which doesn't have that safety.

In practice, nobody will bother to do (annotate 'foo something-which-is-not-a-foo) anyway.

-----

1 point by thefifthman 6183 days ago | link

That sounds exactly like what I need. Thanks a lot!

-----

2 points by rincewind 6182 days ago | link

It seems like you want to re-create an idiom from another programming language in arc. What is the language you come from and how would the standard solution to your problem look in it?

-----