1. I am currently taking a course succinctly named "Computation and Deduction". In reality, this is the secret name for "Immense fun at graduate PL courses". The whole idea of the course is to get acquainted with the theorem representation system Twelf. Based upon the LF-theory, the idea is that one can represent programming languages and their property proofs in a way that ensures correctness. Computer Science proofs tend to be shallow. Usually, one has to run induction over something and most of the proof is just simple slavery to an operational semantics and its inversion rules. Hence, the proof system can usually be kept simple. Twelf is different from traditional proof assistants and theorem provers like Isabelle/HOL and Coq. The latter 2 systems has proof automation support in which the system can automate big parts of the proof. It tends to make easy work on all the boring cases. As it stands, the stable version of Twelf support representation only: You must explicitly write down your proof. However, this turns out to be fairly simple. Further, there is nothing in Twelf which disallows one to add proof automation to the system later. While I do not claim to be a master of Twelf at all, it is not that hard to wrap your head around. Consider this to be an overview of what Twelf is able to do for you. Many parts need further explanation than I provide here, but that may spark the curious mind to dig deeper into the world of Theorem Proving! Also take note that I may have errors in here. Hopefully it will be the case that further proof development will eliminate many of these. Here, we will develop a lambda calculus interpreter, curry style, extended with peano arithmetic (Since I want to add a type system later). What makes the lambda calculus so interesting as a model for computation? Turing completeness, simplicity and basis for functional languages ought to be compelling reasons and enough justification! In Twelf, one begins by declaring exp : type. which reads "exp is a type". "type" here is really the kind (*) symbol in disguise. Then we declare how lambda calculus expressions look syntactically: z : exp. s : exp -> exp. app : exp -> exp -> exp. lam : (exp -> exp) -> exp. Thats it! The symbol 'z' plays the role of zero. It is an expression. The symbol 's' is the successor operator, so when it is used one types 's E' for some expression E which we want the successor of. Hence it will have type 'exp -> exp'. Lambda application is 'app E1 E2'; our encoding of the classical '(E1 E2)' application. So when fed with 2 expressions, E1 and E2 , app will itself be an expression. Finally, the lambda abstraction. It is written as 'lam E' where E is of type 'exp -> exp'. One may wonder where the variables are hidden. The answer, curiously, is that we are using variables in Twelf to represent variables in our lambda calculus. A Lambda Calculus variable 'x' is just 'x' as a Twelf variable. This is clever since we don't have to define a set of variables and then use them in the language as one would have done in Coq. Further, it will become clear that we can get substitution for free by "borrowing" it from the Twelf host language. Again a case where we would had described it directly in Coq. We really do not need it right now, but it may be beneficial to declare what values are in our language. Values are computations which are finished and in due time, we don't want every possible stuck computation to be values. So let us be explicit and define values:
    value : exp -> type.
    
    value_z : value z.
    value_s : value (s S)
               <- value S. 
    value_lam : value (lam [x] E x).
    
    We are declaring that 'value' is of type 'exp -> type'. This means that value is a type family, indexed by expressions. We can then go on and define the members of the type family. The 'value_z' symbol defines that 'z' is a value. The next rule uses an inductive argument: '(s S)' is a value under the assumption that 'S' is a value. Since we are using a capital S, Twelf's type checker will do its magic and infer the type of S. In Twelf variables in uppercase is free in the LF-term and is then treated universially by the system. The rules 'value_z' and 'value_s' together defines a system making all natural numbers into values. We finally address that lambda expressions are values. The notation '[x] E x' is a Twelf-lambda abstraction. in Haskell one would have written something like '\x -> E x' in SML it would have been 'fn x => E x'. This leaves us with the last leg of the journey, namely the semantics of the lambda calculus. The rules are defined as:
    eval : exp -> exp -> type.
    %mode eval +E -V.
    
    eval_z : eval z z.
    eval_s : eval (s S) (s S')
         <- eval S S'.
    eval_app : eval (app E1 E2) V
         <- eval E1 (lam E1')
         <- eval E2 V'
         <- eval (E1' V') V.
    eval_lam : eval (lam [x] E x) (lam [x] E x).
    
    We define a relation on evaluations 'eval E V' where the pair (E, V) is in the relation if the expression E evaluates to V. The '%mode' line is mode information known from logic programming. We define that the first argument is an input and the second argument is an output. This restricts the way the relation is allowed to be used. Twelf will carry out a mode check, ensuring that our use of 'eval' is in accordance with this mode definition. While this is mostly used to ensure certain relations are total, we use it here as an extra check of sanity. If your rules doesn't even pass the mode check, something is definitely wrong. I had a typo originally and the mode check found that immediately since some capitalized free variables were not ground. The symbol 'z' evaluates to itself. In 'eval_s' we call inductively -- if we have '(s S)' and S evaluates to S', then we have (s S'). Lambda abstractions in the last line evaluate to themselves as well. This leaves us with the 'app' case. If E1 evaluates to a lambda abstraction '(lam E1')', E2 evaluates to V', and (E1' V') evaluates to V, then (app E1 E2) evaluates to V. NOTE: The cool thing here is that E1' is of type 'exp -> exp' so we can just apply V'. This is substitution for free! There is no need to define a separate substitution rule because we just borrow it from Twelf. At this point we can begin to play with our toy: add1 : exp = lam [x:exp] (s x). one = s z. two = s one. three = s two. These are straightforward definitions. One can then run queries prolog-style to find solutions to problems. In effect, we have a free interpreter for our language: %query 1 5 eval (app add1 one) V. Twelf outputs 'V = s (s z).' as expected. We have a lot of rough edges in the toy interpreter however, and we may even have bugs in it. But we will weed them out over the coming postings, when we begin to add proofs on top of the system. At the Twelf Website is a lot of tutorials written by the Masters. If this is interesting to you, it is recommended reading!
    0

    Add a comment

Blog Archive
About Me
About Me
What this is about
What this is about
I am jlouis. Pro Erlang programmer. I hack Agda, Coq, Twelf, Erlang, Haskell, and (Oca/S)ML. I sometimes write blog posts. I enjoy beer and whisky. I have a rather kinky mind. I also frag people in Quake.
Popular Posts
Popular Posts
  • On Curiosity and its software I cannot help but speculate on how the software on the Curiosity rover has been constructed. We know that m...
  • In this, I describe why Erlang is different from most other language runtimes. I also describe why it often forgoes throughput for lower la...
  • Haskell vs. Erlang Since I wrote a bittorrent client in both Erlang and Haskell, etorrent and combinatorrent respectively, I decided to put ...
  • A response to “Erlang - overhyped or underestimated” There is a blog post about Erlang which recently cropped up. It is well written and pu...
  • The reason this blog is not getting too many updates is due to me posting over on medium.com for the time. You can find me over there at thi...
  • On using Acme as a day-to-day text editor I've been using the Acme text editor from Plan9Port as my standard text editor for about 9 m...
  • On Erlang, State and Crashes There are two things which are ubiquitous in Erlang: A Process has an internal state. When the process crashes,...
  • When a dog owner wants to train his dog, the procedure is well-known and quite simple. The owner runs two loops: one of positive feedback an...
  • This post is all about parallel computation from a very high level view. I claim Erlang is not a parallel language in particular . It is not...
  • Erlangs message passing In the programming language Erlang[0], there are functionality to pass messages between processes. This feature is...
Loading
Dynamic Views theme. Powered by Blogger. Report Abuse.