ts.
ts.

Reputation: 10709

Polymorphism over argument values (not types)?

Is there a programming language (may be a concept or research paper), which allows a polymorphism over function/method arguments values? Kind of:

function factorial(int value > 0){ /* code here */}
function factorial(int value == 0){ /* code here */}
function factorial(int value < 0){ /* code here */}

And, what is the official name, if any, for this kind of polymorphism?

Upvotes: 4

Views: 129

Answers (3)

Michael Ekstrand
Michael Ekstrand

Reputation: 29090

Pattern matching and guards is one way of doing this; OCaml, Haskell, and Scala all provide them as well.

Prolog has a similar feature: you can define relations that depend on specific values. For example:

factorial(X, 0) :- X =< 0, !.
factorial(1, 1).
factorial(X, Y) :- X2 is X - 1, factorial(X2, Z), Y is X * Z.

In this code, we define a factorial relation such that factorial(X,Y) is satisfied when Y=X!; to do so, we specialize it over three cases, one involving a specific value and another involving a range test.

Yep, Prolog is really weird. Programming consists of writing down true statements; you then query the system for the truth of a particular statement or a variable assignment which makes a statement true. For example, if the above code is saved in factorial.pl:

?- consult(factorial).
% factorial compiled 0.00 sec, 2,072 bytes
true.

?- factorial(3, 6).
true .

?- factorial(5, X).
X = 120 .

?- factorial(4, 25).
false.

Upvotes: 1

deceze
deceze

Reputation: 522081

I guess what you're looking for is pattern matching and/or guards. Erlang for instance allows this:

foo(X) when X > 0  -> bar(X);
foo(X) when X == 0 -> baz(X);
foo(X)             -> X.

foo("bar", X) -> bar(X);
foo(42, X)    -> baz(X);
foo(_, X)     -> X.

The former demonstrates the use of guards, the latter is a simple pattern match, where the first argument is either "bar", 42 or anything else. Both techniques can be found in many functional languages.

Just in case you're not familiar with the syntax, that's equivalent to (as much as it can be compared):

function foo("bar", x) {
    return bar(x);
}
function foo(42, x) {
    return baz(x);
}
...

Upvotes: 8

Ryan Culpepper
Ryan Culpepper

Reputation: 10653

There's a 2006 paper by Matthias Blume called "Extensible Programming with First-Class Cases" that talks about such a system (based on ML, IIRC).

You might be able to do the same sort of thing with some aspect-oriented languages like AspectJ, but I haven't tried it.

Also, in languages like Scheme that support both first-class functions and mutation of names bound to functions, you can extend a function by wrapping the old version:

(define (factorial n)
  1)

(factorial 0) ;; => 1
(factorial 5) ;; => 1

(set! factorial
  (let ([old-factorial factorial])
    (lambda (n)
      (cond [(> n 1)
             (+ (factorial (- n 1)) (factorial (- n 2)))]
            [else
             (old-factorial n)]))))

(factorial 0) ;; => 1
(factorial 5) ;; => 8
(factorial 6) ;; => 13

Redefining a function is accepted for debugging but frowned upon for "real code", and some module systems don't allow mutation of module exports. In that case, an alternative is to have a private mutable variable containing the list of cases; the main function explicitly goes through the cases, and there is a separate function for adding cases.

Upvotes: 2

Related Questions