Haskell
Haskell and its pattern matching allows to define functions with different patterns for different inputs:
func 0 = 1
func 1 = 2
func n | n < 0 = otherfunc
| n > 1 = other n
but it is a definition, not a modification.
Pattern matching also provides additional features, such as the selection based on the structure of a complex object.
C++, C and similar languages
C++ and other conventional programming languages, allow defining a function returning a reference to a modifiable memory area.
In C++ you can also overload the operator ()
being able in this way
to implement a similar semantic.
But we are not saying that changing the behavior of a callable variable is not possible in a conventional language. The point is that increase expressivity by providing left-side assignment as a feature we can construct a language in which objects, arrays and classes are functions or callable objects.
Chat GPT on Programming Languages allowing f(3) = 9 for a function f
Follows a chat with GPT-3 on the topic of the existence of programming languages that allow you to assign a value to a function at a specific point, like f(3) = 9
.
Question
In most programming languages you can put on the left side of an assignment a property of an object or an index of an array like:
For an array:
a[0] = 4;
or for an object:
a["x"] = 9
but you can't do something like this for the value of a function at a specific point like in:
f(3) = 9;
Do you know of any programming language in which you can write something like this?
Chat GPT Answer
{/*
f(3) = 9;
, is not a common feature in programming languages.
However, there are some languages that allow you to achieve similar behavior using various mechanisms. One such language is Mathematica.
In Mathematica, you can define functions with pattern-matching rules, and you can use these rules to effectively assign values to function inputs. Here's an example:
f[x_] := 2x + 1
(* Now we can define values for specific inputs *)
f[3] = 9
(* You can now call f[3] and it will return the assigned value *)
f[3]
(* Output: 9 *)
In this example, we defined a function f[x_]
which represents a linear equation. Then, we assigned a specific value 9
to f[3]
. Now, whenever you call f[3]
, it will return 9
.
Mathematica's pattern-matching rules and symbolic computation capabilities enable this behavior, allowing for a more symbolic and mathematical programming style compared to traditional imperative languages. However, it's important to note that Mathematica operates in a different paradigm compared to most mainstream programming languages, and direct function value assignment like this is not a common feature in those languages. {//}
Comments to Chat GPT answer
Notice the Mathematica example refers to a "linear equation" and not to a "function".
Google Gemini Answer
Here follows a Google Gemini answer to the exactly the same question:
{/* f(3) = 9
, a few programming paradigms and languages offer features that allow you to achieve a similar effect, treating functions more flexibly:
1. Dynamic Languages with Closures:
-
Python, JavaScript, and other dynamic languages often allow you to create closures—functions that capture and modify variables from their surrounding scope. This can be used to create a function that "remembers" certain values and effectively updates itself.
function createCounter() { let count = 0; return function() { count = count + 1; return count; }; } let myCounter = createCounter(); console.log(myCounter()); // 1 console.log(myCounter()); // 2
2. Prototype-Based Languages:
-
Languages like JavaScript and Lua use prototype-based inheritance, where you can modify an object's methods (functions) directly. This means you can indirectly "update" a function's behavior for a specific instance.
function Person(name) { this.name = name; this.greet = function() { console.log("Hello, I'm " + this.name); }; } let person1 = new Person("Alice"); person1.greet(); // "Hello, I'm Alice" person1.greet = function() { console.log("Hi there! "); }; person1.greet(); // "Hi there! "
3. Semantic Function Assignment (Experimental):
- Some languages like R and Julia have explored "semantic function assignment," which allows you to create aliases or modify function definitions in certain contexts. However, this feature is often limited and not universally supported.
4. Symbolic Programming:
- In symbolic programming languages like Mathematica or SymPy, functions are often treated as symbolic expressions that can be manipulated algebraically. This might allow for constructs that resemble assigning to function values, but it's a very different paradigm from traditional programming.
{//}
{/
Wolfram Mathematica
For a introduction to Wolfram Mathematica see:
- MATHEMATICA TUTORIAL for the First Course: Functions (opens in a new tab) by Prof. Vladimir Dobrushkin at Brown University.
- Answers to the question: How to modify function argument? (opens in a new tab) at https://mathematica.stackexchange.com/ (opens in a new tab)
To define a function, just type in the formula. We need to use a special form for the left hand side, which includes
- an underscore after the name of the variable, and
- a special "colon-equals" sign
:=
for the function definition:
f[x_] := (Cos[x] -1)/ x^2
The symbol x_
, pronounced x-blank, denotes a pattern named "x." For example, If one were to enter
f[x] =3*x+1
then
f[3]
andf[a+b]
will not be evaluated, butg[x]
will be evaluated becausex
is set as the only variable that this function will accept.
Function evaluation in Mathematica is indicated by square brackets.
That is, while in mathematical notation, we write , in Mathematica the correct syntax is f[x]
.
Ordinary parentheses are used exclusively for algebraic grouping.
Thus we write (a*t)
to indicate that the argument is the product of a
and t
.
However, if f[x_]=3*x+1
is entered, then
f[2]
is 7,f[a+b] = 3a+3b+1
, andf[x] is 3x+1
because entering x_
allows me to plug in different values for "x-blank."
Quote from How to modify function argument? (opens in a new tab) at https://mathematica.stackexchange.com/ (opens in a new tab):
... Whether or not the expressions passed as arguments can be modified inside a function depends on whether or not they represent an L-value (meaning they can be assigned a value, e.g. via
Set
operator) ... for the following function:
f[x_]:=x=1
this will assign to a
:
Clear[a];
f[a]
while this will result in an error:
a=2;
f[a]
because in the first case, the symbol
a
is injected into the r.h.s. (it is of course first evaluated, but since it does not have any value, it evaluates trivially, to itself), while in the second, it already has a value, so it's immutable value2
is injected.... Note however, that this "injection" parameter-passing mechanism is highly unusual, from the point of view of traditional languages. In essence, parameter-passing acts as a code-generation device, assembling full function's code in a macro-like fashion from code pieces passed as parameters and the body which has "placeholders" and acts like a macro. One can say that in Mathematica, run-time code generation happens at each function invocation.
SymPy
SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS).
If you wish to contribute to SymPy or like to get the latest updates as they come, install SymPy from git. To download the repository, execute the following from the command line:
git clone https://github.com/sympy/sympy.git
Here is a simple example of how to use SymPy:
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> expr = x + 2*y
>>> expr
x + 2*y
>>> expr - x
2*y
>>> expr.evalf(subs={x:4})
2.0*y + 4.0
>>> expr.evalf(subs={x:4}).evalf(subs={y:1})
6.00000000000000
>>> from sympy import Piecewise, Eq, pprint
>>> f = Piecewise((0, Eq(x, 0)), (x + 1, True))
>>> pprint(f, use_unicode=True)
⎧ 0 for x = 0
⎨
⎩x + 1 otherwise
>>> f.subs(x, 0)
0
>>> f.subs(x, 1)
2
Julia
Julia features optional typing, multiple dispatch, and good performance, achieved using type inference and just-in-time (JIT (opens in a new tab)) compilation (and optional ahead-of-time compilation (opens in a new tab)), implemented using LLVM (opens in a new tab). It is multi-paradigm, combining features of imperative, functional, and object-oriented programming.
See section Redefining methods (opens in a new tab) of the Julia documentation:
julia> f(x::Real) = 2*x
f (generic function with 1 method)
julia> f(x::Int) = x
f (generic function with 2 methods)
julia> f(3)
3
julia> f(1.5)
3.0
Also:
julia> struct Polynomial{R}
coeffs::Vector{R}
end
julia> function (p::Polynomial)(x) # defines a callable object for type Polynomial.
v = p.coeffs[end] # last coefficient
for i = (length(p.coeffs)-1):-1:1
v = v*x + p.coeffs[i] # calculates the value of the polynomial at x using Horner's method
end
return v
end
julia> (p::Polynomial)() = 4
julia> p = Polynomial([1,10,100])
Polynomial{Int64}([1, 10, 100])
julia> p(3) # 100*3^2 + 10*3 + 1 = 900 + 30 + 1 = 931
931
julia> p()
4
Lab left-side
The lab left-side is a lab that explores answers to this question.import { throwDeprecation } from 'process'