Learn from Haskell - Functional, Reusable JavaScript
seanhess.github.com
seanhess.github.com
In Haskell, everything is basically a function. Even the number 3 is really just a function that if and when evaluated will return the value 3. I used that concept to to simply some JavaScript UI code a while back. I had SELECT element that I needed to populate dynamically but wanted the last option to invoke an action that allowed the user to add another option
choice 1
choice 2
choice 3
<add new choice>
I ended up populating the dropdown list using a bunch of javascript functions that looked like this function () { return { key: 1, value: 'choice 1' } }
function () { return { key: 2, value: 'choice 2' } }
function () { return { key: 3, value: 'choice 3' } }
function () { /* do stuff that creates a new choice */ }
Not sure I would have thought of that if I had not first played with Haskell and grokked laziness.It's simply not true. In fact, because Haskell is statically typed, it's very easy to tell what's a function and what's not: If its type doesn't have an -> in it, it's not a function.
Also, while it might be true that "people who don't know much about Haskell" often say that everything is a function, it's also the case that people who do know a lot about the language find the notion to be pedagogically useful: On page 13 of The Haskell School of Expression, Paul Hudak offers that pi (which is of type Floating a => a and thus clearly not a function) "can be thought of as a function with no arguments".
ones = 1 : ones
is a non-function value, but is recursively applied to itself. Might help illustrate why this is a useful notion in Haskell."ones" is not "recursively applied to itself". "ones" is not a function. "(:)" is a function.
In actuality, "ones" is both an argument and a result of the (:) function (aka "cons").
This works in Haskell because "ones" is not an atomic value or function; "ones" is a "lazy" structure, where some of the elements's values depend on other element's values, and Haskell uses a "call-by-need" graph reduction algorithm to evaluate arbitrarily complex* computations down to values, not in any sort of sraihtforward order suggested by "argument -> function -> result -> lvalue" in an imperative or strict language.
Laziness is at the core of the runtime system's model for computing a result from a program, and it's not something you can translate line-by-line from a non-lazy program, even a functional language program.
I'm not just being pedantic. Haskell (with a runtime like GHC, required for this conversation to really make sense) really is different from other programming systems. ("Programming system"! Not just "language"!).
The runtime system ("RTS") is not just a C runtime that provides a statement language and shim over OS system calls.
The RTS is not "just" a VM like Java VM.
The RTS is (metaphorically) like perl's "perl -nple" or a SQL RDBMS query planner, where the runtime is itself a program that takes your code as input and does its own extremely sophisticated computation that is not at all visible in your program's code.
[] Arbitrarily* complex, but still "causal" structures that admit at least one valid order of evaluation. Trying to define "ones = ones" or other non-disentanglable cyclic dependencies leads to a runtime failure ("<loop>" exception).
[] In some cases, the graph solver might practically fail to solve a graph that is theoretically solvable. Let's ignore that.
http://darcs.haskell.org/ghc/docs/comm/the-beast/stg.html
http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler...
http://www.reddit.com/r/programming/comments/i4jb1/haskells_...
-------------
Also, Simon M started Google +ing about what he's up to
Haskell doesn't define integers as functions, they are machine integers just like most other languages. You may be thinking of the Peano numbers, which do have a particularly nice representation in Haskell but they certainly aren't used by default.
A thunk can be thought of as a procedure with no arguments (taking some terminology from SICP), and in JavaScript procedures are called "functions", but using JavaScript's terminology in a Haskell context gets confusing.
Additionally, it wouldn't even be correct to say all Haskell values are thunks. You actually have a fair bit of control over this and can have strict, unboxed elements and the like with GHC. A different compiler could implement the non-strict semantic in a different way.
var a = function(x) { if (x == 0) return 'done'; return a(x-1); };
a(100000);
RangeError: Maximum call stack size exceeded
http://code.google.com/p/v8/issues/detail?id=457Guess it's time for me to stop relying on that behavior...
var _slice = Array.prototype.slice;
var curry = function() {
var args = _slice.call(arguments);
return function() {
var args2 = _slice.call(arguments);
return args[0].apply(this, args.slice(1).concat(args2));
};
};
As can the composition func: var compose = function(f, g) {
return function() {
var args =_slice.call(arguments);
return f(g.apply(this, args));
};
}; forever :: Monad m => m a -> m a
I can use it for either asynchronous or synchronous javascript, e.g forever :: IO a -> IO a
forever :: ContT r IO a -> ContT r IO a
The same function can take a synchronous js function or an async js function and run it in an infinite loop. For this reason, I think that if you want to learn from Haskell in writing reusable js then you should look into monads. forever :: Monad m => m a -> m b
Since it never returns a value, the resulting monadic action can be polymorphic in its return type.The best I can figure is first mapping to a nested array where the first element is the object and the second is the computed property but that seems really messy. Thoughts?
If you absolutely insist on map/reduce, you can just use reduce, where your binary function returns whichever object has a longer full name.
(* assuming: val total_len : string -> int *)
List.reduce seq ~f:(fun a b ->
if (total_len a) > (total_len b) then a else b)
It's not necessary in this case, but fold is often a lot more useful than reduce. At least the way I think of it, the type of reduce is 'a list -> ('a -> 'a -> 'a) -> 'a, whereas the type of fold is 'a list -> init:'b -> ('b -> 'a -> 'b) -> 'b. The upside is that you can construct basically any type of thing you'd like, since 'b is a completely different type. The downside is that if 'a is different than 'b, you need some sort of initial value to give it.edit: Yes, this does require you to compute string length multiple times...but keep in mind that getting string length is very cheap in languages with good strings. (That is, basically everything except C's null-terminated strings.) 99% of the time it's not going not going to matter at all. If you do care, you can map to a tuple of (original_struct,total_len) and then do the reduce and then another map to get back to your original structure, or use a fold, as I mentioned, or write a (tail-)recursive function that does it in slightly fewer operations. (Although I don't think JS has tail-call optimizations, so that's probably a bad idea if you're doing it in JS.)
let precomp = List.map lst (fun el -> ((length el), el)) in
let get_max (l1, el1) (l2, el2) = el1 if l1 > l2 else el2 in
List.reduce lst get_max Yes, this does require you to compute string length multiple times...but keep in mind that getting string length is very cheap in languages with good strings. (That is, basically everything except C's null-terminated strings.)
Since this is a discussion about Haskell, too, I feel obliged to say that computing the length of a String type in Haskell is an O(n) operation, because String is really just type String = [Char]
i.e. a linked list of Char values.Typically, if you want performance out of strings in Haskell, you'll use the Text or ByteString types BUT the length operation of Data.Text is still O(n). Only ByteString offers
length :: ByteString -> Int
Which is O(1).reduce/fold is a very general and powerful tool. In general you want to use the most specific and least powerful solution you can get away with. This spares the reader some thinking, and in theory gives the compiler more leeway. Also with less power there's less room for error.
Here a combination of maximum (or maximumBy in Haskell) and map will give you what you are looking for.
An alternative if you don't like that would be having the maximum-finding function receive an additional comparator argument, similarly to qsort.
Yup, though one small thing: it's 'Schwartzian transform'[1] for Randal Schwartz.
_.reduce(arr, function(p1, p2) {
var len = p2.firstName.length + p2.lastName.length;
return (len > p1[1]) ? [p2, len] : p1;
},
[null, -1]); _.chain(people)
.sortBy(function(person) {
return person.firstName.length + person.lastName.length; })
.last()
.value() _(people).max(function(person) {
return person.firstName.length + person.lastName.length;
}); longestName = maximumBy . comparing $ \(f,l) -> length $ f++l people = [Person('foo', 'bar'), Person('John', 'Doe'), Person('Jane', 'Anonymous')]
key = lambda person: len(person.first_name + person.last_name)
max(people, key=key) (argmax (λ(c) (string-length (customer-name c)))
customers)
http://docs.racket-lang.org/reference/pairs.html?q=argmax#(d...)Haskell has argmaxBy: http://hackage.haskell.org/packages/archive/list-extras/0.3....
If your language doesn't have argmax, fold the list with the best value, like this in lisp:
(define (my-argmax fun lst)
(foldl (λ(prev-max elt)
(if (< (fun prev-max) (fun elt))
elt
prev-max))
(car lst)
(cdr lst)))
No extra space usage, no temporary values, no sorting; all in O(N) time. :) longestLastname :: [Person] -> Person
longestLastname names = maximumBy (comparing (length . lastname)) names
In general, you can do map something like extractProperty list = map (\x -> (f x, x)) list
Then work on the first element of the tuple (comparing for sorting etc), and at the end return the original object by extracting the second element of the tuple.Not knocking coffescript here, just saying that "I don't want to write a specific word" is a pretty terrible reason to pick any language over another.