49
Functional programming concepts And applications on GDScript

Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

  • Upload
    others

  • View
    35

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Functional programming concepts

And applications on GDScript

Page 2: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

George Marques

● Godot contributor since 2015● Member of the project’s leadership since 2017● Worked at Javary Games and IMVU (using Godot)

– Hired by Godot since November 2019● Co-author of the book “Godot Engine Game Development in 24

Hours”, published by Pearson● Responsible for the UWP port and typed GDScript

Page 3: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Topics

● Introduction to functional programming● Monad● Functional programming characteristics● Functions● Applications (in general and on GDScript)● Other resources

Page 4: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

What is functional programming?

● “Everything is a function”● Declarative instead of imperative● Application of mathematical concepts● Avoids changing external state (pure functions)● Functions are also values (higher-order functions)

Page 5: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Let’s talk about monad?

“A monad is a monoid in an endofunctor category”

- Rúnar Bjarnason

Page 6: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Okay, let’s not talk about monad

Or will we?

Page 7: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Disadvantages

● Declarative code is not always the most readable● It’s hard to combine pure functions and intended side-effects● Immutability and recursion can lead to performance penalties

– The functional paradigm avoids loops in favor of recursion

Page 8: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Vantages

● Implementation of pure functions are easier to understand– Looking at a function signature may be enough to know what it does

● Easier to run things in parallel (no need for locks)● Testing functions in isolation is more straightforward

– Also easier to debug

Page 9: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Side effects

● By definition: changes in objects outside the function scope● Change of global state● Input and output

– Yes, this includes keyboard input and video output● Changes in the object that contains the function (instance members)

Page 10: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Immutability

● Variables are treated as constants– Forbidden to re-assign a value to a variable– Forbidden to change properties of an object

● (Okay to change a local object for the sake of initialization)

● Instead of changes, make copies– func nope(obj_to_change: Object) -> void– func yep(obj_to_update: Object) -> Object

Page 11: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Pure function

● Don’t have side effects● All parameters are immutable● A call with the same arguments return the same value every time● Similar to the mathematical definition

Page 12: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

First-class functions

● Function is also a value– It can be assigned to a variable– It can be passed as arguments to other functions– It can be the return value of a function

● Everything that can be done with a value can also be done with a function– Including putting them in arrays and dictionaries

Page 13: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

First-class functions in GDScript

● Well, we’re not there yet, but will soon™

Page 14: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

FuncRef to the rescue

● Exclusive Godot type that allows a function reference● Then you can treat the reference as a value

– And call it later (with arguments)● Making it in GDScript:

var my_func = funcref(self, "my_method")

● Calling:

my_func.call_func("An argument")

Page 15: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Higher-order functions

● Functions are “first-order” by default● They’re higher-order if they receive a function as an argument● Or return a function as a result● It’s not mandatory that they are pure

– But we like them more if they are

Page 16: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Higher-order functions

● Examples– map– filter– reduce

Still not available in GDScript for now, but not hard to implement in script

Page 17: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Map

● Take an array and a unary function, returns a copy of the array with the function applied to every element

● Signature: func map(input: Array, function: FuncRef) -> Array● Equivalent to:

var my_arr -= [1, 2, 3]

var mapped -= []

for element in my_arr:

mapped.append(some_func(element))

Page 18: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Map

● Example 1func half(num: int) -> int:

return num / 2

func half_array(numbers: Array) -> Array:

return map(numbers, funcref(self, "half"))

func _ready() -> void:

var numbers -= [2, 4, 6]

var halves -= half_array(numbers) # [1, 2, 3]

Page 19: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Map

● Example 2func get_id(user: User) -> int:

return user.id

func list_user_ids() -> Array:

var users: Array = fetch_users()

return map(users, funcref(self, "get_id"))

Page 20: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Map

● Possible implementationfunc map(input: Array, function: FuncRef) -> Array:

var result -= []

result.resize(input.size())

for i in range(input.size()):

result[i] = function.call_func(input[i])

Page 21: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Filter

● Takes an array and a unary function, returns a new array with only the elements to which the function returns true

● Signature: func filter(input: Array, function: FuncRef) -> Array● Equivalent to:

var my_arr -= [1, 2, 3]

var filtered -= []

for element in my_arr:

if some_func(element):

filtered.append(element)

Page 22: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Filter

● Example 1func is_even(num: int) -> bool:

return num % 2 -= 0

func get_evens(numbers: Array) -> Array:

return filter(numbers, funcref(self, "is_even"))

func _ready() -> void:

var numbers -= [1, 2, 3, 4]

var evens -= get_evens(numbers) # [2, 4]

Page 23: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Filter

● Example 2func is_active(user: User) -> bool:

return user.active

func list_active_users() -> Array

var users: Array = fetch_users()

return filter(users, funcref(self, "is_active"))

Page 24: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Filter

● Possible implementationfunc filter(input: Array, function: FuncRef) -> Array:

var result -= []

for element in input:

if function.call_func(element):

result.append(element)

return result

Page 25: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Reduce

● Takes an array, a binary function, and a value, returns a single value with the function applied to each element and the previous result

● Signature: func reduce(input: Array, function: FuncRef, base)● Equivalent to:

var my_arr -= [1, 2, 3]

var reduced = base

for element in my_arr:

reduced = some_func(reduced, element)

Page 26: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Reduce

● Example 1func sum(num1: int, num2: int) -> int:

return num1 + num2

func array_sum(numbers: Array) -> int:

return reduce(numbers, funcref(self, "sum"), 0)

func _ready():

var numbers -= [1, 2, 3, 4]

var sum: int = array_sum(numbers) # 10

Page 27: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Reduce

● Example 2func larger(num1: int, num2: int) -> int:

return num1 if num1 > num2 else num2

func get_user_age(user: User) -> int:

return user.age

func get_highest_user_age() -> int:

var user_ages: Array = map(fetch_users(), \

funcref(self, "get_user_age"))

# Base value is omitted here

return reduce(user_ages, funcref(self, "larger"))

Page 28: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Reduce

● Possible implementationfunc reduce(input: Array, function: FuncRef, base = null):

var accumulator = base

var index -= 0

if not base and input.size() > 0:

accumulator = input[0]

index = 1

while index < input.size():

accumulator = function.call_func(accumulator, input[index])

index += 1

return accumulator

Page 29: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Lambda

● Function definition where a value (expression) is expected● Helps in cases where the function is simple or doesn’t need to be

reused in another context● Example:

var result -= map([2, 4, 6], x -> x / 2)

print(result) # [1, 2, 3]

Page 30: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Lambdas in GDScript

● Well, we’re not there yet, but will soon™

Seriously!

Page 31: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Let’s talk a bit about theory

I promise it’s only a bit

Page 32: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Functor

● (No need to flee, it’s not that hard)● Functor is a type that can be mapped, that is, there’s a map function

applicable to the type● Yes, that’s it● As we say, Array is a functor because it has* a map function

– *Technically the function isn’t in the Array class, but it doesn’t matter conceptually.

Page 33: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Monoid

● Monoid is a type that has a function with three properties:– Binary

● Has two parameters– Associative

● function(a, function(b, c)) -= function(function(a, b), c)

– Has a neutral (identity) element● function(a, identity) -= function(identity, a) -= a

● It doesn’t need to be commutative– (function(a, b) -= function(b, a))

Page 34: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Monoid

● Example: integers with sum as the function– Binary: 3 + 4 (two arguments: “3” and “4”)– Associative: (1 + 2) + 3 = 1 + (2 + 3) = 6– Identity element: 2 + 0 = 0 + 2 = 2

Page 35: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Monoid

● Array is also a monoid!● What’s the function that makes it a monoid? Concatenation:

– Binary: [1, 2] + [3, 4] -= [1,2,3,4]– Associative: ([1,2] + [3,4]) + [5,6] -= [1,2] + ([3,4] + [5,6]) -=

[1,2,3,4,5,6]

– Identity element: [] + [1,2] -= [1, 2] + [] -= [1, 2]

Page 36: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Monad

● Yes, let’s talk about monad!● As said: “A monad is a monoid in an endofunctor category”● In other words: Monad is a monoid that is also an endofunctor● In the context of programming, every functor is an endofunctor● Which means...

Page 37: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Array is monad!

● Yes, that’s it● It’s a functor (can be mapped)● It’s a monoid (regarding the concatenation function)● So it’s also a monad

Page 38: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise

● Encapsulates asynchronous operations● Allows to maintain the chain of operations while we wait the

promise to be fulfilled– That is, the asynchronous operation complete– The promise can be treated as the value that it contains

● Suppress side effects

Page 39: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise

● Example: HTTP request (promise implementation)class_name Promise

signal success(value) # probably want error and done signals too

var done: bool = false

func _success(value):

done = true

emit_signal("success", value)

Page 40: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise

● Example: HTTP request (kitten request class)var promise: Promise = null

func kitten_request(width: int, height: int) -> Promise:

var http -= HTTPRequest.new(); add_child(http)

http.request("http:-/placekitten.com/%d/%d" % [width, height])

http.connect("request_completed", self, "_on_request_completed")

promise = Promise.new()

return promise

func _on_request_completed(result, response_code, headers, body):

var image -= Image.new()

image.load_jpg_from_buffer(body)

var texture -= ImageTexture.new()

texture.create_from_image(image)

promise._success(texture)

Page 41: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise

● Example: HTTP request (using promises)func _ready():

var kitten_promise: Promise = kitten_request(800, 600)

kitten_promise.connect("on_success", self, "on_get_kitten")

func on_get_kitten(kitten: Texture):

$KittenSprite.texture = kitten

Page 42: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise

Page 43: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise in functional programming

● Promise is also a monad!● Monad can also be seen as encapsulation inside a context● In an Array, the context is the multiplicity of values● In Promise, the context is the “promise” of a future resolution● To put in the context is usually called “Lift”

Page 44: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise as a functor

● The “mapping” in promise means applying the function to the final resolved value– Like the kitten picture

● It’s possible to define Promise.map() which applies the function only when it resolves (that is, when the async operation completes)

● Remember that map() returns the value inside the context, so Promise.map() also returns a Promise

● In turn, it’s possible to apply other pure operations to a Promise before it is fullfilled

Page 45: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Promise map

● Examplevar users: Promise = request_users() # Promise of array of users

var active_users: Promise = users.map(funcref(self, "filter_active_users"))

active_users.connect("success", self, "print_users")

func filter_active_users(users: Array) -> Array:

return filter(users, funcref(self, "is_active"))

func print_users(users):

print(users)

Page 46: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Monad as side effect isolation

● Like Promise encapsulates an asynchronous operation, Monads can also encapsulate side effect

● It’s possible to operate with the values inside the lifted context (i.e. inside the Promise) without depending on its effects

● Example: operating in a text input before the user types anything– The function needs only to get the text as argument, not wait for the side effect

● Keeping side effects and shared state only in a small and specific part of the code

Page 47: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Learn a functional language

● At first it may be hard to assimilate the concepts● But the patterns can be applied in other languages and be used to

increase code quality● Examples of functional languages:

– Elm, Elixir, Haskell, Clojure, Scala, F#

Page 48: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Questions?

[email protected]

github.com/vnen

twitter.com/vnen

Page 49: Functional programming concepts › functional-gdscript.pdf · What is functional programming? “Everything is a function” Declarative instead of imperative Application of mathematical

Thank [email protected]

github.com/vnen

twitter.com/vnen