% fiml Bienvenue dans FIML-light, Version 0.4 ! An interpreter by J. Garrigue, April 1994. #The "#" is the prompt. This may recall you of the CAML system.
#1 ;; it : int = 1 #it+1 ;; it : int = 2 #<1,"a",true> ;; it : <int, string, bool> = <1, "a", true>Like in CAML again, you end a query with ";;". Basically you evaluate expressions. The answer is of the form name : type = value, where name is the identifier defined in the process. By default this is it, and you can use it afterwards, as shown by the second query.
#\x; x ;; it : <'a> -o 'a = <fun> #\x; x+1 ;; it : <int> -o int = <fun> #it 3 ;; it : int = 4 #\x,y; x+y ;; it : <int, int> -o int = <fun> #it 1;; it : <int> -o int = <fun>Functions are first class values, and they are polymorphically typed. Abstraction part starts with a "\", and end with a composing ";". Notice the flat typing for curried functions.
#let x = 3 and y = "a";; y : string = "a" x : int = 3 #let I x = x;; I : <'a> -o 'a =Use let and let rec to introduce respectively non-recursive and recursive definitions. Combined with in the definition is local.#let rec fact n = if (n=0) then:1 else:(n * fact (n-1));; fact : <int> -o int = <fun> #let x = 4 in x ;; it : int = 4
#fact 3;; it : int = 6 #<3;fact>;; it : int = 6 #/3,5; \x,y; y-x ;; it : int = 2 #/I; \x; <x 1,x "a"> ;; it : <int, string> = <1, "a">Of course application can be done with the usual notation, but one may write it postfix, using either of the stream notations. <x;F> is close to an usual mathematical notation for F(x), while "/x;F" puts the emphasis on the abstraction vs. application symmetry. The last example shows than lambda-abstraction in context keeps polymorphism.
#let me = <name:"Garrigue", age:22> ;; me : <age:int, name:string> = <age:22, name:"Garrigue"> #me.age ;; it : int = 22 #me ; <surname:"Jacques"> ;; it : <age:int, name:string, surname:string> = <age:22, name:"Garrigue", surname:"Jacques"> #me ; \age:x; /age:x+1 ;; it : <age:int, name:string> = <age:23, name:"Garrigue">What you may have thought were tuple in the beginning, are in fact streams. These are records with compositional properties. You may select a field, add a new one, or modify it by composing transformations.
#\x; /x+1 ;; it : <int> -o <int> = <fun> #\x,y; /y,x ;; it : <'a, 'b> -o <'b, 'a> = <fun> #\a:x; /b:x ;; it : <a:'a> -o <b:'a> = <fun> #\p:_ ;; it : <p:'a> -o <> = <fun>Any function returning a stream may be viewed as a transformation. They may modify streams both in their values (eg. age above), and in their structure: switching, change in labels, erasure.
#<1:1,3:2> o <1:3,2:4,4:5> ;; it : <1:int, 2:int, 3:int, 4:int, 6:int> = <1:1, 2:3, 3:2, 4:4, 6:5> #it; \1:_,3:_ ;; it : <1:int, 2:int, 4:int> = <1:3, 2:4, 4:5> #<a:4,b+1:"b+1"> o <a:true,b:"b">;; it : <a:int, a+1:bool, b:string, b+1:string> = <a:4, a+1:true, b:"b", b+1:"b+1"> #it; \a:_,b:_;; it : <a:bool, b:string> = <a:true, b:"b+1">In the transformation calculus, application and abstraction are translated into stream composition and matching. Remark how indexes changes when streams are composed: a value with label n appears at the nth unocuppied position of the stream it is added to. Matching as a symmetrical effect. This applies to named labels too.
##dialogue true;; ##set <2,3> ;; it : <int, int> = <2, 3> #/a:1 ;; it : <int, int, a:int> = <2, 3, a:1>#dialogue and #set are two pragmas that respectively set the dialogue mode, and the value of the current stream (it!). In dialogue mode, when you enter an expression, it is automatically composed with the current stream, as if you had entered it; E ;;.
#let tadd x y = /x+y and tsub x y = /x-y;; tsub : <int, int> -o <int> = <fun> tadd : <int, int> -o <int> = <fun> ##set /;; it : <> = <> #/3,4;; it : <int, int> = <3, 4> #tadd;; it : <int> = <7> #/4;; it : <int, int> = <4, 7> #tsub;; it : <int> = <(-3)>In dialogue mode FIML works exactly like a stack machine. You may put values on the stack through "/", and modify it using transformations.
#let dup x = /x,x and switch x y = /y,x ;; switch : <'a, 'b> -o <'b, 'a> = <fun> dup : <'a> -o <'a, 'a> = <fun> #let switch' 2:x = /x and rot 3:x = /x ;; rot : <3:'a> -o <'a> = <fun> switch' : <2:'a> -o <'a> = <fun> #/1,2; switch ;; it : <int, int, int> = <2, 1, -3> #switch' ;; it : <int, int, int> = <1, 2, -3> #rot ;; it : <int, int, int> = <-3, 1, 2>Basic stack operations are easily defined in terms of transformations. Remark that, thanks to numeric indexes, we can simplify switch into switch', or naturally define rot as extracting the third element to put it in the first position.
##set /;; it : <> = <> #put "Name: ";; it : <#std:<>> -o <#std:<>> = <fun> #get;; it : <#std:<>> -o <string, #std:<>> = <fun> #\x; put("Hello " ^ x ^ "!\n") ;; it : <#std:<>> -o <#std:<>> = <fun> ##set open_std; it;; Name: Jacques Hello Jacques! it : <#std:<>> = <#std:<>>We are still in dialogue mode. We progressively construct a function using I/Os by composing transformation. Since they use the hidden label #std, it must finally be composed with open_std to execute.
#type ['a;'b]graph = L <'a> | N <'b ref,'b ref>;; Type graph with constructors: L : <'a> -o ['a; 'b]graph N : <'b ref, 'b ref> -o ['a; 'b]graph ##set open_a dummy;; it : <a:['a; <a:<>>]pool> = <a:<>> #a <- L 1;; it : <<a:<>> ref, a:[[int; 'a]graph; <a:<>>]pool> = <<1>, a:<>> #let x = it.1;; x : <a:<>> ref = <1> #a <- N x x;; it : <<a:<>> ref, <a:<>> ref, a:[[int; <a:<>>]graph;<a:<>>]pool> = <<2>, <1>, a:<>> #a ! x <- L 2 ;; it : <<a:<>> ref, <a:<>> ref, a:[[int; <a:<>>]graph;<a:<>>]pool> = <<2>, <1>, a:<>> #a ! x ;; it : <[int; <a:<>>]graph, [int; <a:<>>]graph, <a:<>> ref, a:[[int; <a:<>>]graph; <a:<>>]pool> = <(L 2), (N <1> <1>), <1>, a:<>>Pooled references are a way to integrate dynamically created objects in the type framework of FIML. The abstract type pool takes two arguments: the type of the values it "contains", and a marker for generating references. At creation time (#set open_a dummy), only the second one is fixed, the type for values being polymorphic. After that type is synthesized in the usual way.
##dialogue false;; #2;; it : int = 2 #it+1;; it : int = 3Dialogue mode is a good way to experiment with transformations, but you can go back to usual program writing by the pragma #dialogue false.
#quit <> ;; A tres bientot... %The official way to quit FIML is the quit function.
You have certainly noticed the very experimental status of this interpreter. There are - purposedly - holes in the type system, to facilitate a toy use. So, if you have a core dump, this is probably not a bug! For more details, see the manual. For any question or comment, feel free to contact me at garrigue@is.s.u-tokyo.ac.jp.