Computer Science Canada

Turing as a Functional Programming Language

Author:  Cervantes [ Sun May 21, 2006 9:53 pm ]
Post subject:  Turing as a Functional Programming Language

Turing as a Functional Programming Language

(Partly.)


What is a functional programming language?

    Let's got to wikipedia on that one.
    Wikipedia wrote:

    Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions. Functional programming emphasizes the definition of functions rather than the implementation of state machines, in contrast to procedural programming, which emphasizes the execution of sequential commands. A purely functional program does not use mutation: rather than modifying state to produce values (as is done in imperative programming), it constructs new values from (but does not overwrite) existing values.

    There is no uniform agreement on what constitutes functional programming or a functional programming language. Often considered important are first-class functions (including anonymous functions), closures, and recursion. Other common features of functional programming languages are continuations, Hindley-Milner type inference systems, non-strict evaluation (i.e. "laziness") or explicit suspensions, and monads.

    If you survived that, bravo! Now I'll restate the important points (for us).

    Functional programming is a programming paradigm that often thought as opposite to procedural programming. Functions in a functional language may appear similar to functions in other languages, such as Turing; however, functions in a functional language are much closer to the mathematical definition of a function. That is, a function takes one value and returns another value, based on the given value. For example, f(x) = x^2 is a function that defines a parabola.

    Functions in a functional language can still take multiple parameters, however. But they work a little differently. Let's take the equation of a plane, f(x, y) = x + y. If x is the x axis, y is the y axis, and f(x, y) is the z axis, you get a plane that contains the lines z = x and z = y. Now, in terms of a functional language, this function is taking two parameters and that's not good. This is allowed, but what really goes on behind the scenes is this function that takes two parameters is turned into a function that takes one parameter. This function that takes one parameter returns another function that takes one parameter. This new function that takes one parameter, finally, returns our value.

    I said something funny there, didn't I? I said that our function returned another function. How can a function return a function? This is possible because functions in a functional language are first-class values, just like integers or strings. This means that our original function can -- instead of returning a real number -- return another function, because functions are first-class values.

    Functions as first-class values raises two important points.

    1. Functions can be anonymous. We don't need to give every function we make a name.
    2. Functions can be passed to other functions as values. This is really what this tutorial is about.



Functions as parameters

    Consider the task of finding a root of a function. [A 'root' of the function f(x) is the x value for which f(x) = 0] We can easily program our computer to find a root of a given function by iterating through all values of x until f(x) = 0. Let's do just that, given the function f(x) = x^2 - 4. [If you're quick, you'll already know the roots of that parabola are -2 and +2]

    code:

    function findRoot (low, high, incriment : real) : real
        % Find the first root of the function f(x) = x^2 - 4
        % starting from 'low' and ending at 'high'.
        var x := low
        loop
            exit when x > high
            % The rounding here is to avoid things like
            % 0.000000000023345 not equalling 0
            if round ((x ** 2 - 4) / incriment) = 0 then
                result x
            end if
            x += incriment
        end loop
        % No root was found for the function f(x) = x^2 - 4
        % between x = low and x = high
        % Return a signal that no root was found.
        result minint
    end findRoot

    put "The first root that I found between -10 and 10 for the function f(x) = x**2 - 4 is: ", findRoot (-10, 10, 0.1)


    But we can do better. Our 'findRoot' function isn't very useful if it only deals with one hardcoded function, f(x) = x^2 - 4. To make it really useful, our 'findRoot' function should take another function as a parameter. This parameter will be the function whose root will be found.
    Here we go. I'm really going to do it. Passing functions as parameters. Just let me put on my wizard robe and pick up my magic wand.
    I'll bet some of you have been reading this and thinking none of it will actually be possible in Turing. Never say "never".
    code:

    function findRoot (f : function f (x : real) : real, low, high, incriment : real) : real
        % Find the first root of the function f(x)
        % starting from 'low' and ending at 'high'.
        var x := low
        loop
            exit when x > high
            % The rounding here is to avoid things like
            % 0.000000000023345 not equalling 0
            if round (f (x) / incriment) = 0 then
                result x
            end if
            x += incriment
        end loop
        % No root was found for the function f(x) = x^2 - 4
        % between x = low and x = high
        % Return a signal that no root was found.
        result minint
    end findRoot

    function f (x : real) : real
        result x ** 2 - 4
    end f
    put "The first root that I found between -10 and 10 for the function f(x) = x**2 - 4 is: ", findRoot (f, -10, 10, 0.1)

    Press F1. ... Success!

    There you have it. Functions in Turing can be passed as parameters.

    Now some notes:

    1. The function parameter in our 'findRoot' function is 'f'. That is, our function is called 'f'. This does not mean that the parameter type -- function f (x : real) : real -- has to use 'f' as the function name. The following is also valid, for example:
      code:
      function findRoot (f : function g (x : real) : real, low, high, incriment : real) : real

    2. There is an alternative syntax to do this. It's essentially the same, but without the initial 'f :' in 'f : function ...'. For example,
      code:
      function findRoot (function f (x : real) : real, low, high, incriment : real) : real

    3. To the best of my knowledge (which, with regard to functionalism in Turing, has been put together in one night), we cannot anonymously create functions. That is, we must define our function f(x) as per usual before calling our 'findRoot' function, then pass f(x) into 'findRoot'.



Another (more useful) example to digest

    Who likes Calculus? I know I do. To that end, I made a very brief 'Calculus' module. It defines two functions: one to find the slope of a given function at a given x value, and one to find the area "under the curve" of the given function between the given x values.

    [To those who are not familiar with Calculus, these are the two problems Calculus is concerned with: finding the slope of a function at a given point and finding the area under the curve of a function between two given points.
    The first task is easier to accomplish. Simply find the slope of the secant from the point (x, f(x)) to the point (x + h, f(x + h)), where h approaches 0. That is, we've got our point on the curve, (x, f(x)), and another point on the curve, (x + h, f(x + h)), and we're going to slide that second point infinitely close to the first point.
    The second task, finding the area under the curve, is a bit harder to accomplish. It is solved using Riemann Sums. Picture a curve. To find the area between the curve and the x axis between two given x values, place a whole bunch of rectangles of uniform width from the left x boundary to the right x boundary. The height of each rectangle is the distance from the x axis to f(x) at that x value. (If you're not picturing this, just go here.) Now, shrink those rectangles to be infinitely thin, but at the same time fill the boundary with an infinite number of those rectangles. Add up the area's of these rectangles and you've got the area under the curve.]

    After that Intro to Calculus lesson, we're ready for the Calculus module, which is far shorter than the explanation, thankfully.
    code:

    module Calculus
        export slope, area
        function slope (f : function f (x : real) : real, x : real) : real
            const accuracy := 0.00000001
            result (f (x + accuracy) - f (x - accuracy)) / (accuracy * 2)
        end slope
        function area (f : function f (x : real) : real, low, high, width : real) : real
            if high - low <= width then
                result (high - low) * f ((high + low) / 2)
            else
                result width * f (low + width / 2) + area (f, low + width, high, width)
            end if
        end area
    end Calculus

    You can test this module with
    code:

    function f (x : real) : real
        result x ** 2
    end f

    put "The slope of 'f' at x = 5 is ", Calculus.slope (f, 5)
    put "The area under 'f' from x = 0 to x = 5 is ", Calculus.area (f, 0, 5, 0.01)

    Or an even more fun test, which just might show you a pattern in all this calculus:
    code:

    function f (x : real) : real
        result x ** 2
    end f
    put "   x   |  slope of f(x) at x"
    put "-------+--------------------"
    for x : -10 .. 10
        put " ", x : 3, "   |    ", Calculus.slope (f, x)
    end for

    output:

       x   |  slope of f(x)
    -------+--------------------
     -10   |    -20
      -9   |    -18
      -8   |    -16
      -7   |    -14
      -6   |    -12
      -5   |    -10
      -4   |    -8
      -3   |    -6
      -2   |    -4
      -1   |    -2
       0   |    0
       1   |    2
       2   |    4
       3   |    6
       4   |    8
       5   |    10
       6   |    12
       7   |    14
       8   |    16
       9   |    18
      10   |    20

    slope of f(x) = 2x, if f(x) = x^2.


If you wish to further your knowledge of Functional Programming, you'll need to learn a new language. Haskell is a purely functional programming language. O'Caml is a great language (I personally recommend it) that fully supports functional programming, but does not limit you to that programming paradigm.

I hope you've enjoyed this lesson in functional programming, in mathematical functions, and in Calculus. Feel free to ask questions.

Author:  Delos [ Mon May 22, 2006 12:15 pm ]
Post subject:  Re: Turing as a Functional Programming Language

Great tut. This sort of idea (passing constructs as parameters) is a very, very powerful one that people too often overlook. It might be interesting right now to refresh people's memories that one is also capable of creating arrays of functions/procedures; which when coupled with this tut, could produce some intriguing results.

Now a little questions:
Cervantes wrote:

3 To the best of my knowledge (which, with regard to functionalism in Turing, has been put together in one night), we cannot anonymously create functions. That is, we must define our function f(x) as per usual before calling our 'findRoot' function, then pass f(x) into 'findRoot'.


Care to define this concept of anonmity a little better? Things were well explained until you hit that comment and then everything went up in a well-woven handbasket.

As for the Calculus - I'm guessing your disturbingly indepth knowledge of calc is a result of some 'advanced' courses you took (AP?), since I know that I didn't learn even the basics of integration in high school (though some others I know did).

As a parting request, how about adding some 'problems to attempt' at the end - get people to apply these ideas in practical settings. I know a lot of the more advanced coders around here would appreciate this largely.

Author:  Cervantes [ Mon May 22, 2006 1:14 pm ]
Post subject:  Re: Turing as a Functional Programming Language

Delos wrote:

Cervantes wrote:

3 To the best of my knowledge (which, with regard to functionalism in Turing, has been put together in one night), we cannot anonymously create functions. That is, we must define our function f(x) as per usual before calling our 'findRoot' function, then pass f(x) into 'findRoot'.


Care to define this concept of anonmity a little better? Things were well explained until you hit that comment and then everything went up in a well-woven handbasket.

Certainly.

Anonymous Functions
(using my own made up Turing syntax, because this doesn't really work in Turing)

    First-class values (such as strings or integers) don't need to be given a name (bound to a handle, or assigned to a variable) to exist. "hello world" is a string that exists fine all by itself. It doesn't need to be bound to a handle (think: var my_string := "hello world"). For example,
    code:
    put length("hello world")

    is the same as
    code:
    var my_string := "hello world"
    put length (my_string)

    This much should be obvious.

    Now, if a function is truly a first-class value, it too shouldn't need a name (be bound to a handle, or assigned to a variable) to exist. Instead of defining f(x) and then passing our function f into our function 'Calculus.slope', we should be able to anonymously create our function to give to 'Calculus.slope', just as we arbitrarily give the string literal "hello world" to the 'length' function.

    Here's an example, with my own home-cooked Turing syntax (note: this code will NOT execute!)
    code:

    put "The slope of f(x) = x ** 3 + 2 when x = 5 is ", Calculus.slope (lambda {[x : real] x ** 3 + 2}, 5)

    The syntax I've made up here uses "lambda" (from lambda calculus) to anonymously define a function, where the parameters to that function are contained with brackets [], and the whole anonymous function is contained within curly braces {}.

    So here I've defined the function that we are giving to 'Calculus.slope' right in the middle of calling 'Calculus.slope'. No need to give it a name; we're never going to use such a boring curve as y = x^3 + 2 later in the program. It's just like put length ("hello world")


Anonymous Functions in Other Languages

    O'Caml is a fantastic functional language. It defines an anonymous function like this:
    Ocaml:

    fun x -> x ** 3 + 2

    'x' is the parameter, and everything after the "->" is the body of the function. This anonymous function could be directly inserted into another function that accepts a function as a parameter, such as a Riemann Sum function.

    Ruby also has functional aspects. Anonymous functions in Ruby come in the form of blocks, or Proc objects..
    Ruby:

    module Calculus
        def self.slope(f, x)
            accuracy = 0.0001
            (f.call(x + accuracy) - f.call(x - accuracy)) / (accuracy * 2)
        end
    end
    puts Calculus.slope(lambda {|x| x ** 2}, 5)


Questions?


_________________
Delos wrote:

As for the Calculus - I'm guessing your disturbingly indepth knowledge of calc is a result of some 'advanced' courses you took (AP?), since I know that I didn't learn even the basics of integration in high school (though some others I know did).

That's right. AP for the win!

Delos wrote:

As a parting request, how about adding some 'problems to attempt' at the end - get people to apply these ideas in practical settings. I know a lot of the more advanced coders around here would appreciate this largely.

I'll see what I can do. There's some more examples coming (examples that aren't purely for math!), so maybe I'll divert some of them to being "exercises".

Author:  wtd [ Mon May 22, 2006 2:11 pm ]
Post subject: 

This is fantastic stuff. Keep it coming, pirate boy. Smile

Author:  Cervantes [ Tue May 23, 2006 6:42 pm ]
Post subject: 

Here's some more examples, some of which deviate from the realm of math and calculus that the first examples were limited to.

TURuby

    The code I'm about to present allows Turing to mimick Ruby. Two of the many things Ruby has going for it are that it is 100% object oriented (things like integers and arrays are objects) and it has a great library that is even more flexible because of blocks (anonymous functions).

    So I've made an IntArray class in Turing to represent an integer array as an object. I've given it a few important methods: create, each, each_with_index, inject, map, and delete_if. I will explain each method in turn. Here is the body code we will be adding to:

    code:
    class IntArray
        export var value_at
        var value_at : array 1 .. 1000 of int
        var n := 0
    end IntArray

    var first_arr, second_arr : ^IntArray
    new IntArray, first_arr

    You'll notice that I used a big honking static array, rather than a flexible array, because Turing cannot export a dynamic array. You'll also notice that I called the internal array 'value_at'. Seems a strange name for an array, doesn't it? But look -- it allows us to do something like this (once we've initialized the object):
    code:

    first_arr -> value_at (3)

    This gets the third element from the array, and it looks just like a method call. Pretty sweet, huh? Now, onwards and upwards, my friends! We're going to add a whole bunch of really cool methods to this IntArray class.

    First, I will define a helper method, push. This accepts one argument and pushes it onto the top of the array. It is defined like this:
    code:

        proc push (value : int)
            n += 1
            value_at (n) := value
        end push

    You'll only see the push method appear in the code later, when I define the map and delete_if methods.

    The first interesting method is create, which is used to initialize the array. It takes two parameters. The first is the number of elements the array will store. The second parameter, however, is a function f that takes one integer argument and returns another integer. The idea is that the nth element of the array will be set to f(n). Here is the code:
    code:

    class IntArray
        export var value_at, create
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)      % This reads quite nicely: "create 4 factorials"


    What is in our array, now that we've created it with a factorial function? If you've done the math, you'd know that the array holds [1, 2, 6, 24]. If we want to print that, we need to iterate over every element in the array. That's where our next method, each, comes in.

    each is our iteration method. It takes each element of our array and performs an action on it. The action that it performs is determined by the procedure that you pass to each. Here is the code:
    code:

    class IntArray
        export var value_at, create, each
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
        proc each (do : procedure do (x : int))
            for i : 1 .. n
                do (value_at (i))
            end for
        end each
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)

    proc print (x : int)
        put x, " " ..
    end print
    first_arr -> each (print)

    output wrote:

    1 2 6 24


    What if we want to iterate through each element of our array, but still have the index by our side? We'll make a new method, each_with_index, whose procedure takes two parameters, x and i, which represent the element and the index, respectively. Here is the code:
    code:

    class IntArray
        export var value_at, create, each_with_index
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
        proc each_with_index (do : procedure do (x, i : int))
            for i : 1 .. n
                do (value_at (i), i)
            end for
        end each_with_index
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)

    proc print_table (x, i : int)
        put i, " | ", x
    end print_table
    first_arr -> each_with_index (print_table)


    The next method is very special. It is the inject method. It is so special that wtd wrote an entire tutorial on it, entitled The Power of Inject. (It's a Ruby tutorial, but inject is a common method in many languages.)

    Inject takes two parameters: an integer to start with and a function f. The function takes two parameters, a and b. Basically, inject starts with an initial value and takes the first element of the array, passing both of these into the function f. It stores the result of that function in its local variable. It then moves on to the second element of the array, passing it's local variable (which is f(starting_value, first_element)) and the second element to the function. This process is repeated until we reach the end of the array. The code might better illustrate this:

    code:

    class IntArray
        export var value_at, create, inject
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
        function inject (start : int, f : function f (a, b : int) : int) : int
            var ans := start
            for i : 1 .. n
                ans := f (ans, value_at (i))
            end for
            result ans
        end inject
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)

    function sum (a, b : int) : int
        result a + b
    end sum
    function maximum (a, b : int) : int
        if a > b then
            result a
        else
            result b
        end if
    end maximum
    put "The sum of the entries in first_arr is ", first_arr -> inject (0, sum)
    put "The largest number in first_arr is ", first_arr -> inject (0, maximum)


    Inject is a powerful method indeed, but it's power is limited by the functions we give it.

    The next method I've made is the map method. map takes each element of our array and applies a function to it, returning a new array. It "maps" the function onto the array.
    code:

    class IntArray
        export var value_at, create, map
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
        proc push (value : int)
            n += 1
            value_at (n) := value
        end push
        function map (f : function f (x : int) : int) : ^IntArray
            var new_arr : ^IntArray
            new IntArray, new_arr
            for i : 1 .. n
                new_arr -> push (f (value_at (i)))
            end for
            result new_arr
        end map
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)

    function perfect_square (x : int) : int
        result x * x
    end perfect_square
    put "The squares of the first four factorials are: " ..
    first_arr -> map (perfect_square) -> each (print)
    put ""


    The final method I've got is delete_if. This method takes one parameter: a function. This function takes one parameter: an integer. This function returns a boolean. The delete_if method will return a new array that is a duplicate of the current array, except all the elements of our current array who satisfy the function will be deleted. Here is the code:
    code:

    class IntArray
        export var value_at, create, delete_if
        var value_at : array 1 .. 1000 of int
        var n := 0
        proc create (num_of_elements : int, f : function f (x : int) : int)
            n := num_of_elements
            for i : 1 .. num_of_elements
                value_at (i) := f (i)
            end for
        end create
        proc push (value : int)
            n += 1
            value_at (n) := value
        end push
        function delete_if (f : function f (x : int) : boolean) : ^IntArray
            var new_arr : ^IntArray
            new IntArray, new_arr
            for i : 1 .. n
                if f (value_at (i)) = false then
                    new_arr -> push (value_at (i))
                end if
            end for
            result new_arr
        end delete_if
    end IntArray

    var first_arr : ^IntArray
    new IntArray, first_arr

    function factorial (n : int) : int
        if n < 2 then
            result 1
        else
            result n * factorial (n - 1)
        end if
    end factorial
    first_arr -> create (4, factorial)

    proc print (x : int)
        put x, " " ..
    end print
    function even (x : int) : boolean
        result x mod 2 = 0
    end even
    put "The only elements of first_arr that even are: " ..
    first_arr -> delete_if (even) -> each (print)
    put ""


    And that's all the methods I've reimplimented into Turing.


Conclusion

    As you can see, this took a lot of code. Every time we wanted to call one of these extremely useful methods, we had to first define a function. This is very bothersome. After all these examples, I hope you can see why anonymous functions are so wonderful. They allow us to do all that we have done here, except much more elegantly. Compare the anonymous approach (using my own home-brewed Turing syntax),
    code:

    first_arr -> delete_if (lambda {[x : int] x mod 2 = 0}) -> each (lambda {[x : int] put x, " " ..})

    to the non-anonymous approach,
    code:

    proc print (x : int)
        put x, " " ..
    end print
    function even (x : int) : boolean
        result x mod 2 = 0
    end even
    first_arr -> delete_if (even) -> each (print)



More Information

    If you want, you can give Ruby a try (in your browser! No installing anything) and experiment with all these methods, and with anonymous functions. To get you started, here's how Ruby's syntax works with the inject method (using it to find the sum of an array):
    Ruby:

    [1, 2, 4, 10, 67].inject(0) {|a, b| a + b}


    You can download the entire Turing IntArray class and the demonstration of it in the attached source file.

Feel free to ask questions.

Author:  Delos [ Tue May 23, 2006 8:47 pm ]
Post subject: 

/blown away

That was pretty damn sweet. These concepts are utterly brilliant, and I can see why OOP is so bloody powerful. Many a heartfelt thanks for taking the time to implement such code in Turing so that us uninitiates can learn a thing or two.

To that effect...you have an error! Check out your delete_if method. It pushes on false as opposed to true as your description says it should...

Anonymous fcns eh...interesting concept...

Author:  Cervantes [ Tue May 23, 2006 9:12 pm ]
Post subject: 

Delos wrote:

To that effect...you have an error! Check out your delete_if method. It pushes on false as opposed to true as your description says it should...

There is no error. Wink

It pushes on false. Only if the function returns false is that element pushed into the new array. Conversely, if the function returns true, that element is not pushed into the new array, and it is thus 'deleted' (though it still lives on in the current array).

If I had instead used a method like delete_at (index : int), then I would have called that whenever the given function f returned true.

Anyone have any suggestions for the next part? I could give the array of procedure example for a GUI widget, but that's kind of boring.

Author:  md [ Tue May 23, 2006 9:58 pm ]
Post subject: 

Cervantes... you scare me... turing as a functional language (sorta)? How do you even think of these things, let alone actually try?

'Course you can do all of these tricks in other languages too (C, C++, Pascal); though still without the anonymous functions.

Author:  wtd [ Wed May 24, 2006 10:15 am ]
Post subject: 

Cornflake wrote:
Cervantes... you scare me... turing as a functional language (sorta)? How do you even think of these things, let alone actually try?

'Course you can do all of these tricks in other languages too (C, C++, Pascal); though still without the anonymous functions.


You can't do anonymous functions in C++? Wink

http://www.boost.org/doc/html/lambda.html

Author:  Cervantes [ Wed May 24, 2006 11:23 am ]
Post subject: 

Cornflake wrote:
Cervantes... you scare me... turing as a functional language (sorta)? How do you even think of these things, let alone actually try?

I was actually looking through the Turing Help Manual to help TheOneTrueGod out when I saw this very thing done. Look under 'paramDeclarations', or something like that (Well, not you, cornflake).

Author:  md [ Wed May 24, 2006 12:27 pm ]
Post subject: 

I see... and wtd thanks for pointing out that I'm wrong... now I get to play with more things (hopefully) Very Happy

One place where function pointers are rather useful in graphics libraries is when you have a list of sprites. Each sprite might have all the same data in a record to control what and where to draw it, but they might all have different functions controlling how they move; or different collision functions. Instead of having to rely on the person using hte sprite library to call all the correct procedures you can just have two function pointers in your sprite record. One that's called every frame to move and update the sprite, and one that's called on collisions.

There was a sprite library for Pascal on Mac (actually _the_ library) that worked this way and it worked really well.

Author:  wtd [ Wed May 24, 2006 4:54 pm ]
Post subject: 

Once you realize the expressive power of fucntional programming, you begin to realize how short-sighted Turing is for using keywords for things like IO.

Imagine a world where "put" is a function.

code:
some_int_array -> each (put)


But instead, we have to wrap put in a function.

Author:  GlobeTrotter [ Wed May 24, 2006 7:30 pm ]
Post subject: 

If "put" was a function, what would it return? I understand get being a function. Do you mean "put" as a procedure?

Author:  Delos [ Wed May 24, 2006 7:40 pm ]
Post subject: 

No, a function! Think about it...imagine being able to do this:

code:

foo_arr -> each (put ( root_with_base (3)));


On the fly output - this would be useful in debugging sits. I'm sure wtd had more intricate thoughts in mind though, I'm just delving here Wink.

Author:  wtd [ Thu May 25, 2006 12:50 pm ]
Post subject: 

Delos wrote:
No, a function! Think about it...imagine being able to do this:

code:

foo_arr -> each (put ( root_with_base (3)));


Without even realizing it, you have described partial function application. It is one of the more powerful concepts in fucntional programming. Smile

Author:  MysticVegeta [ Fri May 26, 2006 2:40 pm ]
Post subject: 

For the area under a curve one we could use some integral calculus, simple 1 line function with no recursion.

code:
function area (power : int, low, high : int) : real
    result ((high ** (power + 1)) / (power + 1)) - ((low ** (power + 1)) / (power + 1))
end area

put area (2, 0, 5)

Author:  zylum [ Fri May 26, 2006 3:37 pm ]
Post subject: 

your code will only work for functions of the form f(x) = x^power... the point is that you can pass any function to cervantes code and it will calculate the area. i'd imagine it would be quite difficult to implement code that finds the integral to any function (like here: http://integrals.wolfram.com/index.jsp)

Author:  Cervantes [ Fri May 26, 2006 9:15 pm ]
Post subject: 

Propogation of Functions: functions that return functions
(with imaginary Turing syntax)

    What is a function? This is a question of a time long ago, when you were first learning the fundamentals of a function. You learned it is a block of code that takes some values as parameters and returns another value, or something like that.

    Take this definition, but recall what I said in the opening of this tutorial.
    Cervantes wrote:

    functions in a functional language are first-class values, just like integers or strings

    If a function is a first-class value, and if a function is just something that takes parameters and returns a value, then that value that it returns could itself be a function. In fewer words: A function that returns a function.


Function Composition

    Here's an example. Say we have two functions, f(x) = x^2 and g(x) = x + 1. What is the value of f(g(x)) (also written as f̥̥̥̥⃝g, read as f composed with g)? Going through the math, it would be f(x + 1) = (x + 1)^2. Let's code this.
    First, we'll define our functions
    code:

    function f (x : real) : real
        result x ** 2
    end f
    function g (x : real) : real
        result x + 1
    end g

    Nothing new here. This is valid Turing syntax. However, this section is going to require us to pretend Turing can create anonymous functions, so we'll use the same syntax I introduced in part one. Let's redefine those functions, using this syntax:
    code:

    var f : (function (x : real) : real) := lambda {[x : real] x ** 2}
    var g : (function (x : real) : real) := lambda {[x : real] x + 1}

    That's a pretty hefty data type, though. It's a lot to "type". Let's factor that out into a type (oh, oh, pun!):
    code:

    type real2real : (function (x : real) : real)
    var f : real2real := lambda {[x : real] x ** 2}
    var g : real2real := lambda {[x : real] x + 1}

    Now we must write a compose function:
    code:

    function compose (f, g : real2real) : real2real
        var c : real2real := lambda {[x : real] f (g (x))}
        result c    % I defined the variable, 'c', just to specify the type of the function returned.
    end compose

    And put it to action:
    code:

    var h : real2real := compose (f, g)
    put h (5)       % outputs (5 + 1)^2 = 36

    Function composition allows us to "compose" new functions from previously existing ones. This is a powerful concept, allowing us to code only the most basic functions, building the more advanced functions from them.


Partial Function Application

    Once more, let's go back to our previous knowledge of functions. If I have a function f(a, b) = a + b, then calling f(5, 7) is acceptable, but calling f(5) is not. Why does calling f with only one parameter necessitate an error?

    That's obvious, for sure. We can't return an integer if we are missing values to plug a + b. Fair enough: we can't return an integer. What can we return?

    We can return a function. Since f(a, b) = a + b, f(5) = 5 + b. 5 + b is just another function. This is quite intuitive: if my function takes two parameters, then fixing one of them to a certain value returns another function that now only relies on one parameter.

    Implementing PFA method one

      Now, to implement this in Turing, we're not only going to have to use our "lambda" syntax for anonymous functions, we're going to have to use function overloading. In case you're not familiar with this concept, we'll take a very brief detour to explain it.

      Detour: Function Overloading

        Languages that support function overloading (languages such as my imaginary version of Turing) allow multiple copies of a function by the same name to exist, so long as they all take different parameters. When that function is called, the version of that function that is run is determined by the parameters passed in the function call. For example, if I have a function f(a, b) = a + b and another version of that function, f(a) = a + 5, then I can call f with either one or two parameters.


      Using function overloading, here is an implementation of Partial Function Application on the function f in iTuring.
      code:

      function f (a, b : int) : int
          result a + b
      end f

      function f (a : int) : (function (b : int) : int)
          result lambda {[b : int] a + b}
      end f

      Now we can call this function f like this:
      code:

      put f (5, 7)      % => 12
      put (f (5)) (7)   % => 12

      The second represents f (5) creating a new function that takes 7 as a parameter and returns 5 + 7 = 12.

      There's another way we can implement PFA, however; one that does not require function overloading.


    Implementing PFA method two

      This approach involves our function f returning a function that returns a function...
      code:

      var f : ((function (a : int) : (function (b : int) : int))) := lambda {[a : int] lambda {[b : int] a + b}}

      Calling f now looks like this:
      code:

      put (f (5)) (7) % => 12



    What's the point?

      There is no point to Partial Function Application if we're just going to use it like (f (5)) (7). We might as well just write f (5, 7). So what's the point?

      Partial Function Application, just like Function Composition, allows us to quickly and easily create new functions from a base of functions. Say we had a function to multiply two numbers,
      code:

      var mul := lambda {[a, b : int] a * b}

      We could then, using PFA, create functions with names based on the English language:
      code:

      var double := mul (2)
      var triple := mul (3)
      % ...


      We could also pass the function returned by our PFA technique into another function. Assuming we've got an already initialized IntArray object based on my IntArray class from the TURuby section of this tutorial, the following code doubles each element in our array.
      code:

      int_array -> map (mul (2))

      No need to go through all the 'lambda' business. This one is actually readable, too: "map 'multiply by 2' onto int_array".


Author:  wtd [ Sat May 27, 2006 5:41 pm ]
Post subject: 

Good stuff. I would mention that this works much better in languages where it's actually meant to happen. Smile

Author:  MysticVegeta [ Mon May 29, 2006 1:25 pm ]
Post subject: 

wait a sec, if this is all imaginary syntax, then what you are trying to do by writing this tut? Are you sending something to holtsoft to implement these features or is it jsut for beginners to functional programming like me understand this concept, but not be able to do this turing?

Author:  Delos [ Mon May 29, 2006 1:32 pm ]
Post subject: 

MysticVegeta wrote:
wait a sec, if this is all imaginary syntax, then what you are trying to do by writing this tut? Are you sending something to holtsoft to implement these features or is it jsut for beginners to functional programming like me understand this concept, but not be able to do this turing?


Essentially the latter. There's nothing wrong or outlandish about presenting a theoretical tutorial. If people are able to follow along with it, then it might just be time for them to embrace another language to implement such structures.
Personally, I find that having these conceptually challenging ideas thrown around quite conducive to my own coding. The more I read about functional programming, the more I use it (even if it still is in Turing). Getting into good habits, methinks...

Author:  wtd [ Mon May 29, 2006 2:10 pm ]
Post subject: 

A lot of it is fictional in Turing. But it's not imaginary elsewhere.

Cervantes' lambda syntax is imaginary. I know, I helped devise it. Smile

But, there are lots of languages where this can be done.

Common Lisp:

code:
(let ((foo (lambda (bar) (+ (* bar 2) 1))))
   (funcall foo 4))


Scheme:

code:
(let ((foo (lambda (bar) (+ (* bar 2) 1))))
   (foo 4))


Haskell:

code:
let foo = \bar -> bar * 2 + 1 in foo 4


Or using composition:

code:
let foo = (+ 1) . (* 2) in foo 4


O'Caml:

code:
let foo = fun bar -> bar * 2 + 1 in
   foo 4


SML:

code:
let
   val foo = fn bar => bar * 2 + 1
in
   foo 4
end


Python:

code:
foo = lambda bar: bar * 2 + 1
foo(4)


Ruby:

code:
foo = lambda { |bar| bar * 2 + 1 }
foo.call(4)


Smalltalk:

code:
| foo |
   foo := [ :bar | bar * 2 + 1 ].
   foo value: 4


Io:

code:
foo := block(bar, bar * 2 + 1)
foo(4)


Scala:

code:
val foo = bar: Int => bar * 2 + 1
foo(4)


C# 3.0:

code:
var foo = bar => bar * 2 + 1;
foo(4);


Nice:

code:
let Any->Any foo = Any bar => bar * 2 + 1;
foo(4);


I likely left out a huge number of languages.

Author:  Delos [ Mon May 29, 2006 4:42 pm ]
Post subject: 

Bah, polyglot...

Author:  Cervantes [ Mon May 29, 2006 5:22 pm ]
Post subject: 

Nice, wtd! As wtd's post clearly shows, the idea of anonymous functions is a big deal. It's a big deal because it relieves the heavy burden of declaring a function before using it for it's one time purpose, as illustrated by the TURuby section.

MysticVegeta wrote:
wait a sec, if this is all imaginary syntax, then what you are trying to do by writing this tut? Are you sending something to holtsoft to implement these features or is it jsut for beginners to functional programming like me understand this concept, but not be able to do this turing?

As Delos said, the latter. This tutorial serves as a segue to higher level languages. The concepts discussed in this tutorial (and in the full OOP tutorial) are very applicable to programming languages, though less so to Turing. Thus, these tutorials begin the journey into new programming paradigms from within the comfortable world of Turing, thereby making the transition to new languages easier.

Author:  kishan25 [ Tue Jan 13, 2009 5:38 pm ]
Post subject:  RE:Turing as a Functional Programming Language

this was really helpful to me

Author:  syntax_error [ Tue Jan 13, 2009 5:50 pm ]
Post subject:  Re: RE:Turing as a Functional Programming Language

kishan25 @ Tue Jan 13, 2009 5:38 pm wrote:
this was really helpful to me


necroposting is bad.

Author:  redsp0t [ Sun Nov 14, 2010 10:16 pm ]
Post subject:  RE:Turing as a Functional Programming Language

Amazing guide, really helpful like all of your other guides.


: