[info]en_dmitriid


Tigers, and lions, and bears, oh my!


Shame on me
happy
[info]dmitriid
Shame on me. I had a mistake in my previous pos. Thanks to [info]xaprb for pointing this out.

I misplaced quote marks in my second query.

Instead of

date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000' AND accomodation=1


I wrote this:

date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1'


Note the last quote mark after accomodation=1

:)
Tags: ,

Those evening queries...
happy
[info]dmitriid
I'm dealing with databases right now while keeping an eye on «High performance MySQL». Finally decided to try something out. Here we go...

We have a table split into partitions be week:


CREATE TABLE `price_detail` (
`accomodation` int(11) NOT NULL,
`date` datetime NOT NULL,
`price` int(11) NOT NULL,
`room_id` int(11) NOT NULL,
KEY `idx_price_date` (`date`) USING BTREE,
KEY `idx_price` (`price`) USING BTREE,
KEY `idx_price_accomodation` (`accomodation`) USING BTREE
)
PARTITION BY RANGE (TO_DAYS(`date`)) (
PARTITION p20080101 VALUES LESS THAN (733407),
PARTITION p20080108 VALUES LESS THAN (733414),
/* and so on — an entire years worth */


Yep... We have indexes as well...

We have 26 million rows per week in the worst case. Needless to say that we have the worst case for testing purposes .

Let's see how a simple query will work on such a database:

Query I:

explain partitions 
  select 
    accomodation 
  from 
    price_detail 
  where 
    accomodation=1 and date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000'\G;

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: price_detail
   partitions: p20080108,p20080115
         type: ref
possible_keys: idx_price_date,idx_price_accomodation
          key: idx_price_accomodation
      key_len: 4
          ref: const
         rows: 40292
        Extra: Using where


Query II:


explain partitions 
  select 
    accomodation 
  from 
    price_detail 
  where 
    date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1'\G;

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: price_detail
   partitions: p20080108,p20080115,p20080122,p20080129,p20080205,p20080212,p20080219,
                     p20080226,p20080304,p20080311,p20080318,p20080325,p20080401,p20080408,
                     p20080415,p20080422,p20080429,p20080506,p20080513,p20080520,p20080527,
                     p20080603,p20080610,p20080617,p20080624,p20080701,p20080708,p20080715,
                     p20080722,p20080729,p20080805,p20080812,p20080819,p20080826,p20080902,
                     p20080909,p20080916,p20080923,p20080930,p20081007,p20081014,p20081021,
                     p20081028,p20081104,p20081111,p20081118,p20081125,p20081202,p20081209,
                     p20081216,p20081223,p20081230,p20090106,p20090113,p20090120,p20090127
         type: ALL
possible_keys: idx_price_date
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 274845000
        Extra: Using where


What a nasty surprise!

I'm off learning about covering indices...
Tags: ,

For whom the headhunt tolls...
happy
[info]dmitriid
We're hiring now.

The first line in job description reads: Good or excellent knowledge of PHP

Here's what we get: "I've just started learning РНР...»

Perhaps, we should've stated our requirements more clearly... Somehow... :)
Tags:

A hundred wise men
happy
[info]dmitriid
via http://andrzejn.livejournal.com/1152676.html

- A hundred wise men know that something is impossible. Then a fool comes along...

- And he makes a discovery?

- No. However, the wise men remember him with tenderness for long afterwards.

Musings
happy
[info]dmitriid
As I promised here are my thoughts:

1. First of all, pattern matching lets you describe what yo eed with little or no effort. For example, consider these rules:

{field}
{field, Func}
{field, {'=', field2}}


Frankly, I have a very vague idea how these could be matched using ifs only. I guess you would create a Rule class and inherit a slew of classes from it. Something like RuleField, RuleFunc, RuleOperator and so on. If you use pattern matching, however, the rules are easily parsed::

%%{field}
validate_rule({FieldName}) ->
    ok.

%%{field, Func}
validate_rule({FieldName, Func}) when is_function(Func) ->
    ok.

%%{field, {'=', field2}}
validate_rule({FieldName, {Operator, FieldName2}}) ->
    ok.


Such ease, however, may bring (and it does bring me) to my second thought:

2. WTF-ish code. The thought that you can easily slice through complex constructs makes you write before you think.

In my case I knew what I wanted to pass to the function, but I had no idea what I expected the function to return. Ok, I've parsed the rules, what now?

As a result, the first version of the function would return a deeply nested list of lists that looked something like this:


[[Field, [Error1, Error2]], [Field2, [Error3, Error4]]]


It took me two additional refactorings to make it return a properproplist.

This same "wow, look at how I handle things!" approach resulted in an ugly preprocessing of results before I return these to the user:

lists:filter(
    fun(Elem) -> 
        case 
            Elem of {} -> false; 
            {_, []} -> false;
            _ -> true 
        end 
    end, 
    lists:flatten(validate1(A, ValidationRules, [])))


Yup. Get rid of those unwanted elements before the user sees them. Are you scared? I am. I m saddened as well :(


Here's where my train of thoughts stops...
Tags:

Validation for erlyweb
happy
[info]dmitriid
I've written a validation function for Erlyweb. First I'll tell you how it works and then, perhaps, more thoughts on its internals.

Create a form with four fields:
- login
- password
- pasword_repeat
- email

This is a pretty standard registration form. Naturally we'd have to validate input coming from this form:
- login has to be 4-16 symbols in length
- password has to be 4-16 symbols in length
- password has to be the same as password_repeat
- email has to be a valid email address

Erlyweb's validation functions cant cope with this. My function can :)

Suppose you have a function called process_signup, which accepts the yaws_arg record. Then the validation will look like so:

process_signup(A) ->
	F = fun(A, Field) ->
		{ok, Val} = yaws_api:postvar(A, Field),
		L = string:len(Val),
		if
			L < 4 orelse L > 16 ->
				{Field, length};
			true ->
				{}
			end
	end,
	EmailCheck = fun(Args, Field2) ->
		{ok, Email} = yaws_api:postvar(Args, Field2),
		Match = regexp:match(Email, "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+"),
		Match /= nomatch
	end,

	%% magic is here:
	buktu_form:validate(A, [
		{login, F},
		{email, EmailCheck},
		{password, [{'=', password_repeat}, F]}
	]).


If we don't input any field at all, we'll get back the following list:
[{login,invalid_field},
 {email,invalid_field},
 {password,[{invalid_fields,[password,password_repeat]},
            invalid_field]}]


If we input values that don't match our criteria, we'll get:
[{login,length},
 {email,invalid_value},
 {password,[{not_equal,password_repeat},
            length]}]


  • The callback function that you can pass can return the following:

    • a tuple {FieldName, Error}

    • true if the field is validated and false otherwise (then the validation function will return {FieldName, invalid_value})

    • any value Value which will be transformed into {FieldName, Value}


  • if any of the fields in the rule don't exist or are empty, for each such rule the validation function will return:

    • invalid_field if the field is compared against a value

    • {invalid_fields, [field1, field2]} if the field is compared against another field


  • If you need to bind several rules to a field, pass a list of rules. If you just need to check if a field exists, pass in a tuple containing the field's name:

    • Does the field exist?
      {field_name}

    • Compare a field field_name to field field2_name (you can use '=', '/=', '<', '=<', '>', '>=' )
      {field_name, {'=', field2_name}}

    • Compare a field to any value:
      {field_name, {'=', Value}}

    • Use a callback function (function/2, first parameter is yaws_arg, the second is the field's name). The function can be a lambda or any function of any module in the form of module:function/2 or {module, function}
      {field_name, F}

    • Use a callback function with an additional value (function/3,first parameter is yaws_arg, the second is the field's name, the third is the value). The function can be a lambda or any function of any module in the form of module:function/3 or {module, function}
      {field_name, {F, Value}}


  • The validation function returns a proplist:
    [{FieldName(), Errors()}]

    where
    FieldName() = atom()
    Errors() = Error() | [Error()]
    Error() = user_defined_values | absent | invalid_value | 
              invalid_field | {invalid_fields, [FieldName(), FieldName()]} |
              ComparisonError()
    ComparisonError() = {not_equal, value_or_field} | {equal, value_or_field} |
                        {not_greater, value_or_field} | {not_less, value_or_field} |
                        {not_greater_or_equal, value_or_field} | {not_less_or_equal, value_or_field} |
    

Apple...
happy
[info]dmitriid



there it is.... quite unusual, but there is no way back...
Tags:

Somebody stop me
happy
[info]dmitriid
Or shoot me before I suffer too long :)

I've managed to get one of the oldest Firefox bugs going: https://bugzilla.mozilla.org/show_bug.cgi?id=69230 (see comment 86). I have know idea what to do with it now :) And there's the possibility that I will have to compile Mozilla from the source...

"Tell my wife and children that I love them..." :))

Erlang Bit Syntax
happy
[info]dmitriid
I've decided to see whether all is as well with Erlang's bit syntax as advertised..

A friend of mine has been struggling with SOny's OMA format which is being used in Sony's players. The only feature this format has that distinguishes it from "normal" formats is its lack of documentation and whatever documentation there is differs significantly from whatever actual files have to offer :)Read more... )
Tags:

Programming Erlang
happy
[info]dmitriid
It has finally arrived!

Programming Erlang Programming Erlang Programming Erlang

It arrived at Moldova on Juky 21st. It got out of customs on July 25th. And it is only today that I finally noticed the invitation to pick it up at my local post office. :)

Hurrah!

Now if I only could find time to study it...
Tags:

Keep'em languages coming!
happy
[info]dmitriid
Past Friday I got myself acquainted to Python.

Different people view Python as "nothing to learn" or as "if only I had time to learn" or even as "very difficult to learn".

I had to learn it quick for a very simple reason. I have lots of photos taken at different resolutions. I need to resize them to different sizes (much like Flickr does), create several thumbnails of each image, upload them to a host (Amazon S3) and load information on uploaded images to a database.

The process must be automated, of course.

It all started when I convinced myself that Python Imaging Library is the best library to manipulate images. Indeed:

  1. import Image
  2.  
  3. im = Image.open('path/to/image')
  4. im.thumbnail((800, 800), Image.ANTIALIAS)
  5. im.save('path/to/image')


The line with im.thumbnail((800, 800), Image.ANTIALIAS) is absolute genius. If your image is, say, 1024х768px, then what you get in the end is not a skewed 800х800px, but a proportional 800x600px.

Combined with MySQLDb and Boto this yields a very easy solution to my task. However, that's not the point.

As it turns out, getting to know many languages (even superficially) may give you a huge head start in learning a new programming language. I've had an overview or, sometimes, more than an overview of such languages as Lisp, Haskell, Erlang... How does that help me with Python? Well, I got an imeddiate understanding of the following:

Note: I can bear no responsibility for Haskell codes in the examples :)


Tuples and destructuring assignment

A tuple is a fixed-length list. This list may contain elements that are very different in nature:
  1. # Python tuple
  2. (1, 2, 'hello')
  1. -- Haskell tuple
  2. (1, 2, "hello")
  1. %% Erlang tuple
  2. {1, 2, 'hello'}


Looks similar, doesn't it? :)

Let now get data out of our tuples:
  1. # Python
  2. # А will contain 1
  3. # В will contain 2
  4. # С will contain 'hello'
  5. A, B, C = (1, 2, 'hello')
  1. -- Haskell
  2. -- a will contain 1
  3. -- b will contain 2
  4. -- c will contain 'hello'
  5. (a, b, c) = (1, 2, "hello")
  1. %% Erlang
  2. %% А will contain 1
  3. %% В will contain 2
  4. %% С will contain 'hello'
  5. {A, B, C} = {1, 2, 'hello'}


Certainly, the principles of theses examples are very different. Haskell and Erlang use pattern matching that Python doesn't have. However, it looks very similar and is very easy to understand once you know the other examples. PHP, by the way, has a similar thing:
  1. // А will contain 1
  2. // В will contain 2
  3. // С will contain 'hello'
  4. list($A, $B, $C) = array(1, 2, 'hello')


This, of course, is very different and the inner workings are absolutely different :) One thing about this is quite peculiar, though. Once you get used to these sorts of destructuring assignments, you start using PHP's list and Python's tuples much more often. Because it's so convenient.

Named parameters

I once talked about named parameters. As it turns out, Pythons has them.
  1. # define a function
  2. def myfunc(param='', another_param=1)
  3.     print param, another_param
  4.  
  5.  
  6. # call the function
  7.  
  8. # will print hello 5
  9. myfunc(another_param=5, param='hello')
  10.  
  11. # will print hello 1
  12. myfunc(param='hello')
  13.  
  14. # will print 1
  15. myfunc()


Anonymous functions (lambdas)

  1. # Python
  2. # define a list
  3. li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. # get only evens out of the list
  6. li_even = filter(lambda item: item % 2 == 0, li)


Very similar to code in other languages:

  1. -- Haskell
  2. -- define a list
  3. li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. -- get only evens out of the list
  6. li_even = filter (\item -> (mod item 2) == 0) li
  1. %% Erlang
  2. %% define a list
  3. Li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. %% get only evens out of the list
  6. Li_even = lists:filter(fun(Item) -> Item rem 2 == 0 end, Li)


List comprehensions

Remember how we used to define lists in mathematics? For instance:
  1. S = [1, 2, 3, 4]
  2. M = {x | x in S, x even}


M is all x's where x belongs to S and x is even. Nice languages have ways to define lists in a very mathematical way. This is called list comprehensions. One downside to Python's comprehensions is the syntax. Here are previous examples rewritten to use list comprehensions instead of lambdas:

  1. # Python
  2. # define a list
  3. li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. # get only evens out of the list
  6. li_even = [x for x in li if x % 2 == 0]
  1. -- Haskell
  2. -- define a list
  3. li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. -- get only evens out of the list
  6. li_even = [x | x <- li, (mod x 2) == 0]
  1. %% Erlang
  2. %% define a list
  3. Li = [1, 2, 3, 5, 9, 10, 256, -3]
  4.  
  5. %% get only evens out of the list
  6. Li_even = [X || X <- Li, X rem 2 == 0]




Anyhow, thanks to other languages the new language (a more or less mainstream language, not K :)) ) is not just easy, it's extreamly easy and can be learned in, say, 15 minutes.



Update: Corrections to Haskell code. Thanks, [info]deni_ok

Pragmatic Erlang
happy
[info]dmitriid
Almost 60 dollars left my pocket to get Joe Armstrong. "Programming Erlang. Software for a Concurrent World". It's finally mine! As a PDF only so far. I'll get to reading it eventually......

Yeah, baby! :)
Tags:

NY
happy
[info]dmitriid
HAPPY NEW YEAR TO ALL FRIENDS AND ENEMIES!!!
Tags:

Orcas official site
happy
[info]dmitriid
Orcas has been moved to http://orcas.dmitriid.com/

Project description etc etc can be accessed via project wiki at http://orcas.dmitriid.com/wiki
Tags: ,

Refactoring, pattern matching, function guards and Erlang
happy
[info]dmitriid
I'm still late on my promise to talk about pattern matching even though today's post is related to patern matching.

In brief here is what pattern matching all about. The all-knowing Wikipedia defines pattern matching as
  the act of checking for the presence of the constituents of rigidly specified pattern

What does this mean though? This means that when a function (especially in dynamically typed languages) accepts parameters of different kinds/types we can define this function like this::
  
fun([H|T]) ->
    do_smth();

fun({a, tuple}) ->
    do_smth_else();

fun(value) ->
   do_a_third_thing();

fun(DefaultValue) ->
   do_default_thing().

What happens in the code above?
  
1. First function accepts a list/array as its parameter. The list consists of a Head and a Tail. So, if we pass [1, 2, 3, 4] to this function, the variable H will contain 1 and variable T will contain [2, 3, 4]

2. The second function accepts a tuple, that consists of two immutable values, "a" and "tuple"

3. The third function accepts an immutable value of "value"

4. The fourth function accepts everything rejected by the first three functions

So. If we call a function like this:
  
fun([2, 5, 72]).

we will trigger the first function, since it accepts an array/list.

However, if we call the function like so:
  
fun({another, tuple}).
 

we will trigger the fourth function. Why? Because even though we pass a tuple, the third function accepts only one tuple, {a, tuple} whereas we pass a different tuple, {another, tuple}.

So what's refactoring got to do with all this? And what's function guards?

A couple of things about function guards. Consider the ubiquitous Fibonacci function. First, pattern matching:
  
fib(1) -> 1; 

fib(2) -> 1; 

fib(X) -> fib(X-1) + fib(X-2).


Now, function guards:
  
 
fib(X) when X =< 3 -> 1; 

fib(X) -> fib(X-1) + fib(X-2).

The first line reads: "If X is less than 3, return 1".

Guards can check for a wide range of conditions, including such thnigs as is_list(X), is_tuple(X) etc. They can also be stacked using such keywords as "and", "or" etc. More on them in forecoming topics. Back to the topic at hand.

A question was raised over at RSDN with regard to implementation of the Factory pattern. For instance, in creating a wrapper for various database connections. The following С++ code was given as an example:
  
class Driver
{
public:
  virtual void connect() = 0;
};

class MysqlDriver : public Driver {}; //implements connect() using some mysql API calls.
class OracleDriver : public Driver {}; //implements connect() using some oracle API calls.
 


And, finally,
  
class DriverFactory
{
public:
  static Driver * createDriver( string name ) //creates necessary driver
  {
    Driver * driver = 0;
    if("mysql" == name ) driver = new MysqlDriver();
    else if( "oracle" == name ) driver = OracleDriver();
    else throw 1;
    return driver;
  }
};
 


The key to this factory, is of course the following:
  
if("mysql" == name ) driver = new MysqlDriver();
    else if( "oracle" == name ) driver = OracleDriver();
    else throw 1;
 


Ok. Let's rewrite it using Erlang.

First that springs to mind is the obvious line-by-line translation of the C++ code:
  
start(Driver) ->
    case Driver of
        mysql ->
            % connection happens here
            {};
        mnesia ->
            % connection happens here
            {};
        odbc ->
            % connection happens here
            {}
    end.


It's clear that you would call the function as follows:
  
start(mysql).
start(mnesia).
start(odbc).

Note that the "case" construct in Erlang also employs pattern matching.

What if connection i a bulky piece of code spanning 10-20 lines? We could employ function guards of course::
  
 
start(Driver) when Driver == mysql ->
    % connection happens here
    {};

start(Driver) when Driver == mnesia ->
    % connection happens here
    {};

start(Driver) when Driver == odbc ->
    % connection happens here
    {}.

This is more like it. Function call hasn't chaned a bit: start(driver_name). Guards, however, are not expressive enough. You have to read the definition of the function and only after that your eye cathes the guard. Is there a better way to do this? Of course!

Recall that pattern matching involves a "rigidly defined pattern". What's more rigid than
  
start(mysql) ->
    % connection happens here
    {};

start(mnesia) ->
    % connection happens here
    {};

start(odbc) ->
    % connection happens here
    {};


We can improve on our example and introduce options that will be passed to the connection code:
  
start(Driver) ->
    start(Driver, []).
        
start(mysql, Options) ->
    % connection happens here
    {};
    
start(mnesia, Options) ->
    % connection happens here
    {};
    
start(odbc, Options) ->
    % connection happens here
    {};
    
start(_, _) ->
    {error, driver_not_supported}.

Last lines simply states that for any other type of connection we through an error. Underscore means "any variable", a sort of "joker".

That's a part of what, to me, is the "Erlang Way". Very shortly and quite inaccurately this could be defined as: focus on what you need. A slight improvement of this definition is described in the concept of "aggressve programming". See. Erlang programming rules for a description.

That is, a program/module/function should do only the things it's meant to do. Under all other circumstances it should fail specifying why it failed, because it really should be the problem of the person who decided to (ab)use the program/module/function in the wrong way. As a result you end up with a small number of functions/modules which only do what they are meant to do. And this is quite handy

The same page lists other things in the "Erlang way", in particular:
  
- top-down programming (you start with abstractions and work towards details),
- "make it work correctly now and worry about otimizations later",
- principle of least astonishment (the system must behave predictably)
- "aggressive" programming etc.


Actually, this approach can be used - and is used! - in mainstream languages such as C++, Java, C#. However, in order to employ this approach in these languages, you need experience (quite a lot of it some times - a nod to C++). Erlang, on the other hand, encourages this approach right from the start.

I general, I'm not going to give up on Erlang in the forseeable future :) Unless it's in favour of Ne,erle, perhaps :))
Tags:

erlsoap
happy
[info]dmitriid
Opensource projects, eh? They make you involve in all sorts of things :) Thanks to the ever unpredictable Lady Luck, Anton Fedorov decided to bring the aforementioned erlsoap library up to date.

So the battle ensued between me and the shiny new 0.4 version of erlsoap. This version decided it didn't like RSDN's webservice. After some fruitful discussion with Anton a new, 0.4.1, version of erlsoap appeared. This version, however, didn't suit qrilka in the least :)) The problem lay with the inets library distributed with Erlang and used by erlsoap. Inets, as it turned out, doesn't support proxy authentication.

So what's a developer got to do? I geared up and went to hunt... ehm... to add support for a more advanced ibrowse library. As a result, I've officially added my first patch to an opensource project which even brought me a mention in the soapclient module in erlsoap's new version 0.4.2.

Now I'm tuly happy :)
Tags: ,

The cavalry is coming!
happy
[info]dmitriid
Camrade qrilka has shown interest in Orcas. Right now we are not quite sure as to what is the best approach to SOAP in Erlang.

Since RSDN's webservice is quite small, one approach would be to implement all calls and parse all responses by hand. But that's boring, isn't it? :)

Today I've braced myself and went to war with erlsoap, which has been stagnating for the past two years. Well, you can check out the code yourself. The file is test_soap.erl, and you run it by
  
test_soap:run().


qrilka suggested that we use Smerl. This still leaves quite a lot to do by hand. Anyway, tomorrow, time permitting, we'll take a look into the matter.
Tags: ,

Orcas
happy
[info]dmitriid
Well, I've slowly started working on an offline client for RSDN.ru in Erlang.

Here's the project page: Orcas
Here's anouncement of the fact on RSDN (in Russian)

(no subject)
happy
[info]dmitriid
Kosma Prutkov. Fruits of thought. # 66

Many a thing we do not understand not because our reasoning is weak, but because we have no notion of them

Dynamic languages rule
happy
[info]dmitriid
It seems to me that dynamic languages...That is, languages which have more dynamics built in... That is, nice languages to put it simply :)) Well, they rule.

I've been browsing Jakarta Commons' source code using http://www.koders.com/ (I actualy only needed their WordUtils in order to rewrite them in ColdFusion). An then I see Validate.java:
    public static void isTrue(boolean expression, String message, Object value) {
        if (expression == false) {
            throw new IllegalArgumentException(message + value);
        }
    }

    public static void isTrue(boolean expression, String message, long value) {
        if (expression == false) {
            throw new IllegalArgumentException(message + value);
        }
    }

    public static void isTrue(boolean expression, String message, double value) {
        if (expression == false) {
            throw new IllegalArgumentException(message + value);
        }
    }

    public static void isTrue(boolean expression) {
        if (expression == false) {
            throw new IllegalArgumentException("The validated expression is false");
        }
    }


:)) Static typing is the answer to everything they say, but at what cost? :))) I wonder if this could be rewritten... ColdFusion-style:
<cffunction name="isTrue">
    <cfargument name="expression" type="boolean" required="yes">    
    <cfargument name="message" type="string" required="no">
    <cfargument name="value" type="any" required="no">  
	
	<cfset msg=IIF(IsDefined('arguments.message'), DE(arguments.message), DE("The validated expression is false"))>
	<cfset v=IIF(IsDefined('arguments.value'), DE(arguments.value), DE(""))>
	
	<cfif arguments.expression EQ false>
		<cfthrow type="IllegalArgumentException" message=msg & ToString(v)>
    </cfif>
</cffunction>


Key moments here are required="true|false" in argument definitions and the IsDefined('arguments....') function which, well, defines whether optional arguments exist. Oh, and don't forget about type="any" which allows you to pass arguments of any type. And the ToString function :)

Function overloading - is, undoubtedly, great. But I'll trade it IsDefined most of the time (when it's uses are justified, of course :) )

PS. Correct me if I'm wrong, but does Java really not have access to the array of arguments passed to a function? And I'm not talking about Variable arity.

Home