Branch: Tag:

2016-05-30

2016-05-30 18:37:45 by Per Hedbor <ph@opera.com>

Added C++11-alike Function.bind functionality.

This is somewhat similar to Function.curry, but more dynamic.

However, the usage is somewhat cumbersome, you have to use
placeholders if you want anything except static arguments.

As an example:

import Function.Placeholder;
function add_6 = Function.bind( `+, arg0, 6 );

This returns a function that adds 6 to it's only argument (in that
order, unlike curry which would be the other way around)

Function.Placeholder has these useful indices:

o argN -- takes argument N from the call
o rest -- All unused arguments (by argN, Arg(N) or Splice)
o Splice(start[,end]) arguments from start (if end is not specified, all arguments)
o Arg(N) similar to argN, but N can be negative
o Expr(func[,splice]) Call func with arguments, use return value
if splice is true, returns an array to be @:ed.

Things to do:

The syntax is rather horrid, really.
Is there a way to get a more reasonable one?

88:    };   }    + //! Partially evaluate a function call. + //! + //! This function returns a function that when called will do the + //! specified argument mapping. It is similar to @[curry], but allows + //! more dynamic changes of the argument evaluation, you can leave the + //! first argument unspecified while setting others, or simply change + //! the argument order. + //! + //! The first argument is the function to be called. + //! + //! All other arguments is either a generic value, which will be sent as-is to the funciton + //! or one of the placeholder values define in [Function.Placeholder], + //! or one of your own implementation (inherit + //! Function.Placeholder.Base and implement the value function.). + //! + //! @example + //! This example returns a funciton that limits the given argument + //! to between 0 and 99. + //! @code + //! import Functio.Placeholder; + //! function clip = Function.bind(limit, 0, arg0, 100); + //! @endcode + class bind(function f, mixed ... bind_args) + { +  protected string _sprintf() { +  return sprintf("Function.bind(%O%{, %O%})",f,bind_args); +  }    -  +  protected mixed `()(mixed ... args) +  { +  array processed = ({}); +  for(int i=0; i<sizeof(bind_args);i++) +  { +  if( objectp(bind_args[i]) && bind_args[i]->_is_placeholder ) +  { +  mixed val = bind_args[i]->value(this,args); +  if( bind_args[i]->_splice ) +  processed += val; +  else +  processed += ({val}); +  } +  else +  processed += ({bind_args[i]}); +  } +  return f(@processed); +  } + } +    //! This function, given a function taking N parameters, returns a new   //! function taking N+1 parameters. The first argument will be   //! ignored.
149:    return args;    };   } +  +  + //! @module Placeholder + //! + //! Placeholder arguments for Function.bind + object Placeholder = class + { +  class Base +  { +  constant _is_placeholder = true; +  //! @decl mixed value(bind x, array args); +  //! +  //! The function that is called to return the argument value. +  } +  +  class Arg(int num) +  //! Arg(x) returns the value of argument X +  { +  inherit Base; +  protected string _sprintf() { +  return sprintf("arg%d",num); +  } +  +  mixed value(bind x, array args) +  { +  if(num>=sizeof(args) || -num>sizeof(args)) +  error("No argument %d given\n",num); +  return args[num]; +  } +  }; +  +  class Splice(int from, void|int end) +  //! Splice(from) adds all arguments starting with argument number @[from], +  //! optionally ending with end. +  //! Equivalent to @args[from..end] +  { +  inherit Base; +  constant _splice = true; +  array(mixed) value(bind x, array args) +  { +  if( end ) +  return args[from..end]; +  return args[from..]; +  } +  } +  +  //! @decl Arg rest; +  //! Return all arguments not used by any @[Arg] or @[Splice]. +  //! +  //! Unlike @[Splice] this will return non-continous unused arguments. +  //! +  //! @example +  //! This creates a version of call_out that has the function argument as +  //! the last argument +  //! @code +  //! import Function.Placeholder; +  //! Function.bind( call_out, Arg(-1), rest) +  //! @endcode +  object rest = class { +  inherit Base; +  constant _splice = true; +  +  array(mixed) value( bind x, array args ) +  { +  array ret = copy_value(args); +  foreach(x->bind_args, mixed arg) +  { +  if( Program.inherits(arg,Arg) ) +  { +  int a = arg->num; +  ret[a] = UNDEFINED; +  } +  else if( Program.inherits(arg,Splice) ) +  { +  int e = min(arg->end+1,sizeof(args)); +  if( e==1 ) e = sizeof(args); +  for(int i=arg->start; i<e; i++) +  ret[i] = UNDEFINED; +  } +  } +  return ret - ({ UNDEFINED }); +  } +  }(); +  +  //! @decl constant arg0; +  //! @decl constant arg1; +  //! @decl constant arg2; +  //! @decl constant arg3; +  //! @decl constant arg4; +  //! @decl constant arg5; +  //! @decl constant arg6; +  //! @decl constant arg7; +  //! @decl constant arg8; +  //! @decl constant arg9; +  //! @decl constant arg...; +  //! arg<n> will return an instance of @[Arg] that returns the n:th arg. +  //! For convenience for c++11 developers _0, _1 etc also works. +  //! +  //! Note that arg0 is the first argument, not arg1 +  +  class Expr(function value) +  //! Expr(x) returns the result of calling @[x]. +  //! The function will be passed the bind instance and the +  //! list of arguments, in that order. +  //! +  //! Function.Placeholder.arg1 is thus more or less equivalent to +  //! @code Expr(lambda(Function.bind b, array args ){return args[1];}); +  //! (however, @[Function.Placeholder.rest] would not know about this usage) +  { +  inherit Base; +  protected string _sprintf() { +  return sprintf("Expr(%O)",value); +  } +  } +  +  private mapping _cache = ([]); +  mixed `[](string name) +  { +  if( _cache[name] ) +  return _cache[name]; +  mixed tmp; +  if(sscanf(name, "arg%d", tmp) || sscanf(name, "_%d", tmp)) +  return _cache[name] = Arg(tmp); +  return ::`[](name); +  }; + }(); +  +  +