Functional programming paradigm aims at correctness and beauty. It produces the most elegant piece of code with a streamlined look and feel.
Personally, I can effortlessly write functional code and would like to write it rather than procedural or object-oriented code. It's more straightforward for me: I'd like to use map
rather than for-loop.
But because most of my colleagues just don't get functional programming, they claim my code is unreadable, so unmaintainable. Beginners are scared away from my code.
A sample of my code, making use of lodash
a lot:
const get_piece = (board, x, y) =>
_.chain(board.pieces)
.filter(piece => piece.current_x == x && piece.current_y == y)
.first()
.value();
const process_move = (move, board) => {
// FIXME the move should be specified by absolute coeff of the piece to move
const to_move_x = board.hole_x + move[0];
const to_move_y = board.hole_y + move[1];
const piece = get_piece(board, to_move_x, to_move_y);
piece.current_x = board.hole_x;
piece.current_y = board.hole_y;
board.hole_x = to_move_x;
board.hole_y = to_move_y;
};
const adjacent_offsets = Object.freeze([
[0, 1], // up
[1, 0], // left
[0, -1], // down
[-1, 0] // right
]);
// Check if co-effient is in bound of the board
const in_bound = (board, offset) =>
(board.hole_x + offset[0] >= 0) &&
(board.hole_y + offset[1] >= 0) &&
(board.hole_x + offset[0] < board.split_x) &&
(board.hole_y + offset[1] < board.split_y);
// Return a random element from an array, uniform distribution
const choice = array => array[Math.floor(Math.random() * array.length)];
const random_move = board => {
const possible_moves = _.chain(adjacent_offsets)
.filter(offset => in_bound(board, offset))
.value();
const move = choice(possible_moves);
process_move(move, board);
};
const shuffle = (board, times) => {
_.range(times).map(() => random_move(board));
};
const is_piece_in_position = piece =>
piece.current_x == piece.result_x && piece.current_y == piece.result_y;
// return true if all pieces in right position
const check_result = async board =>
_.chain(board.pieces)
.map(is_piece_in_position)
.every()
.value();
Do you think functional code readable ?
Top comments (5)
A few minor things could help:
I don't think using lambdas over regular
function
definitions gets you much here. If your lambda if split over four lines, the extrareturn
is not a big deal. Usingfunction
alone will make the code look much more familiar.Second, assuming you're targeting modern JavaScript (or have polyfills available), you don't need to use lodash.
filter
andmap
are already a standard members of arrays.Also, you really should document your functions' purposes. I can't really figure out what your
shuffle
, though it does appear to be mutatingboard
which is not very functional. Where you are mutating objects, you should probably stick to an imperative style.Also, you should probably not use
[0]
and[1]
; use.x
and.y
since that's what you mean!Agree. I wrote this code yesterday, haven't merged yet. At that time I just want to prototype as fast as possible, so less keystroke is a thing :)
Yes... I just get used to it than native Web API. Also AFAIK, Web API doesn't have
range()
and few other things.My bad!
How can I write it without this shit
var i=0; i<50; i++
?Many thanks anyway :)
I think
Is probably the clearest way to write it. While
for
loops aren't very aesthetic, they are simple and very clear, and easily recognizable to JavaScript programmers.If you really wanted to get rid of it, you could use something like
but since this isn't an established idiom, it takes more mental effort to read even if it is prettier.
I notice that
random_move
is actually doing too much. It shouldn't choose a move and execute it; it should just choose a move. Theshuffle
function body is the place where you should callprocess_move
.:)
Oh yes. Thank you!
I am also interested in programming in a functional style, I care quite a lot about it so I want to contribute to the conversation.
Firstly, I want to say that I like your code, but also that the focus should be on how it can be changed to make it more readable for your colleagues.
A point that can be difficult for us to accept is that even though there are some general principles that can lead to programs being more or less readable (coupling, mutation, long functions, etc) readability can still be subjective: when we read a new piece of code, in order to have an easy time understanding the code, we must know what the little pieces do ("while" execute the block repeatedly, "range" returns an array, "if" does conditional execution, etc).
When we develop software with others, we should check first if your colleagues are familiar with or eager to embrace the concepts we want to introduce.
Then, when we program we keep in mind that the target audience is not the compiler (or interpreter), but other people and ask ourselves what we can do to make it easier for them to read the code. Thinking about our audience is not something we do only when programming but, actually, any time we communicate we choose which details to add and which details to leave out to correctly target the audience we have in mind.
To give you my personal experience reading this as an example, I can tell you that I know what the
map()
function does because I use it often and I even usedObject.freeze()
, so I know what it does, but I have not seenchain()
before and so I don't know what it does. That means that there are parts of the code that I can read and parts of the code that are not easy for me to read and understand. Try to keep in mind that your readers will have some sort of experience like this when reading the code and try to imagine what they will feel and think when you are writing it.I won't claim to know the solution here. I don't even have much experience working with other programmers myself, but here's a couple of ideas that I think may help.
Vectors are Cool
There's one last thing I want to add. I don't think it would help with readability in your case, but I want to mention it just because I love vectors. In your code, you have some parts that operate on components of vectors like this.
Each of the two lines here does the same operation, but for each component. This could be written more briefly with vectors.
An example of a vector class implementation can be found here on this gist.