Re:My Perception of CoffeeScript
by oinksoft on Friday June 15, 2012 @11:13AM
(#4116895)
I am not a fan of CoffeeScript for my own reasons (it doesn't offer enough for me to drop drop JS), but there is nothing stopping the author from writing his CS like so:
But really, this is just JS wearing some new clothes. The string interpolation is nice, but apart from that, you really gain nothing over the JS version.
[ Parent
| on HN ]
>But really, this is just JS wearing some new clothes.
And that's not a bad thing.
If you want FP and encapsulation, you can write something like this (functionally equivalent to the OO version, unlike TFA's example)
move = ->
alert "#{@move_verb or "Mov"}ing..."
alert "#{@name or "It"} moved #{@distance or 1} meters"
snake =
name: "Sammy"
distance: 5
move_verb: "Slither"
horse =
name: "Tommy"
distance: 45
move_verb: "Gallop"
move.call snake
move.call horseDude. Really ? You've just invented objects, but you'd rather not call it that. You have two attributes ( name & distance ) and you've made an attribute out of the method ( move_verb ?! heh heh ) as well, very clever. You are then calling a function & asking it to sort it all out. If we are going to call this FP, that's a real stretch. What objects buy you is that name,distance & move_verb are common to both snake & horse, so they should be refactored to some base class & then snake & horse should be instances of that class. But then you won't do OO, so you must encapsulate in this roundabout fashion:) I'll grant it does give you FP + encapsulation.
shiffern's example uses the same coding style you use in Clojure. It doesn't feel like stretch to call it FP. Start with a basic data structure (the object in shiffern's example would translate to a map in Clojure) and then operate on it with simple functions.
(def snake {:name "Sammy"
:distance 5
:move-verb "Slither"})
(def horse {:name "Tommy"
:distance 45
:move-verb "Gallop"})
(defn move[{:keys [name distance move-verb]}]
(println (str (or move-verb "Mov") "ing..."))
(println (or name "It") "moved" (or distance 1) "meters"))
(move snake)
(move horse)or in scala:
val snake = Map("name"->"Sammy", "dist"->"5m","move"->" slithers ")
val horse = Map("name"->"Tommy", "dist"->"45m","move"->" gallops ")
def move(m: Map[String,String]) = println( m("name") + m("move" ) + m("dist")
scala> move (snake)
Sammy slithers 5m
scala> move (horse)
Tommy gallops 45m
If FP = OO via dictionaries, then yeah, ok :))The example given by author is very FP and not OOP, since the move function doesn't modify shared state and the only side-effect is output.
We use CoffeeScript for node.js and browser client code. I think classes more useful for control patterns, like EventEmitter in node.js, and much less for wrapping of data, as done in traditional Java/C++ -style OOP or ORM models.
I'll chip in Erlang (real production-grade code with type-specs, not a short REPL example):
-module(animal).
-record(animal{
name :: string(),
distance :: integer(),
move_verb :: string()
}).
-spec move(animal()) -> ok.
move(#animal{name=Name, distance=Distance, move_verb=MoveVerb}) ->
io:format("~sing... ~s moved ~b meters~n",
[default(MoveVerb, "Mov"), default(Name, "It"), default(Distance,1)]).
-spec default(X::any(), Val::any()) -> any().
default(undefined,Val) -> Val.
default(X, _) -> X.
-spec main() -> ok.
main() ->
Snake = #animal{name = "Sammy",
distance = 5,
move_verb = "Slither"},
Horse = #animal{name = "Tommy",
distance = 45,
move_verb = "Gallop"},
[move(A) || A <- [Snake, Horse]],
ok.