; Functions
; ---------

; use the "lambda" keyword to define an anonymous function.

(define pythagoras
    (lambda (a b)
        (sqrt
            (+
                (* a a)
                (* b b)
            )
        )
    )
)

(display (pythagoras 3 4))
(newline)
; Displays 5

; Note: you can declare a lambda and execute it in the same statement:

(define myvar ((lambda (a) (* 2 a)) 10))
; myvar will be set to 20

; A function executes all the statements inside it and returns the last statement.

(define myfunc
    (lambda (a b)
        (display a)
        (display "+")
        (display b)
        (display "=")
        (+ a b)
    )
)

(display (myfunc 5 6))
(newline)
;Will display:
;5+6=11

; More about "define" and "set!"
; ------------------------------

; A define inside a function will only be visible inside that function.
; Assuming a variable of the same name was defined in the outside, it will
; be restored to the same value as soon as the function terminates.

(define a 500)

(define myfunc
    (lambda ()
        (define a 6)
        (display a)
        (newline)
    )
)

(myfunc)
(display a)
(newline)

; A function can see all the variables that were defined on the outside.

; set! sets the variable that is most closest to the current scope. (hence
; is in the inner-most scope).

(define a "a_old_value")
(define b "b_old_value")

(define myfunc
    (lambda ()
        (define a 30)
        (set! a "a_new_value")
        (set! b "b_new_value")
    )
)

(display a)
(newline)
(display b)
(newline)

; This code will print:
; a_old_value
; b_new_value

; Functions inside functions
; --------------------------

; One can define functions inside functions. Those functions can see the
; variables of the outer functions even after those functions terminated.
; This behaviour is called lexical scoping.

(define display_sums
    (lambda (a b c d e f)
        (define display_sum
            (lambda (one two)
                (display one)
                (display "+")
                (display two)
                (display "=")
                (display (+ one two))
                (newline)
            )
        )
        (display_sum a b)
        (display_sum c d)
        (display_sum e f)
    )
)

(if #f
    (display_sum 5 6) ; Error!, display_sum is not defined in this scope.
)




(define make-counter
    (lambda (start_from)
        (define increment
            (lambda ()
                (set! start_from (+ start_from 1))
                start_from
            )
        )
        increment
    )
)

(define counter (make-counter 100))
(display (counter))
(newline)
(display (counter))
(newline)
; This code will print:
; 100
; 101

;;; Now let's define a second counter
(define second-counter (make-counter 100))
(display (second-counter))
(newline)
(display (counter))
(newline)

; Lambdas are regular variables
; -----------------------------

; You can pass them around. For example:

(define myfunc 0)
(set! myfunc (lambda (h) (* 3 h)))

(define twice (lambda (func x)
    (func (func x))
))

(display (twice myfunc 2))
(newline)

; This will print 18