The website is the old version of James Porter's Projects and Writings. You can find the new version here.
I wrote this quite a few years ago. I think it was before I officially and briefly became a 'professional Elixir developer' (I was paid to write a few lines of code in Elixir that were deployed).
I have been following with interest and learning about Elixir (and Phoenix). They offer proper functional programming but with nice syntax, superficially similar to Ruby, and a structure somewhat like Rails. They offer proper concurrency, compile time macros. And I have really enjoyed working through the Exercism challenges on Elixir. But I've also been surprised how nice Javascript has been for this.
Taking one of the more realistic challenges (a data transformation one around seed planting), Javascript (ES6-ish) seems to result in a shorter and easier to follow solution which is nearly entirely functional.
First let's look at Elixir:
defmodule Garden do
@doc """
Accepts a string representing the arrangement of cups on a windowsill and a
list with names of students in the class. The student names list does not
have to be in alphabetical order.
It decodes that string into the various gardens for each student and returns
that information in a map.
"""
@default_names [:alice, :bob, :charlie, :david, :eve, :fred, :ginny, :harriet, :ileana, :joseph, :kincaid, :larry]
@plants %{"R" => :radishes, "C" => :clover, "G" => :grass, "V" => :violets}
@spec info(String.t(), list) :: map
def info(info_string, names \\ @default_names) do
student_names = Enum.sort(names)
[first_row, second_row] = String.split(info_string, "\n", trim: true)
|> Enum.map(&(String.split(&1, "", trim: true)))
seeds = Enum.zip(Enum.chunk(first_row, 2), Enum.chunk(second_row, 2))
|> Enum.zip(student_names)
|> Enum.reduce(%{}, fn {{[a,b], [c,d]}, name }, map ->
Map.put(map, name, List.to_tuple(Enum.map([a,b,c,d], &(@plants[&1]))))
end)
Enum.reduce(student_names, seeds, fn name, map ->
Map.put_new(map, name, {})
end)
end
end
and now Javascript
const default_names = ["alice", "bob", "charlie", "david", "eve", "fred",
"ginny", "harriet", "ileana", "joseph", "kincaid", "larry"];
const plants = {R: "radishes", C: "clover", G: "grass", V: "violets"};
const chunk = (arr, n) => {
let newArr = [];
for(let i=0; i <arr.length; i+= n){
newArr.push([arr.slice(i, i + n)]);
}
return newArr;
};
const Garden = function(seeds, children = default_names){
const sortedChildren = children.map(c => c.toLowerCase()).sort();
const seedLists = seeds.split("\n");
const firstRow = chunk(seedLists[0].split(""), 2);
const secondRow = chunk(seedLists[1].split(""), 2);
sortedChildren.forEach((child, idx) => {
if(idx < firstRow.length)
this[child] = (firstRow[idx].concat(secondRow[idx])).map(el => plants[el]);
});
};
module.exports = Garden;
And while I'm not claiming either are perfect they probably represent a similar quality (within each language). A few observations:
fn n -> n + 1 end
form which I typically prefer).But maybe all of this is irrelevant in 'real' applications. And Phoenix is way better than any Javascript backend framework I've used. And concurrency. To be continued.