Jump to content


Photo

Gmfunctional


  • Please log in to reply
6 replies to this topic

#1 OpticalLiam

OpticalLiam

    GMC Member

  • New Member
  • 782 posts

Posted 25 March 2010 - 10:12 PM

:P gmFunctional


Download

v1.0 - Download


Introduction

gmFunctional is a script library to enable functional style programming in Game Maker.
It is suitable for whenever you wish to process strings or lists, or do mathematical computations.
It reduces the amount of code you need to write as most of the leg work is handled for you.

Features
  • Many useful built-in functions - Most commonly used functions (based on Haskell's) are included and written in native non-recursive GML.
  • Low dependancy - Almost all included scripts can be used standalone (if you remove disposes).
  • Low verbosity - Intuitive single word script names enhance readabillity and reduce code length.
  • Chainable functions - Complex functions can often be written as one-liners.
  • Higher-Order functions - Common recursion patterns are captured by the functions Map, Filter and Fold.
  • Anonymous (Lambda) functions - Anonymous functions can be passed by value as strings.
  • Currying (function composition) - Functions can be composed to create new ones (as lambdas).
  • Ds_list / string duallity - The included suite of scripts accept both strings and ds_lists.
  • Mathematical correctness - In theory, functionally written expressions are provable to some extent.
Functional Programming?

Functional programming is a different programming paradigm than most programmers are used to, especially those that have only used GML.
In GML (and other imperative languages) you describe how to perform a computation, as steps of an algorithm. In functional programming you declare (or define) what a result is by a set of equations (the functions). At first, coming from imperative programming, functional code will probably seem very unintuitive and unreadable. When you eventually get your head around how functional programming works you will see that they will likely become very intuitive and natural by the end. I am not going to go into any more detail about functional programming as it's a huge topic. If you wish to learn how to program functionally I strongly reccommend that you learn Haskell with the aid of the brilliant tutorial learn you a haskell.


Important Notes
  • The term function in this library means either a script or a lambda.
  • The term list in this library means either a ds_list or a string (where a string is a list of characters).
  • The term element in this library means an element of a list.
  • It should be assumed (for most library functions) that once a function is applied to a list, that list is disposed and destroyed when the gc is run.
  • BUT know that some functions in reality modify the given list and return it (for performance reasons), others create new lists (and dispose the old one). See Issues.
  • This library is only meant for functional programming style, it is far from truly functional (as GM is way too limited).
  • Functions that take lists always do so as the first argument (unlike Haskell). The reason is to do with the higher-order functions (you'll see).
  • The garbage collector (gc) should be run at the end of your computation to destroy unused (disposed) lists.
  • Explicit recursion with scripts is doable, but always use higher-order functions (map, filter, fold) instead where possible.
  • Use scripts in higher-order functions (over lambdas) where possible as they are significantly faster to execute.
  • When using strings as lists, the first element (character) index is 0 (not 1 as it is in GM string functions)
  • Lambda expressions are in the form of strings. They may contain anything that can be excuted by GM.
    They use special variables preceded by a dot to be matched as arguments.
    Arguments passed to the function correlate to the special variables ".a", ".b", ".c" through to ".o" (argument0 through to argument15).
    Aliases are given for ".a" = ".x" = ".n" and ".b" = ".y" and ".c" = ".z".
  • GM8 can give a notable performance boost when using this library.
Issues
  • The fact that some functions modify old lists (for performance reasons) means there may be unwanted side effects with a few functions (such as tail()) if you are not careful. This is a tricky issue that I'm not sure what I'm going to do about yet (other than assume non-mutabillity and sacrifice performance).
  • It is also kind of inconsistent in which functions dispose the lists given to them and which don't. I'm not sure why, for some reason it feels natural that some functions do dispose and some don't. There's currently no real strategy to it. This means there's probably going to be memory leaks. Not sure what to do here yet.
  • Performance is acceptable, but explicit recursion via scripts and the execution of lambdas may be fairly slow.
  • Currying (function composition) via o(a,b) has issues with internal scope. It works for some things, but for others it needs fixing.
  • Support for nested lists is extremely tentative. As lists are passed as integers, its virtually impossible to programatically tell if a list element is an integer or if it is another list.
  • My documentation is a bit thin. I'll get round to making it better. Maybe.
  • Probably loads more I forgot or I'm not aware of.

Download

v1.0 - Download


Usage Examples

list(5, 1,2,3,4,5) = [1,2,3,4,5]
map(list(3, 1,2,3),add,10) = [11,12,13]
map("hello how are you","chr(ord(.x)-.y)",1) = "gdkk gnv `qd xnt"
map(list(3, 1,2,3),".x/2") = [0.5,1,1.5]
filter(list(10, 1,2,3,4,5,6,7,8,9,10),factor,3) = [3,6,9]
filter(list(10, 1,2,3,4,5,6,7,8,9,10),even) = [2,4,6,8,10]
filter(split("hello howz are you x"," "),"len(.x) > 3") = ["hello", "howz"]
foldl("abcdefghijk",".x + .z + .y"," ") = "a b c d e f g h i j k"
foldl(list(10, 1,2,3,4,5,6,7,8,9,10),add) = 55
foldr("hello how are you",cnc) = "uoy era woh olleh"
head(list(6, 1,2,3,4,5,6)) = 1
tail(list(6, 1,2,3,4,5,6)) = [2,3,4,5,6]
head("hello") = "h"
tail("hello") = "ello"
cnc(list(5, 1,2,3,4,5),list(5, 6,7,8,9,10)) = [1,2,3,4,5,6,7,8,9,10]
range(5) = [0,1,2,3,4,5]
range(5,20,2) = [5,7,9,11,13,15,17,19]
sequence(0,10,".n*.n + 2*.n") = [0,3,8,15,24,35,48,63,80,99,120]
sequence(0,10,multiply,10) = [0,10,20,30,40,50,60,70,80,90,100]
split("hello how are you"," ") = ["hello","how","are","you"]
f(".x + .y",1,2) = 1 + 2 = 3
f(add,1,2) = 1 + 2 = 3
elem("hello world","w") = true
drop(list(10, 34,3,43,53,3,45,4,42,12,8,33,1),3) = [53,3,45,4,42,12,8,33,1]
take(list(10, 34,3,43,53,3,45,4,42,12,8,33,1),3) = [34,3,43]
any(list(3, true,false,false)) = true
any(list(3, false,false,false)) = false
all(list(3, true,false,false)) = false
all(list(3, false,false,false)) = false
all(list(3, true,true,true)) = true
str(map(range(97,96+26),"chr(.x)")) = "abcdefghijklmnopqrstuvwxyz"

Edited by OpticalLiam, 27 March 2010 - 03:22 PM.

  • 0

#2 Juju

Juju

    GMC Member

  • GMC Member
  • 1109 posts
  • Version:Unknown

Posted 25 March 2010 - 10:34 PM

Supremely useful. Do you have any idea how quick this is?
  • 0

Come find me @jujuadams

 

Try out my open-source 3D globe terrain generator!

How about a fancy-pants text engine?

Adding dialogue boxes to your games is now super easy. Also localisation. Also tweening.


#3 kaz3

kaz3

    GMC Member

  • New Member
  • 730 posts

Posted 26 March 2010 - 02:33 AM

Very interesting. Don't understand it fully, but it makes sense. Useful for a variety of things and I encourage people to check it out for any project really.
  • 0

#4 Rusky

Rusky

    GMC Member

  • GMC Member
  • 2492 posts
  • Version:Unknown

Posted 26 March 2010 - 02:31 PM

This is definitely a cool idea. It seems fast enough if you don't use it very heavily, but once I added four or five calculations per step (to an already-existing game) it started to slow down a lot. I don't know how much a DLL would speed it up, because there are a lot of smaller calls going on.

Besides the speed, I had a problem with the really short function names. Apparently var declarations can't shadow script names, so it interfered with some variable names (o, f) that were already in the game I stuck it in.
  • 0

#5 Josh @ Dreamland

Josh @ Dreamland

    GMC Member

  • New Member
  • 455 posts
  • Version:GM8

Posted 27 March 2010 - 02:55 PM

Very nice. Though I'm personally not a fan of the functional paradigm, I know a lot of people are, and it's somewhat fun to see small pieces of code that manage to make arrays do backflips.

I don't believe that this will be overly appreciated on the GMC; we are dealing with a race of people that doesn't understand == is an operator, let alone any other syntactical phenomena not implemented in Game Maker.

However, kudos for the project. Overall a pretty great idea.
  • 0

#6 OpticalLiam

OpticalLiam

    GMC Member

  • New Member
  • 782 posts

Posted 29 March 2010 - 11:41 AM

Supremely useful. Do you have any idea how quick this is?

Depends on how it's used. It should be reasonably fast if you stick to using the higher-order functions and pass scripts (as opposed to lambdas). Also GM8 seems to give a decent boost as Mark improved GML execution speed somewhat and reduced script overhead by a decent amount.

Very interesting. Don't understand it fully, but it makes sense. Useful for a variety of things and I encourage people to check it out for any project really.

Yeah its difficult to understand at first if you've never programmed functionally. But once you understand what the arguments of the scripts are and how the higher order functions work (they're not all that complicated) it will all be come much clearer.

It seems fast enough if you don't use it very heavily, but once I added four or five calculations per step (to an already-existing game) it started to slow down a lot.

Yeah I guess really this is meant to be more for one-time calculations that happen in a single step. Though, it still shouldn't that slow, but it vastly depends on what you were doing.

I don't know how much a DLL would speed it up, because there are a lot of smaller calls going on.

I thought about writing a DLL for it. It would speed it up by a lot, especially if I wrote my own list data structure. I'm still thinking about that.

Besides the speed, I had a problem with the really short function names. Apparently var declarations can't shadow script names, so it interfered with some variable names (o, f) that were already in the game I stuck it in.

That's true. I'll probably change the short function names to something longer in the next version.

I don't believe that this will be overly appreciated on the GMC; we are dealing with a race of people that doesn't understand == is an operator, let alone any other syntactical phenomena not implemented in Game Maker.

Yeah, it pretty much the nature of the GMC and Game Maker to have new programmers that will never have been exposed to this. To be fair, even many seasoned programmers will not have either, as for some reason functional is still pretty niche. But yeah, I understood before I wrote this that it wasn't going to get much of a reception in the GM community. I mostly wrote it as a proof of concept.
  • 0

#7 GrayAvocado

GrayAvocado

    A big bad Wolf

  • GMC Member
  • 1226 posts

Posted 07 August 2010 - 11:49 PM

This really needs a bump, it is very nice, but i think it could be improved. I think you could come to great use by many programmers for the more light applications.Posted Image
  • 0