A Sample FIML Session

Starting. New notations. Dialogue. Imperative features. Final remarks.

The best way to start with a programming language has always been a guided tour.

1. Starting with it

Simple and classical things: the toplevel of a functional programming language.

Into FIML

% fiml
Bienvenue dans FIML-light, Version 0.42 !
An interpreter by J. Garrigue, April 1995.

#
The "#" is the prompt. This may recall you of the CAML system.

Some values

#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.

Functions

#\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.

Definitions

#let x = 3 and y = "a";;
y : string = "a"
x : int = 3
#let I x = x;;
I : <'a> -o 'a = 
#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
Use let and let rec to introduce respectively non-recursive and recursive definitions. Combined with in the definition is local.

2. New notations

Introducing fundamental concepts of the transformation calculus.

Application

#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.

Streams

#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.

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.

Composition and matching

#<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.

3. The dialogue mode

Not used to transformation composition? In this part we will use the dialogue mode to compose everything.

Entering dialogue mode

##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 ;;.

A stack machine in your lambda-calculus

#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.

Some classical definitions

#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.

4. Imperative features

Using transformations permits to write functional programs in an imperative style. This becomes even more interesting when functional evaluation is combined with imperative features.

I/O's

##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 new sync std in 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.

Pooled references

#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
#new pool a;;
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.

Quiting dialogue mode

##dialogue false;;
#2;;
it : int = 2
#it+1;;
it : int = 3
Dialogue mode is a good way to experiment with transformations, but you can go back to usual program writing by the pragma #dialogue false.

5. Final remarks

#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.


JG 94.6.10