GUI elements in Fluent are written in an implementation of the Scheme programming language. To load a Scheme file into Fluent, click File, select the Read submenu, and select Scheme.... Scheme statements are written in parenthesis, and statements are often nested within other statements. Comment lines in Scheme begin with a semi-colon, and variable names and Scheme commands are not case sensitive. In this section, you will learn about some basic Scheme statements and how you can use them as you create a GUI for your UDF. For a more complete guide to the Scheme language, visit http://www.scheme.com/tspl4/.
Scheme allows for the use of a number of important data types. Those provided in this section are the types that Fluent allows you to pass to your user defined functions.
(define isBool #t) (set! isBool #f) (display isBool) (boolean? isBool)
In Scheme, booleans are initialized using the notation
#t
for true and#f
for false.In the example above, a new variable
isBool
is being initialized to true and then being set to false.The
display
command is a console printing method that allows you to check the value of a variable. The line(display isBool)
would output#f
.The
boolean?
command is used to check if the following argument is a Boolean or not. The line(boolean? isBool)
would output#t
.
(define isInt 1) (set! isInt (+ isInt 1)) (display isInt) (integer? isInt) (number? isInt)
Integers in Scheme are similar to integers in most other languages.
In the example above, the new variable
isInt
is initialized to1
.In the next line, it is incremented to
2
. Remember that in Scheme the operation always comes before the data elements being operated on.Using the
display
command, we can check that the value ofisInt
is indeed2
.The
integer?
command will check to see ifisInt
is an integer and thenumber?
command will check to see if it is one of any of the number data types in Scheme. The output of both of these commands will be#t
.
(define isReal 1.2) (set! isReal (- isReal .5)) (display isReal) (real? isReal) (number? isReal)
Real numbers in Scheme work in a similar way as the integers.
In the example above, the new variable
isReal
is initialized to1.2
.In the next line, it is set to its own value minus
0.5
. Remember that in Scheme the operation always comes before the data elements being operated on.The
display
command allows us to verify that the value ofisReal
is0.7
.The commands
real?
andnumber?
will both return#t
.
(define isChar #\a) (set! isChar #\b) (display isChar) (char? isChar)
Characters in Scheme are always preceded by the notation
#\
.In the example above the variable
isChar
is initialized to the valuea
. In the next line, it is set instead to the value ofb
.By using the
display
command, we can check that the value ofisChar
is indeedb
.The result of the
char?
command will be#t.
Note: Although the example above proves that variables can be set to character values, you will usually just be working the character itself, as it makes little sense to assign a variable for a data type that is only 1 character.
(define isString "This is a string") (set! isString "This is a different string") (display isString) (string? isString) (string-length isString) (string-ref isString 0) (set! isString (string-append isString " plus more"))
Strings in Scheme are very similar to strings in most other languages.
In the example above, the variable
isString
is initialized to"This is a string"
. In the next line, it is set instead to the value"This is a different string"
.Using the
display
command, we can confirm that the value ofisString
is"This is a different string"
.The
string?
command will confirm thatisString
is a string by outputting#t
.The
string-length
command will return the number of characters in the string, in this case26
, and thestring-ref
command will return the character in the string at the position of the integer passed to it. In this case it would return the character at position 0, or#\T
.The
string-append
command adds two strings together and returns the result as a third string. The example above uses astring-append
command to combineisString
with" plus more"
. It also uses aset!
command to save the result back to the variableisString
. The new value ofisString
would now be"This is a different string plus more"
.
Note: While the string-ref
command
asserts that a string starts a position 0, the string-length
command will always treat strings as though they start at position
1. This makes sense, as a string with a string-length
of 1 has one character at position 0.
(define isSymbol 'abc) (symbol? isSymbol) (define isString "abc") (eq? (string->symbol isString) isSymbol)
Symbols are atomic values that are somewhat similar to strings. Symbols and strings could be used for many of the same purposes, but there are a few important differences that give symbols their own niche in Scheme.
Multiple variable names that refer to the same symbol will be considered the same object, meaning that when using the
eq?
function on two different variables that refer to the same symbol content, you will get#t
instead of#f
like you would with strings.This is seen in the example above. A symbol,
isSymbol
, is initialized toabc
. Thesymbol?
command confirms that the variable is a symbol. Then, a stringisString
is initialized to the same value,abc
. The following line illustrates two important points. First, any string can be cast as a symbol using thestring->symbol
command. Second, any two symbols with the same contents are considered to be the same object and are equal. In this case theeq?
statement would return#t
.Additionally, symbols are immutable, meaning that they cannot be altered once they are created. Strings on the other hand are mutable, meaning that they can be altered, for example, using
string-append
(see Strings).This shows that while strings have more versatility as a data type, symbols are a lightweight and easily comparable data type that is preferable when you know that you will not need to make any alterations once you have created it. As a result, symbols are seen very often in Scheme code in places where strings simply are not necessary.
(a . b) (define aPair (cons 1 2)) (a . (b . (c . ()))) (define aList (list 1 2 3 4 5)) (car aList) (cdr aList) (list-ref aList 2) (list-tail aList 1)
Pairs and lists are two related Scheme data constructs that are often used when multiple data items need to be stored together.
Pairs are two data items that can be accessed from the same variable name. Pairs are often represented as two data items with a period between them, such as the first line in the example above. Pairs are created using the
cons
statement, as seen in the second line of the example above.Pairs can be used to create lists. Lists are pairs whose second element is another pair. This makes your list as long as you need it to be by having the right number of appended pairs.
The third line of the example above shows how a list looks conceptually, made up of individual pairs. The fourth line in the example above shows you how to create a list using the
list
statement.The rest of the example above shows how to access certain pieces of data within the list. The
car
statement is used to access the first item in the list. In the example above, it would return1
. Thecdr
statement is used to access everything but the first item in the list. In the example above, it would return(2 3 4 5)
.The
list-ref
statement is used to access an item in a list by the position it is within the list. Thelist-ref
statement in the example above would return3
. Thelist-tail
statement is used to return the remaining items in the list starting at the position you tell it to. In the example above, since it is passed position1
as a starting point, thelist-tail
statement would return(2 3 4 5)
.
There are a number of Scheme concepts that are important to understand when creating your own Fluent interface. This section discusses those concepts that you are most likely to see in examples throughout this guide and use as you build your own interface.
(define isInt 1) (define whoKnows)
The
define
statement is used to create a new variable in Scheme.Variables in Scheme do not have types, but rather return a value. The return values themselves have types, but using the variable name itself is like calling a function that can return a value of any type. For example, the variable
isInt
in the example above returns the integer1
.Scheme variables do not have to be initialized with a value when they are created. In the example above the variable
whoKnows
is empty and can be assigned to a value of any type.
(define someVariable) (set! someVariable 1) (set! someVariable 2.5)
The
set!
statement is used to assign a value to a variable that has already been created. If the variable has not been created via adefine
statement you will receive anunbound variable
error.Since there are no variable types in Scheme, a variable can be overwritten with multiple types of data. In the example above, the variable
someVariable
is initially empty.After the first
set!
statement,someVariable
will return the integer value of1
. After the secondset!
statement however,someVariable
will return thereal
value of2.5
instead.This flexibility in variable use has its advantages, as it is easy to create a variable and begin working with it immediately. It can also be a problem if you aren’t diligent in keeping track of each variable’s return type.
(let ((x 5)) (let ((x 2) (y x)) (+ y x)))
let
statements define the scope of variable bindings that occur within them. They are similar to { } in C and C++.The
let
statement is broken up into two parts. The first part begins with the parenthesis immediately following the wordlet
. Within this set of parenthesis are the variable bindings that you want to define for the rest of thelet
statement.For the outer
let
in the example above, the first part consists of the variable binding(x 5)
. For the innerlet
of the example above the first part consists of the variable bindings(x 2)
and(y x)
.The second part of the
let
statement is known as the body. Once the variable bindings are complete, this is where the operations using these variables occurs.For the outer
let
in the example above, the body consists of everything that follows the first part, including the innerlet
, until the final parenthesis. For the innerlet
in the example above, the body includes the statement(+ y x)
.When
lets
are nested, as in the example above, and both the outer and innerlet
bind a value to the same variable, the value bound by the outerlet
is the one used until the first part of the innerlet
is closed.Therefore, the
(+ y x)
statement in the example above will use the value2
forx
as opposed to5
. The value fory
in this statement, however, will still be5
because wheny
was assigned the value ofx
, it didn’t see thatx
had been changed to2
because the first part of the inner let wasn’t closed yet. This makes the result of the addition7
.
(define doubleProcedure (lambda (x) (+ x x))) (doubleProcedure 1)
The
lambda
keyword is used to create a new Scheme procedure.When using the
lambda
keyword to make a new procedure, you must first provide a list of the variables that will be used in the procedure. Once the variables are declared, you can then specify the functionality of the procedure.This process is seen in the example above. Once the keyword
lambda
is used to makedoubleProcedure
a procedure, the variablex
is declared. Sincex
is the only variable being used in this procedure, we can now move on to the functionality, which consists of the(+ x x)
portion of the line.Now that the procedure has been created, we can call it using any number for
x
, as seen in the second line of the example above, which will return2
.
(define ifExample (lambda (x) (if (>= x 0) (+ x 1) (- x 1)))) (ifExample 1) (ifExample -1)
The Scheme
if
statement has the same basic logic asif
statements in most other programming languages, though the syntax is slightly different.In Scheme, the
if
statement is made up of three parts. The first part is thetest
, which determines how you will proceed through the rest of theif
code.The second part is the
consequent
, which is the code block executed if thetest
is true.The third part is the
alternative
, which corresponds to anelse
statement in other popular languages. This code block is executed if thetest
is false.In the example above, a procedure called
ifExample
is created to show how theif
statement works. For information about creating procedures in Scheme, see Lambda.In the body of the procedure is a simple
if
statement. Thetest
in this example is(>= x 0)
, theconsequent
is(+ x 1)
, and thealternative
is(- x 1)
.When passed a number, the
ifExample
procedure will increment the number if it is greater than or equal to 0 and decrement it if it is a negative number. Therefore, the function call(ifExample 1)
will return2
and the function call(ifExample -1)
will return-2
.
(define halve (lambda (x) (/ x 2))) (map halve (list 2 4 6 8 10))
The Scheme
map
statement applies a desired function to each element of a list. For more information on lists, see Pairs and Lists.The example above uses a function called
halve
. For more information on creating functions with alambda
statement, see Lambda.In the example above, the function
halve
is created to divide any number passed to it by 2.In the second line, a map statement is used to apply the
halve
function to each number of the list(list 2 4 6 8 10)
. This map statement returns(1 2 3 4 5)
.The map statement can be used with essentially any function as long as that function is compatible with the list items on which it will operate.