One of the less used, but widely distributed macro processors (M4) can be transformed into a functional language. After reading http://www.cs.stir.ac.uk/~kjt/research/pdf/expl-m4.pdf
I decided to create some list processing functions (Foldr,Foldl,Head,Tail,Append,Prepend,Null,Sum,Filter)
To implement Foldr and Foldl I have used CPS (continuation passing style), so these functions are difficult to read, but easily testable. Just fireup haskell and try: foldr (-) 0 [3,2,1] -> 2 and foldl should give -6. So Foldl(`Sub',0,[3;2;1]) should give -6 too.
Some logic operators (Not,And,Or,isBool)
Adding support for 'lambdas' -not really, but it is close- and add support for currying.
Maybe I change it in a real project some day. It is rather fun and with foldl and foldr a lot of functions from Data.List can be ported. The real challenge is adding monads. I am not sure if that is possible yet, but I have the feeling it probably can.
Of course there is no type system, but it suprised me how easily most functions could be implemented.
An example:
Lambda(x,`eval($1-($2))')dnl
`foldr'(`x',0,[3;2;1]) is : Foldr(`x',0,t)
`foldl'(`x',0,[3;2;1]) is : Foldl(`x',0,t)
The output of this is:
foldr(x,0,[3;2;1]) is : 2
foldl(x,0,[3;2;1]) is : -6
Even cooler is that you can write your testcases in haskell. Like:
test1 = `foldr' (-) 0 [3,2,1] == Foldr(`x',0,`[3;2;1;]')
Which gives us in ghci:
test1 = foldr (-) 0 [3,2,1] == (2)
ann@ann-desktop:~$ ghci
GHCi, version 6.12.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> foldr (-) 0 [3,2,1] == (2)
True
Prelude>
This is the minilib, it is somewhat dense, but so I could use it in a project:
changecom(`#',`')dnl
dnl# `Id`
define(Id, $1)dnl
dnl# `Lambda'
define(Lambda, `define($1, `$2')')dnl
define(Curry1, `define($1,`$2'($3,$`'1,$`'2,$`'3,$`'4))')dnl
define(Curry2, `define($1,`$2'($3,$4,$`'1,$`'2,$`'3,$`'4))')dnl
define(Curry3, `define($1,`$2'($3,$4,$5,$`'1,$`'2,$`'3,$`'4))')dnl
define(Curry4, `define($1,`$2'($3,$4,$5,$6,$`'1,$`'2,$`'3,$`'4))')dnl
define(Curry5, `define($1,`$2'($3,$4,$5,$6,$7,$`'1,$`'2,$`'3,$`'4))')dnl
define(Curry6, `define($1,`$2'($3,$4,$5,$6,$7,$8,$`'1,$`'2,$`'3,$`'4))')dnl
dnl# `Logical operators'
define(True, 1)dnl
define(False,0)dnl
define(Not, `ifelse($1, True, False, `ifelse($1, False, True, `errprint(Not a bool)')')')dnl
define(isBool, `ifelse($1, True, True, `ifelse($1, False, True, False)')')dnl
define(And, `ifelse($1, True, `ifelse($2, True, True, False)', False)')dnl
define(Or, `ifelse($1, True, True, `ifelse($2, True, True, False)')')dnl
dnl# `List operators'
define(Empty, [])dnl
define(Prep, `ifelse($2,[],[$1;],[$1;`substr($2,1,decr(len($2)))')')dnl
define(App, `ifelse($2,[],[$1;],`substr($2, 0, decr(len($2)))'$1;])')dnl
define(Next, `substr($2,$1,1)')dnl
define(Getpos, `ifelse(Next($1,$2),$3,$1,`Getpos(incr($1), $2, $3)')')dnl
define(Head, `substr($1, 1, decr(Getpos(0, $1,;)))')dnl
define(Tail, `ifelse($1,[],`errprint(tail: empty list)',[`substr($1,incr(Getpos(0,$1,;)))')')dnl
define(Index, `ifelse($1,0,`Head($2)',`Index(decr($1), Tail($2))')')dnl
define(Null, `ifelse($1,[],True,False)')dnl
dnl# `Foldr continuation passing style'
define(_Step, `$4(`$1',$1(Head($3),$2),Tail($3))')dnl
define(Foldr, `ifelse(Null($3),True,$2,`_Step(`$1',$2,$3,`Foldr')')')dnl
dnl# `Foldl continuation passing style'
define(_Stepl, `$4(`$1',$1($2,Head($3)),Tail($3))')dnl
define(Foldl, `ifelse(Null($3),True,$2,`_Stepl(`$1',$2,$3,`Foldl')')')dnl
dnl# `Sum example usage of Foldr'
define(Plus, `eval($1+$2)')dnl
define(Sum, `Foldr(`Plus',0,$1)')dnl
dnl# `Filter'
dnl# `Filter creates an locally scoped curried function, implemented withdefine..undefine'
dnl# ` ```$1''' is just a trick to get the function unpacked at the right place. Every passing
dnl# `removes a `' from the functionname'
define(_Stepf, `ifelse($1($2), True, Prep($2, $3), $3)')dnl
define(Filter,`Curry1(__Stepf,`_Stepf',```$1''')Foldr(`__Stepf',Empty,`$2')undefine(`__Stepf')')dnl
dnl# `Example foldr and foldl'
define(t, Prep(3,Prep(2,Prep(1, Empty))))dnl
Lambda(x,`eval($1-($2))')dnl
`foldr'(`x',0,[3;2;1]) is : Foldr(`x',0,t)
`foldl'(`x',0,[3;2;1]) is : Foldl(`x',0,t)
zaterdag 8 mei 2010
donderdag 18 februari 2010
Some stuff I miss in javascript
I haven't go a lot of time, so this will be a short one.
There is a lot of stuff I miss in javascript. I made an small lib for myself, which I want to share.
First I added some OO patterns:
Decorator,
Proxy,
Observer,
Observable,
Strategy,
Chain,
Chainable exceptionhandlers
I added a logger, which plays nicely with firebug. And some functional stuff like:
foldr,
foldl
A function, which returns a function, which allow partial function application:
pfunc
And a function, which gives any object an fluent interface (useful for loggers):
fluentize
And also added the function merge to the Array object. Here is the small lib:
There are probably some bugs and I haven't documentation, but on request I can add some examples.
There is a lot of stuff I miss in javascript. I made an small lib for myself, which I want to share.
First I added some OO patterns:
Decorator,
Proxy,
Observer,
Observable,
Strategy,
Chain,
Chainable exceptionhandlers
I added a logger, which plays nicely with firebug. And some functional stuff like:
foldr,
foldl
A function, which returns a function, which allow partial function application:
pfunc
And a function, which gives any object an fluent interface (useful for loggers):
fluentize
And also added the function merge to the Array object. Here is the small lib:
There are probably some bugs and I haven't documentation, but on request I can add some examples.
/* Some functions I miss in javascript */ /* Haskell style partial function application */ /* Transforms a function of type: ( (a, b) -> c) to a -> b -> c so if we have a function f(x,y,z) -> x - y + z we can do this now: f(1,y,z) = g(y,z) = 1 - y + z or this: f(1,2,z) = g(z) = 1 - 2 + z or this f(x,y,z) = f(x,y,z); use this class as function min(a,b,c){ return a - b + c; } var pmin = pfunc(min); var t = pmin(1)(2)(3); print(t); */ function pfunc(func, i, acc){ if(!i){ i = 0; acc = new Array(); } if(i == func.arity) return func.apply(func, acc); return function(){ acc.merge(arguments); return pfunc(func, i + arguments.length, acc); } } /* Makes an object fluent */ function fluentize(obj){ var newobj = {}; for(key in obj){ var val = obj[key]; var valn; if(typeof(val) == 'function'){ newobj[key] = function(){ val.apply(obj, this.arguments); return obj; } } else { newobj[key] = val; } } return newobj; } /* Foldr and fold */ function foldr(list, func, acc){ var list = list.reverse(); if(list.length == 0) return acc; for(var i = 0; i < list.length; i++){ var val = list[i]; if(typeof(val) != 'string' && typeof(val) != 'boolean' && typeof(val) != 'number') continue; acc = func(val,acc); } return acc; } function foldl(list, func, acc){ if(list.length == 0) return acc; for(var i = 0; i < list.length; i++){ var val = list[i]; if(typeof(val) != 'string' && typeof(val) != 'boolean' && typeof(val) != 'number') continue; acc = func(acc, val); } return acc; } /* Very important, array merge: */ Array.prototype.merge = function(arr){ for(var i = 0; i < arr.length; i++){ this.push(arr[i]); } } /* Voor ieder een hel Voor de enkeling een hemel maak je keuze snel */ /* Function which can ensure an element exists. callback should be return true or false */ function ensure(callback, cps){ function _ensurethis(){ if(callback()){ cps(); return; } ensure(callback, cps); } window.setTimeout(_ensurethis, 10); } /* Some OO patterns, which are very useful: */ /* Extends an object with other objects * @function extendedObject = Extend(obj, obj1, obj2, obj3 .. objn) */ function Extend(){ var obj = arguments[0]; for(var i = 1; i < arguments.length; i++){ for(key in arguments[i]){ obj[key] = arguments[i][key]; } } return obj; } /* Couple of methods, which let us forward calls to an inner object, very useful to build decorators, proxies etcetera */ function ForwardCall(){ } /* Forward calls to an inner object */ ForwardCall.prototype.forwardCall = function(func) { this[func] = function(){ return this.innerCall(func, arguments); } } ForwardCall.prototype.innerCall = function (func, args){ return this.obj[func].apply(this.obj, args); } /* Decorator object */ Decorator.prototype = ForwardCall.prototype; function Decorator(obj){ this.obj = obj; for(key in this.obj){ var val = this.obj[key]; if(typeof(val) == 'function'){ this.forwardCall(key); } } } /* Proxy object */ Proxy.prototype = ForwardCall.prototype; function Proxy(obj){ this.obj = obj; for(key in obj){ var val = this.obj[key]; if(typeof(val) == 'function'){ this[key] = function(){ var args = this.before(key, arguments); ret = this.innerCall(key, args); return this.after(key, ret); } } } this.before = function(func, args){ return args; }; this.after = function (func, ret){ return ret; } } /* Observable */ function Observable(){ } Observable.prototype.addObserver = function(observer){ if(typeof(this.observers) == 'undefined'){ this.observers = new Array(); } this.observers.push(observer); } Observable.prototype.updateObservers = function (message){ for(observerid in this.observers){ var observer = this.observers[observerid]; observer.receiveMessage(message); } } /* Observer */ function Observer(){ } Observer.prototype.receiveMessage = function (message){ } /* Implementation of the strategy pattern */ function Strategy(){ this.strategies = new Array(); this.createStrategy = function (func, test){ return {func: func, test: test}; }; this.addStrategy = function (func, test){ this.strategies.push(this.createStrategy(func, test)); return this.strategies.length; }; this.setDefaultStrategy = function (func){ this.defaultStrategy = func; }; this.removeStrategy = function (id){ this.strategies[id] = null; }; this.run = function() { var args = arguments; for(key in this.strategies){ if(this.strategies[key] != null){ var strategy = this.strategies[key]; if(strategy['test'].apply(this, args)){ return strategy['func'].apply(this, args); } } } return this.defaultStrategy.apply(this, args); } } function Chain(){ } function Chain(test, func){ this.func = func; this.test = test; this.next = null; this.run = function(){ if( this.test.apply(this, arguments) ){ return this.func.apply(this, arguments); } if(this.next != null){ return this.next.run.apply(this.next, arguments); } return null; } this.addToChain = function(test,func){ if(this.next == null){ this.next = new Chain(test,func); return; } this.next.addToChain(test,func); } } ExceptionHandler.prototype = ForwardCall.prototype; function ExceptionHandler (obj){ this.obj = obj; this.strategy = new Strategy; this.strategy.setDefaultStrategy(function(ex,key){ print ("Uncaught Exception calling: " + key); print(ex); return; }); this.addFunc = function(key){ this[key] = function(){ try { this.innerCall(key, arguments); } catch (e){ this.strategy.run(e, key); } } } for(key in this.obj){ var val = this.obj[key]; if(typeof(val) == 'function'){ this.addFunc(key); return this.next.run.apply(this.next, arguments); } return null; } this.addToChain = function(test,func){ if(this.next == null){ this.next = new Chain(test,func); return; } this.next.addToChain(test,func); } } ExceptionHandler.prototype = ForwardCall.prototype; function ExceptionHandler (obj){ this.obj = obj; this.strategy = new Strategy; this.strategy.setDefaultStrategy(function(ex,key){ print ("Uncaught Exception calling: " + key); print(ex); return; }); this.addFunc = function(key){ this[key] = function(){ try { this.innerCall(key, arguments); } catch (e){ this.strategy.run(e, key); } } } for(key in this.obj){ var val = this.obj[key]; if(typeof(val) == 'function'){ this.addFunc(key); this.addFunc(key); } } this.addHandler = function(test, func){ this.strategy.addStrategy(test, func); } } /* Inspection function */ function Inspect(obj, depth, i, indent){ if(!depth){ depth = 5; } if(!i){ i = 0; indent = ''; } for(key in obj){ var type = typeof(obj[key]); print (indent + type + ": " + key); if((type == 'object' || type == 'function' ) && i < depth){ Inspect(obj[key], depth, i + 1, indent + " "); } } } /* We need to define a print function, to work with all the classes above: */ function print(str){ Logger.getInstance().startGroup('print').log('from: ' + arguments.callee.caller.toString()).log(str).endGroup(); } /* HTML generating functions */ /* Simple positioning function */ function putHTML(pos){ return function(data){ Logger.getInstance().startGroup('putHTML'); Logger.getInstance().log('position data at: ' + pos); $(pos).html(data); Logger.getInstance().endGroup(); } } /* Javascript logger, logs to firebug, this an singleton, on non development site, we have to run Logger.getInstance().setSilence(), then it shuts up */ function Logger(){ this.silence = false; } Logger.getInstance = function(){ if(!Logger.instance){ Logger.instance = /*fluentize(*/new Logger();//); } return Logger.instance; } Logger.prototype.setSilence = function(silence){ if(!silence){ silence = true; } this.silence = silence; } Logger.prototype.startGroup = function(name){ if(this.silence) return; console.group(name); return this; } Logger.prototype.endGroup = function(){ if(this.silence) return; console.groupEnd(); return this; } Logger.prototype.log = function (logline, errorlevel){ /* formatted mode */ if(!console || this.silence){ return; } if(typeof(logline) == 'object'){ this.multiline(logline, errorlevel); } if(errorlevel == 'error'){ console.error(logline); } else if(errorlevel == 'warn'){ console.warn(logline); } else { console.info(logline); } return this; } Logger.prototype.multiline = function(loglines, errorlevel){ console.group('Object inspector:'); console.dir(loglines); console.groupEnd(); return this; }
Labels:
functional,
Javascript,
logger,
OO,
php design patterns decorators
Abonneren op:
Posts (Atom)