Haskell in ES6: Part 1
casualjavascript.com
casualjavascript.com
Several of the functions listed here are quite different from their Haskell counterparts in ways that make them subtly less useful — and in ways that make them not have the same types as the Haskell originals, which is interesting considering we're barely given more than the type of each of the functions. flip and zipWith are the ones that were quite obvious to me, others might have the same problems (with flip being outright wrong due to it inverting all the arguments instead of flipping the first two, and zipWith working fine with two lists, but generalising to multiple lists in a way that can't be a generalisation of the two-list zipWith). Composition also works in reverse to what one would expect in Haskell, for no reason I can fathom.
My guess is that it's to show that (1) Haskell is pretty awesome, and (2) EC6 is pretty awesome too. In reverse order maybe?
Functional programming isn't an end unto itself, it's one of several strategies for breaking programs down into manageable chunks. Without showing how this type of tiny function is used in journeyman Haskell code, nor showing how the translation to ES6 would apply to real world JavaScript, it just feels like functional wankery.
And there is something that a dynamic (and/or gradually typed) languages will never be able to do: function overload based on the function's return type, e.g. like Haskell's `return` :)
edit: oh wait, after monad of no return its `pure` :D
interface SignalFn<T> {
(time: number): T;
}
interface SignalTransformer<A, B>{
(a: SignalFn<A>): SignalFn<B>
}
// the >>> function
function pipe<A,B,C> (a: SignalTransformer<A,B>, b: SignalTransformer<B,C>): SignalTransformer<A,C> {
return (x: SignalFn<A>) => b(a(x))
}
// the arr lift
function lift<A, B>(fn: Function1<A,B>): SignalTransformer<A,B> {
return (input: SignalFn<A>) => <SignalFn<B>>((time: Time) => fn(input(time)));
}- `f.apply(null, args)` calls can be replaced with `f(...args)`
- Your head function returns the first argument while your last function returns an array containing the last argument. I'd rewrite those functions as:
const head = (x) => x;
const last = (...xs) => xs[xs.length-1];
const tail = (x, ...xs) => xs;
const init = (...xs) => xs.slice(0, -1);
(Unfortunately it seems the rest operator can only appear as the last argument.)- Why put everything on the prototype? It doesn't seem necessary since you are not using the `this` keyword. I would just stick the `export` keyword in front of functions to be exported.
Looking forward to part 2.
Those functions operate on lists. I think it should read as follows.
const head = (xs) => xs[0];
const last = (xs) => xs[xs.length-1];
const tail = (xs) => xs.slice(1);
const init = (xs) => xs.slice(0, -1);
Am I missing something?e.g. instead of
1/4 === comp(1)
do
comp(1)
// returns 1/4
This might be my personal preference but it's the way it's written in the Mozilla javascript reference pages.
Other than that it looks great!
function thunk(f) { return { force: f } }
This, for instance, gives you a lazy stream function unfold(step, state) {
return thunk(function () {
let m = step(state);
if (!m.ok) {
return {};
} else {
return { head: m.value,
tail: unfold(step, m.state) };
}
});
} function thunk(f) {
var evaluated = false;
var value = undefined;
var excepting = undefined;
return { force: function () {
if (!evaluated) {
evaluated = true;
try { value = f(); } catch (e) { excepting = e; }
}
if (excepting) throw excepting;
return value;
}}
}"Functional programming isn't an end unto itself, it's one of several strategies for breaking programs down into manageable chunks. I would have found the article more interesting if it showed how this type of tiny function is used in journeyman Haskell code, or how the translation to ES6 would apply to real world JavaScript."
"Wankery" is a great word! It's fun to say, fun to read. It is a criticism, but with some humour to it. Isn't that what we should be encouraging?
Its only crime is vulgarity, but there is no HN guideline against that.