## PyFL Output: the magic of simple side effects [700 views]

As I’ve already explained I’ve invented and implemented an experimental functional language – PyFL (Python based Functional Language) – to try out some ideas.

For example, PyFL has a full set of Variable Binding Operators (VBOs) that use a linear ASCII form to represent the constructs like sigma notation of conventional mathematics. For example the following adds up the first 20 squares

``sumfor i in range(1,20) all i*i  end``

and the following tests if N is prime

``forall d in range(2,sqrt(N)): N mod d != 0 end``

In the previous post  I described the PyFL approach to input, which is (arguably) purely functional, drastically simpler, and trivial  to implement, as compared to Haskell’s cumbersome IO monad. (Btw I have nothing against monads or Haskell in general, I’m just not impressed by the Haskell IO monad).

This time I’ll talk about PyFL’s output scheme. It ticks two of the three boxes just mentioned – it’s simple and easily implemented. Here’s some output (yep, Pascal’s triangle). I’ll show the program at the end of this post.

Unfortunately I can’t claim that PyFL output is functional. It shamelessly causes a side effect. I just don’t know how to do better.

A simple interactive program will illustrate the point. In BC in Canada at one stage you had to be a certain (varying) age to book an appointment for a Covid shot. This PyFL program asks your name and age and tells you whether you’re eligible.

``````query = if age>40
then outputs(name^', you are eligible!')
else outputs(name^', sorry, you are not  eligible.')
fi;

When query is evaluated the program will prompt for your name, then, addressing you by name, prompt you for your age, then tell you whether or not you’re eligible. (The carat symbol stands in PyFL for string concatenation.)

The ‘function’ outputs when evaluated prints its argument, which is normally a string. This is a blatant side effect. We can debate whether input invokes a side effect, too, but the important point is that they are easy to use and implement. And since invocations take the form of function calls they can be embedded in PyFL constructs like if-then-else-fi and VBOs.

The arguments to input and outputs can be any expressions. They don’t have to be string constants. In particular they can involve previous input values, as  above.

There is also a more primitive output ‘function’ namely output, but outputs is usually preferred because it strips the the string quotes off its string argument, as does input.

The outputs pseudo function can be called embedded in PyFL constructs like VBOs, lambda expressions, if-then-else-fi’s and so on. Here is a program that prints a table of the first 10 squares and cubes:

``````res = concatfor i in range(1,10) all
outputs(i)      && outputs(' ')   &&
outputs(i*i)    && outputs(' ')   &&
outputs(i*i*i)  && outputs('\n')  &&
''
end;``````

(The operator && evaluates the first argument for its side effects, and discards the result.)

The result isn’t that great. We don’t  have columns lined up. Fortunately we can do better. One of the advantage of using Python as the implementation language is that we have access to all its features in one way or another. In particular Python has a rather sophisticated output formatting system based on format strings with ‘holes’ for data. In PyFL you use another output pseudo function outputf. The first argument is a format string with holes, and the remaining arguments are data items that will be slotted into the holes. Format strings are well documented and what works for us is the string ‘{:5d} {:5d} {:5d}\n’.

``````res = concatfor i in range(1,10)) all
outputf('{:5d} {:5d} {:5d}\n',i,i*i,i*i*i) && ''
end;``````

The result is much better.  Now the numbers are lined up in columns five characters wide.

The Python formatting strings can naturally handle floating point numbers as well. Suppose we want to print a table of square and cube roots of the first ten whole numbers but only want to display four significant digits. Here is the complete program

``````res = concatfor i in range(1,10) all
outputf('{:4d} {:8.4f} {:8.4f}\n',
i,sqrt(i),i**(1/3)) && ''
end;``````

and here is its output (I’ll deal with the empty string at the bottom later).  There’s no need here to go into the details of Python format strings because they are well documented elsewhere.

Printing numbers in columns is one thing but producing a triangle is a bit harder – there’s nothing built-in to the Python formatting strings that would cover that. Instead we have to use the computational power of PyFL to generate appropriate side effects.

Here is the complete program

``````scc(L) =
while tl(M) != []
M = L <> [0] fby tl(M);
k = hd(M);
pk = 0 fby k;
n =  k+pk;
N = [] fby n :: N;
result = n :: N;
end
;
tri =
while i<18
L = [1] fby scc(L);
i = 1 fby outputs(sp(35-( width(L) div 2))) &&
outputs(strlist(L))               &&
outputs('\n')                     &&
i+1;
result = L && '';
end
;

sp(j) = concatfor i  in range(1,j) all ' ' end; // j spaces

width(L) = sumfor h in L all w(h)+1 end -1;

strlist(L) = concatfor h in L all str(h)^' ' end;

w(h) = 1 + (log10(h) div 1);
``````

The first definition, of the function scc, defines a computation that generates the line of the triangle that succeeds a given line, both represented as lists. Thus scc([1]) is [1 1], scc([1 1])  is [1 2 1],  scc([1 2 1]) is [1 3 3 1], and so on. Yes, that’s a while loop – I’ll explain in the next post, but let me assure you in the mean time that’s all legit, no side effects.

The next definition, of tri, is another while loop. It repeatedly applies scc starting with [1] and generates 18 lines of the triangle. It converts these lists into strings and calculates their width. It reduces the left hand spacing as the lines grow wider.

I’m sure that by now you believe that as far as the terminal goes, PyFL can do anything reasonable (output strings can contain control characters and escape sequences).

However I’ve acquired a didactic debt, namely while (and other!)  loops. I have some ‘splaining to do. Next  post.