1 405 views (last 30 days)

Show older comments

Let's say MathWorks decides to create a MATLAB X release, which takes a big one-time breaking change that abandons back-compatibility and creates a more modern MATLAB language, ditching the unfortunate stuff that's around for historical reasons. What would you like to see in it?

I'm thinking stuff like syntax and semantics tweaks, changes to function behavior and interfaces in the standard library and Toolboxes, and so on.

(The "X" is for major version 10, like in "OS X". Matlab is still on version 9.x even though we use "R20xxa" release names now.)

Andrew Janke
on 11 Sep 2021

Walter Roberson
ungefĂ¤r 17 timmar ago

Jan
on 16 Sep 2021

A complete list of changes for each command.

Currently we find "introduced in Rxy" already, but modifications of inputs and outputs are very useful also. Examples: When did unique introduce the 'legacy' flag? When did strncmp change the behaviour for empty strings and n=0?

Tucker Downs
on 11 Sep 2021

Yes! I think in all established products it's occasionally neccissary to make major pruning of older functionality for the good of the product / eco system. In companies I've worked for we've done this and made plenty of annoucements "Your legacy code might not work in version!!!! but we have guides on how to change it / we will support old matlab for the next X (many many) years."

For the most part it's always been well recieved.

I'll add

max(2,[]) should not return []

++ incrementing

maps as a more prominant base data type

expose more internal apis for making subclasses for plot objects, like custom arrows

Matt J
on 17 Sep 2021 at 8:50

Edited: Matt J
on 17 Sep 2021 at 9:11

My wish list:

(1) Colon operator produces column vectors, not row vectors:

x=1:4

(2) Optimization Toolbox solvers should have only one algorithm per solver, i.e., instead of,

x1=lsqnonlin(fun,x0,lb,ub, optimoptions(@lsqnonlin,'Algorithm','levenberg-marquardt'))

x2=fminunc(fun,x0, optimoptions(@fminunc,'Algorithm','trust-region'))

we would just have

x1=lsqnonlinLevMarq(fun,x0,lb,ub)

x2=fminuncTrustReg(fun,x0)

etc...

(3) The Image Processing and the Computer Vision Toolboxes would be designed around the coordinate conventions of ndgrid() instead of meshgrid().

(4)One-dimensional array types, i.e., with ndims(X)=1.

Tucker Downs
ungefĂ¤r 22 timmar ago

@Paul I don't think the dot operator is necisary in your example. Just transpose.

Only one character, but for something I type A LOT it's syntax convieniences like this that matter. Also, for anyone else reading, you can capture a colon-operator list and transpose it to a column. Not as convinient but maybe helpful.

for v = x(:)'

end

y = [[1:5]' [3:7]']

gwoo
on 29 Sep 2021 at 18:14

I don't know the technical name for it but being able to call methods, properties, or indexing without having to make a new variable first. Kind like in python where you can call a function that will output an array or whatever and instead of saving it to a variable first and then indexing, you can just index right off the end of the function call. I know you can do this for strings and structs, but not for cells or arrays. Also, being able to perform a series of functions on an array, the way you can now with strings.

For example:

[5, 1, 2](2) = 1

horzcat([3;2;1], [5;6;7])(3,2) = 1

Munin
on 29 Sep 2021 at 21:59

Edited: Munin
on 30 Sep 2021 at 6:41

An LSP for other IDEs, better documentation of the Python engine, easier install of MEfP using some kind of shell script or dep manager, and a modern IDE UI supporting dark theme.

Also all components like Coder require a support of MATLABs licensing scheme so that they are usable in CI etc.

Walter Roberson
on 17 Sep 2021 at 10:44

Edited: Walter Roberson
on 19 Sep 2021 at 2:18

Currently the model of MATLAB is that it always evaluates from left to right [*] finding the left-most unprocessed sub-expression and evaluating it, and then finding and evaluating the right hand side operand, and then performing the operation. The right operand is not processed until the left is evaluated, but unless the left operand results in an error, or the operation is && or || the right will always be evaluated.

[*] exception: there are some funky things with chains of ^ and .^ operators, they are not left strictly left to right.

This behavior prevents there from being function forms of if/else operations -- there is no equivalent to C's ?: operation. In C, the unselected operation is not evaluated at all.

The hack work-arounds require embedding the work to be done inside an anonymous function and writing a function like

function varargout = ifelse(expr, basepart, elsepart)

if expr

if isa(base_part, 'function_handle')

[varargout{:}] = basepart();

else

varargout{1} = basepart;

end

elseif isa(elsepart, 'function_handle')

[varargout{:}] = elsepart();

else

varargout{1} = elsepart;

end

end

and using that gets ugly... and probably messes up multiple output processing.

Piecewise(x ~= 0, 0, 1./x)

can't be done and would have to look like

Piecewise(x ~= 0, 0, @(x)1./x)

I would like to see a cleaner way of handling this -- one in which the function being called does not need to know that a delayed evaluation is being done.

In the Maple programming language, there are two related mechanisms available. First, there is a simple syntax to delay evaluation. This is indicated by using ' ' around the expression. For example,

Piecewise(x <> 0, 0, '1/x')

In Maple, this is not a quoted string: Maple uses double-quotes for strings. Instead it is a delayed evaluation. Each time the relevant expression is evaluated, one level of unevaluation is removed; when it is eventually evaluated in a context where there are not remaining protective uneval() levels, then the expression is evaluated.

Secondly, Maple allows procedures (that is, functions) to declare a parameter as being of type "uneval", which has the effect of adding a layer of uneval around what is passed in. For example,

Piecewise := proc(x, basepart::uneval, elsepart::uneval) #stuff; end proc;

would permit uses to code

Piecewise(x <> 0, 0, 1/x)

and the 1/x will not be evaluated before being passed in to the procedure.

Some programming languages deal with these kinds of issues by using "lazy evaluation". Something like

Piecewise(x <> 0, 0, 1/x)

would not evaluate any of the parameters until such time as the code inside Piecewise asked for their value -- so if the code logic did not ask for the value of a particular parameter, it would never be evaluated.

If I understand correctly, tallarray() already does some delayed evaluation, building up expressions and then internally finding ways to reduce the memory access during evaluation.

Walter Roberson
on 19 Sep 2021 at 3:32

This would not break backwards compatibility, but something to consider:

A lot of time, people try to

for x = first:increment:last

with non-integer increment. And then they want to

f(x) = value;

but of course x is non-integer so that fails.

There are standard ways of rewriting this: the common

counter = 1;

for x = first:increment:last

f(counter) = value;

counter = counter + 1;

end

x =

or (less likely by far, but cleaner since counter is more sensible)

counter = 0;

for x = first:increment:last

counter = counter + 1;

f(counter) = value;

end

or the formal and flexible

xvals = first:increment:last;

num_x = numel(xvals);

f = zeros(1, num_x);

for xidx = 1 : num_x

x = xvals(xidx);

f(xidx) = value;

end

But... keeping those counters is a bit of a nuisance, and people get them wrong.

So I would suggest something I have seen in a couple of programming languages: that there be an accessible automatic counter. We could imagine, for example,

for x = 0:.01:2*pi

f(#x) = sin(x.^2 - pi/7);

end

where the #x translates as "the number of x values we have processed so far".

Indexing a variety of arrays with the same # would be considered valid, so you could write

for x = 0:.01:2*pi

f(#x) = sin(x.^2 - phase(#x));

end

But now we have a question that might lead to some backwards incompatibility: suppose we have

for x = 0:.01:2*pi

y = 0;

for x = 1 : .5 : 5

y = y + z.^(x-1)./gamma(x+1);

end

f(#x) = sin(x.^2 - y);

end

and the question is: in that f(#x) that is after the nested for x, should the #x refer to

- the last index associated with the inner x?
- the index after the last one associated with the inner x?
- the index associated with the outer x?

Consistency with existing nested for loops would say it should be the first of those, that at any point, this hypothetical #x should refer to the last for index for variable x that was encounted in the flow of execution -- just like the way that the sin(x.^2 - y) is going to use the last x value from the for x = 1 : .5 : 5 .

I would kind of like such an operator to be associated with the innermost enclosing loop so that in this example the f(#x) would be counting relative to the for x = 0:.01:2*pi loop, but I do admit that it would be confusing to have the #x refer to that loop at the same time that the x itself would be what was left-over for the for x = 1: 0.5 : 5 loop. Also, in a context such as

f = zeros(1,5000);

for x = 0:.01:2*pi

if x.^2 - sin(x) > 1; break; end

f(#x) = acos(x);

end

f(#x+1:end) = [];

then it would make sense for the counter to survive the loop itself, which argues for the status quo of "last value assigned" rather than "according to scope". I think the factors are in tension here.

Now, if we are going to have automatic counters with for loops it might make sense to have automatic counters associated with while loops as well:

x = 0;

while x <= 2*pi & x.^2 - sin(x) < 1

f(#???) = acos(x);

x = x + 0.01;

end

But while loops have no associated variable. So I might suggest

x = 0;

while x <= 2*pi & x.^2 - sin(x) < 1

f(#) = acos(x);

x = x + 0.01;

end

where # by itself is the counter for the innermost enclosing for or while loop. Which would then permit

for x = 0:.01:2*pi

f(#) = sin(x.^2 - phase(#));

end

which is not ambiguous. Now about about with nested loops?

for x = 0:.01:2*pi

y = 0;

for x = 1 : .5 : 5

y = y + z.^(x-1)./gamma(x+1);

end

f(#) = sin(x.^2 - y);

end

The innermost enclosing for or while loop would be the outer for x loop... the one the user probably intended in such a context.

With the discussion above about what #x means after the end of a for x loop, this proposed behavior of # by itself would lead to the possibility that at that point, assigning to f(#) would be assigning according to the loop counter for the outer for x, but that assigning f(#x) would be assigning according to the loop counter for the inner for x . That is not ideal for readability, and is likely to lead to confusion.

It seems to me that in some cases, people would want a #x at that point to refer to the outer loop, but people would also sometimes want a #x to refer to the inner for x . It would also not surprise me at all if people wanted both ways at the same time. Of course, if they wanted clarity and readability, they probably should not have used nested for loops with the same variable name !!!

Image Analyst
on 23 Sep 2021 at 21:43

I'd like a way to enter 2-D matrices interactively easier. The current way with inputdlg() or input() is not WYSIWIG and very clunky and non-intuitive (do I put bracket, parentheses, commas, semicolons - no clue!) We need something like

% Pop up a modal dialog box with a 4 by 5 grid (worksheet) where users can enter values:

m = inputmatrix('Enter your values', 4, 5);

Jim Svensson
on 15 Sep 2021

Most important

- Start indexing from 0
- Redo package system
- Improve the class system
- Improve language a bit (like value += delta)

Gregory Vernon
on 3 Oct 2021 at 21:13

Just cracked open my nonlinear continuum textbook and am greeted with a glut of things like:

that are based on 1-based indexing. Point being that switching to zero-based indexing will just cause pain for people in technical fields that are one-based. To me, seeing means an initial stress state, while means the maximum principle stress. Two entirely different concepts.

Going along with this exercise, instead of changing existing functionality, I'd rather see an extension to functionality -- maybe allowing specifying whether an array/vector is a one-based or zero-based array:

x = zeros(10,1); % A standard 1-based indices array

y = zeros(10,1,"startIndex", 0); % A 0-based indices array

So for something like getting an array of Legendre basis polynomials (which are 0-based indexing) instead of:

P = legendreBasisArray(3, sym("x"))

disp(P(1)) % Display the constant Legendre polynomial, which is usually referred to as P_0(x)

function P = legendreBasisArray(p,variate)

P = sym(zeros(p+1,1));

for n = 0 : p

P(n+1) = 1/((2^n)*factorial(n))*diff((variate^2-1)^n,variate,n);

end

P = simplify(P);

end

You'd instead do something like:

P = legendreBasisArray(3, sym("x"))

disp(P(0))

function P = legendreBasisArray(p,variate)

P = sym(zeros(p+1,1), "startIndex", 0);

for n = 0 : p

P(n) = 1/((2^n)*factorial(n))*diff((variate^2-1)^n,variate,n);

end

P = simplify(P);

end

The challenge here, though, would be that now I'd have to remember that P is zero-based and after a few years and 100k more lines of code, whomever takes over will probably be very confused. Personally, I don't think it's worth the effort and would rather just figure out once whether I need to use n+1 or n in my loops.

Andrew Janke
on 18 Sep 2021 at 23:19

Walter Roberson
on 19 Sep 2021 at 2:28

... Or there could be some kind of reshape-to-row operator similar to the (:) reshape-to-column operator... along with a slightly different interpretation of (:)

Currently reshape-to-column is considered an indexing expression, so you cannot use

f(x)(:)

but you can use

f(x).'

The fact that (:) is considered an indexing expression has consequences for complex arrays whose imaginary part is 0: it is required to drop the zero imaginary part, whereas reshape() does not drop it.

Aik-Siong Koh
on 23 Sep 2021 at 20:58

I would suggest MATLAB learn how to implement Pure Object Oriented Programming from Smalltalk.

Pure OOP embodies the following fundamentals:

- Everything is an object all the time.
- Every operation is through message passing.

Pure OOP enables the following capabilities:

- Environment is running and alive all the time.
- Run everywhere, Inspect everywhere, Debug everywhere, Edit everywhere.
- Entire environment object is saved to disk for fast reload.

Tobias Held
on 30 Sep 2021 at 7:26

- Darktheme
- Standart font with distinguishable lI1, 0O etc. (eg. FiraCode, Input)

gwoo
on 30 Sep 2021 at 20:28

I use Matlab Schemer for my dark theme and personally enjoy Consolas for the font.

gwoo
on 30 Sep 2021 at 19:05

I would also like:

- auto-complete options on inputs to custom functions
- specified type of arguments such that if an argument is supposed to be a filename or path, then it would allow you to autocomplete a path the way imread() and dir() do, but for custom functions
- keyword arguments (is that already a thing?) like in python, instead of all arguments being "equal" and having to parse out
- functional programming features such as in-line loops, if statements, direct indexing into function outputs (without an intermediate variable explicitly created).