From: Alex Shabanov on
Several days ago I needed an easy to use and easy to extend facility
that performs pattern matching.

I ended up with the following interface:

// matcher function. To prevent situations when no pattern matches the
object given,
// it might be initialized as follows:
// matcher = function() { throw new Error("no applicable pattern
found") }
var matcher

matcher = combine(PATTERN1, CALLBACK1(OBJ, .. OPTIONAL_ARGS){...},
matcher)
matcher = combine(PATTERN2, CALLBACK2(OBJ, .. OPTIONAL_ARGS){...},
matcher)
// the result of combine method shall be immediately callable to
perform pattern matching
matcher = combine(PATTERN3, CALLBACK3(OBJ, .. OPTIONAL_ARGS){...},
matcher)

....

// perform pattern matching
matcher(OBJ, ... OPTIONAL_ARGS)

Here is a sample usage:

var matcher = function(val, arg) {
print("matcher fallback: val = " + val + ", arg = " + arg)
}

matcher = pm.combine({type: "string"}, function(val, arg) {
print({expr: "matcher(stringVal, arg)", value: "val = " + val
+ ", arg = " + arg})
}, matcher)

matcher = pm.combine({instanceOf: Function}, function(val, arg) {
print({expr: "matcher(functionVal, arg)", value: "val = " +
val + ", arg = " + arg})
}, matcher)

matcher = pm.combine({scheme: {key: "number", value: "any"}},
function(val, arg) {
print({expr: "matcher({key:number, value:any}, arg)", value:
"val = (" + val.key + "," + val.value + "), arg = " + arg})
}, matcher)

matcher(5, "one")
matcher("str", "two")
matcher(new Function("return 1"), "three")
matcher({key: 12, value: 34}, "four")
matcher({key: "some", value: "unk"}, "five")

Here is an implementation:

// namespace
var pm = {}

/**
* Matcher functions constructors are used in pm.combine method.
* Each key in this object corresponds to the certain pattern member.
*/
pm._matcherConstructors = {
instanceOf: function (matcher, instanceTarget) {
return function (obj) {
if (obj instanceof instanceTarget) {
return matcher.apply(this, arguments)
}
return false
}
},

type: function (matcher, typeId) {
return function (obj) {
if (typeof(obj) === typeId) {
return matcher.apply(this, arguments)
}
return false
}
},

scheme: function (matcher, scheme) {
return function (obj) {
if (typeof(obj) !== "object") {
return false
}
for (var i in scheme) {
if (i in obj) {
var target = obj[i]
var source = scheme[i]
var sourceType = typeof(source)
if (sourceType === "string") {
if (source === "any" || source ==
typeof(target)) {
continue
}

return false
}

if (source !== target) {
return false
}
}
else {
return false
}
}
return matcher.apply(this, arguments)
}
}
}

/**
* Creates pattern matching function that accepts the pattern given.
* The latter combined patterns takes priority over the previously
declared ones.
* @param pattern Pattern to match the target object.
* @param callback User-defined callback to accept target object as
well as the accompanying arguments.
* @param prevMatcher Previous matcher function created by combine
method or null or undefined.
* @returns Matcher function to be used as follows:
matcher.call(objectToBeMatched, optionalArguments...).
*/
pm.combine = function(pattern, callback, prevMatcher) {
var matcher = function() {
callback.apply(this, arguments)
return true
}

// join visitor function according to the pattern given
for (var i in pattern) {
if (!(i in pm._matcherConstructors)) {
throw new Error("unexpected pattern tag: " + i)
}

matcher = pm._matcherConstructors[i](matcher, pattern[i])
}

// if prev matcher either undefined or null - create new function
if (prevMatcher == null) {
return matcher
}
else {
return function() {
if (matcher.apply(this, arguments)) {
return true
}
return prevMatcher.apply(this, arguments)
}
}
}

/**
* Helper function that initializes matcher for all the types of
objects with
* the callback that throws an error.
*/
pm.unknownObjectMatcher = function() {
throw new Error("unknown object matched")
}


Comments appreciated :)
From: Thomas 'PointedEars' Lahn on
Alex Shabanov wrote:

> Several days ago I needed an easy to use and easy to extend facility
> that performs pattern matching.
>
> I ended up with the following interface:
>
> [...]
>
> Comments appreciated :)

Looks like an awful lot of code to do what could probably be done in one
line. Perhaps you could explain why one would use your approach instead?


PointedEars
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
From: Alex Shabanov on
On May 14, 4:20 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
wrote:
> Alex Shabanov wrote:
> > Several days ago I needed an easy to use and easy to extend facility
> > that performs pattern matching.
>
> > I ended up with the following interface:
>
> > [...]
>
> > Comments appreciated :)
>
> Looks like an awful lot of code to do what could probably be done in one
> line.  Perhaps you could explain why one would use your approach instead?

This is to avoid using lots of typeofs and ifs to analyze certain
incoming object.
You can think of using this approach as of visitor pattern. In
contrast to visitor pattern, pattern-matching approach deals with
object type or with it's structure or with both.

A benefit to hand-written approach is that pattern matching provides a
kind of abstraction layer to the code what deals with the object's
structure.

>
> PointedEars
> --
> Use any version of Microsoft Frontpage to create your site.
> (This won't prevent people from viewing your source, but no one
> will want to steal it.)
>   -- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)

From: Thomas 'PointedEars' Lahn on
Alex Shabanov wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Alex Shabanov wrote:
>> > Several days ago I needed an easy to use and easy to extend facility
>> > that performs pattern matching.
>> >
>> > I ended up with the following interface:
>> >
>> > [...]
>> >
>> > Comments appreciated :)
>>
>> Looks like an awful lot of code to do what could probably be done in one
>> line. Perhaps you could explain why one would use your approach instead?
>
> This is to avoid using lots of typeofs

By making a lot of comparably more expensive calls instead. The
requirements being made to the capabilities of the implementation are also
more than with `typeof' -- which is not a function, BTW -- and friends.

> and ifs to analyze certain incoming object.

Note that 5 and "str" are primitive values.

> You can think of using this approach as of visitor pattern. In
> contrast to visitor pattern, pattern-matching approach deals with
> object type or with it's structure or with both.
>
> A benefit to hand-written approach is that pattern matching provides a
> kind of abstraction layer to the code what deals with the object's
> structure.

I still do not see the problem this is supposed to solve; except perhaps
extensive method overloading, which is inherently flawed and so should be
designed out of an API to begin with.


PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$8300dec7(a)news.demon.co.uk>
From: Alex Shabanov on
On May 14, 9:50 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
wrote:
> Alex Shabanov wrote:
> > Thomas 'PointedEars' Lahn wrote:
> >> Alex Shabanov wrote:
> >> > Several days ago I needed an easy to use and easy to extend facility
> >> > that performs pattern matching.
>
> >> > I ended up with the following interface:
>
> >> > [...]
>
> >> > Comments appreciated :)
>
> >> Looks like an awful lot of code to do what could probably be done in one
> >> line.  Perhaps you could explain why one would use your approach instead?
>
> > This is to avoid using lots of typeofs
>
> By making a lot of comparably more expensive calls instead.  The
> requirements being made to the capabilities of the implementation are also
> more than with `typeof' -- which is not a function, BTW -- and friends.
>
> > and ifs to analyze certain incoming object.
>
> Note that 5 and "str" are primitive values.

Yep, I know. The "matcher" function given above does understand
primitives as well as objects.

>
> > You can think of using this approach as of visitor pattern. In
> > contrast to visitor pattern, pattern-matching approach deals with
> > object type or with it's structure or with both.
>
> > A benefit to hand-written approach is that pattern matching provides a
> > kind of abstraction layer to the code what deals with the object's
> > structure.
>
> I still do not see the problem this is supposed to solve; except perhaps
> extensive method overloading, which is inherently flawed and so should be
> designed out of an API to begin with.

This is exactly the case I dealt with.
I needed to write a sort of print function that takes one parameter
and prints it in a way that depends on type and structure of the
object given. E.g. strings shall be printed as is, objects with expr
and value properties shall be represented as two differently colored
elements, the object with snippet property shall be represented within
an expandable element, etc.

The other sample is when I needed to display a widget that intended to
show a particular object in particular state. The problem was that
there were too much objects and their states so I ended up with a mess
of numerous if-expressions.

Of course, most of the real life problems are trivial and can be best
expressed with just built-in means but not all of them.
It is funny - I never ever needed to use regular expressions during my
professional career though I wrote literally hundreds of them in
academic years.
I wouldn't say that regular expressions are useless but I've just
never encountered a task that'd require them.

>
> PointedEars
> --
> Prototype.js was written by people who don't know javascript for people
> who don't know javascript. People who don't know javascript are not
> the best source of advice on designing systems that use javascript.
>   -- Richard Cornford, cljs, <f806at$ail$1$8300d...(a)news.demon.co.uk>