Chapter 7

Style and semantics

7.1 Rest and spread parameters

highlight = (name) ->  
  color find(name), 'yellow'





highlight = (names) ->
  for name in names
    color find(name), 'yellow'





highlight = (names...) ->
  for name in names
    color find(name), 'yellow'





highlight()                                   
highlight 'taipans'                               
highlight 'taipans', 'wolverines', 'sabertooths'  





highlight = (first, rest...) ->
  color find first 'gold'
  for name in rest
    color find(name), 'blue'
highlight 'taipans', 'sabertooths', 'wolverines'  





teams = ['wolverines', 'sabertooths', 'mongooses']





highlight teams[0], teams[1], teams[2]





highlight teams...  





highlight teams      
highlight teams...     





insert = (teams...) ->    
teams
initialize = (teams...) ->
insert teams
initialize 'wolverines', 'sabertooths', 'mongooses' # [ [ 'wolverines','sabertooths', 'mongooses' ] ]





initialize = (teams...) ->  
insert teams...
initialize 'wolverines', 'sabertooths', 'mongooses' # [ 'wolverines','sabertooths', 'mongooses' ]





Listing 7.1 Competition

find = (name) ->                      
document.querySelector ".#{name}"
color = (element, color) ->
element.style.background = color
insert = (teams...) -> root = document.querySelector '.teams' for team in teams element = document.createElement 'li' element.innerHTML = team element.className = team
root.appendChild element
highlight = (first, rest...) -> color find(first), 'gold' for name in rest
color find(name), 'blue'
initialize = (ranked) -> insert ranked... first = ranked.slice(0, 1) rest = ranked.slice 1
highlight first, rest...
window.onload = -> initialize [ 'wolverines' 'wildcats' 'mongooses' ]





7.2 Destructuring

makeToggler = (active, inactive) ->
  ->
    temporary = active       
    active = inactive        
    inactive = temporary     
[active, inactive]
toggler = makeToggler 'komodos', 'raptors'
toggler()
# ['raptors', 'komodos']
toggler() # ['komodos', 'raptors']





active = 'komodos'
inactive = 'raptors'
[active, inactive] = [inactive, active]
active
# raptors
inactive # komodos





toggler = (a, b) ->
-> [a,b] = [b,a]
toggle = toggler 'on', 'off'
toggle()
# ['off', 'on']
toggle() # ['on', 'off']





relegate = (team) -> "#{team.name} got relegated"
rank = (array..., using) -> array.sort (first, second) ->
first[using] < second[using]
competitors = [ name: 'wildcats' points: 5 , name: 'bobcats' points: 3
]
[first, field..., last] = rank competitors..., 'points'
relegate last # 'bobcats got relegated'





data =
  team2311:
    name: 'Honey Badgers'
    stats: 
      scored: 22 
      conceded: 22
      points: 11
  team4326:
    name: 'Mongooses'
    stats:
      scored: 14
      conceded: 19
      points: 8





for id, team of data
  name = team.name
  points = team.stats.points
  {
    name: name
    points: points
}
# [ { name: 'Honey Badgers', points: 11 }, # { name: 'Mongooses', points: 8 } ]





for id, team of data
{name: team.name, points: team.stats.points}
# [ { name: 'Honey Badgers', points: 11 }, # { name: 'Mongooses', points: 8 } ]





{pad, trim, dirify} = require 'util'





util = require 'util'
pad = util.pad
trim = util.trim
dirify = util.dirify





makeCompetition = ->
  find = ->
    # function body omitted
  color = ->
    # function body omitted
  highlight = ->
    # function body omitted
  initialize = ->
# function body omitted
highlight: highlight,
initialize: initialize
competition = makeCompetition()
# { highlight: [Function], initialize: [Function] }





highlight = ->
initialize = ->
object = highlight: highlight initialize: initialize





makeCompetition = ->
  find = ->
  color = ->
  highlight = ->
initialize = -> 'initialized'
{highlight, initialize}
competition = makeCompetition() competition.initialize() # 'initialized'





makeCompetition = (options) ->
  options.max
  options.sort





makeCompetition = ({max, sort}) ->
  {max, sort}





makeCompetition max: 5, sort: ->        
# {max: 5, sort: [Function]}
makeCompetition sort: (->), max: 5 # {max: 5, sort: [Function]}





competitors = [
  { name: 'wildcats', points: 3 }
  { name: 'tigers', points: 1 }
  { name: 'taipans', points: 5 }  
]





[first, middle..., last] = competitors
first
# { name: 'wildcats', points: 3 }
last # { name: 'taipans', points: 5 }





[first, ..., last] = competitors
first
# { name: 'wildcats', points: 3 }
last # { name: 'taipans', points: 5 }





{"A":[{"name":"Andy", "phone":"5551111"},...],"B":[...],...}





Listing 7.2 Competition with module pattern

makeCompetition = ({max, sort}) ->     
  find = (name) ->
document.querySelector ".#{name}"
color = (element, color) ->
element.style.background = color
insert = (teams...) -> root = document.querySelector '.teams' for team in teams element = document.createElement 'li' element.innerHTML = "#{team.name} (#{team.points})" element.className = team.name
root.appendChild element
highlight = (first, rest...) -> color find(first.name), 'gold' for team in rest
color find(team.name), 'blue'
rank = (unranked) ->
unranked.sort(sort).slice(0, max)
initialize = (unranked) -> ranked = rank unranked insert ranked... first = ranked.slice(0, 1)[0] rest = ranked.slice 1
highlight first, rest...
{ initialize }
sortOnPoints = (a, b) ->
a.points > b.pointsv
window.onload = -> competition = makeCompetition(max: 5, sort: sortOnPoints) competition.initialize [ { name: 'wolverines', points: 22 } { name: 'wildcats', points: 11 } { name: 'mongooses', points: 33 } { name: 'raccoons', points: 12 } { name: 'badgers', points: 19 } { name: 'baboons', points: 16 } ]





7.3 No nulls

roundSquare?
# false





user = 
  name:
    title: 'Mr'
    first: 'Data'
    last: 'Object'
  contact:
    phone:
      home: '555 2234'
      mobile: '555 7766'
    email:
      primary: 'mrdataobject@coffeescriptinaction.com'





user.contact.phone.home
# '555 2234'





user =
  name: 
    first: 'Haveno'
    middle: 'Contact'
    last: 'Details'





user.contact.phone.home
# TypeError: cannot read property 'phone' of undefined





if user.contact and user.contact.phone and user.contact.phone.home
  user.contact.phone.home





try 
  user.contact.phone.home
catch e
  'no contact number'





user?.contact?.phone?.home





render = (user) ->
  """
  <html>
  Home phone for #{user.name.first}: #{user.contact.phone.home}
  """





user =
  name:
    first: 'Donot'
last: 'Callme'
render user # TypeError: cannot read property 'phone' of undefined





render = (user) ->
  """
  <html>
  Home phone for #{user?.name?.first}: #{user?.contact?.phone?.home}
"""
user = null render user: null # <html> # Home phone for undefined: undefined





user = null
contact = user?.contact?.phone?.home || 'Not provided'
contact
# Not provided





user = {}
user?.contact?.phone?.home = '555 5555'





user.contact?
# false





phone = undefined
phone ?= '555 5555'
phone
# '555 5555'
phone = null phone ?= '555 1111' phone
# '555 1111'
phone ?= 'something else' phone # '555 1111'





variableYouNeverDeclared ?= 'something'
# error: the variable "variableYouNeverDeclared" can't be assigned with ?= because it has not been declared before





Listing 7.3 Using null soak in a view

makeCompetition = ({max, sort}) ->
  render = (team) ->                        
    """
    <tr class='#{team?.name||''}'>
    <td>#{team?.name||''}</td>
    <td>#{team?.points||''}</td>
    <td>#{team?.goals?.scored||''}</td>
    <td>#{team?.goals?.conceded||''}</td>
    </tr>
"""
find = (name) ->
document.querySelector ".#{name}"
color = (element, color) ->
element.style.background = color
insert = (teams...) -> root = document.querySelector '.teams' for team in teams
root.innerHTML += render team
highlight = (first, rest...) -> color find(first.name), 'gold' for team in rest
color find(team.name), 'blue'
rank = (unranked) ->
unranked.sort(sort).slice(0, max).reverse()
initialize: (unranked) -> ranked = rank unranked insert ranked... first = ranked.slice(0, 1)[0] rest = ranked.slice 1
highlight first, rest...
sortOnPoints = (a, b) ->
a.points > b.points
window.onload = -> competition = makeCompetition max:5, sort: sortOnPoints competition.initialize [ name: 'wolverines' points: 56 goals: scored: 26 conceded: 8 , name: 'wildcats' points: 53 goals: scored: 32 conceded: 19 , name: 'mongooses' points: 34 goals: scored: 9 conceded: 9 , name: 'raccoons' points: 0 ]





typeof null
# null





7.4 No types?the duck test

class Duck
  walk: ->
quack: (distance) ->
daffy = new Duck





daffy.walk()





donald = new Duck
donald.quack() daffy.quack()





class DuckRace
  constructor: (@ducks) ->
  go: ->
    duck.walk() for duck in @ducks





class Hare
  run: ->
walk: -> run()
hare = new Hare
race = new DuckRace [hare]





daffy = new Duck
typeof daffy 
# 'object'





daffy instanceof Duck
# true





class DuckRace
  constructor (applicants) ->
    @ducks = d for d in applicants when d instanceof Duck
  go: ->
    duck.walk() for duck in @ducks





duck = new Duck
ultraDuckMarathon = new DuckRace [duck]
turnIntoSnake = -> duck.walk = null
duck.slither = ->
turnIntoSnake duck
ultraDuckMarathon.go() # TypeError: Property walk of object #<AsianDuck> is not a function





class Duck
daffy = new Duck
Duck:: = class Snake
daffy instanceof Duck # false





class Duck
daffy = new Duck
daffy.constructor.name
# Duck





duck =
  walk: ->
quack: ->
daffy = Object.create duck





daffy.constructor.name
# 'Object'





class DuckRace
  duck: (contestant) ->
    contestant.quack?.call and contestant.walk?.call
  constructor: (applicants...) ->
@ducks = (applicant for applicant in applicants when @duck applicant)
duck = name: 'Daffy' quack: ->
walk: ->
cow = name: 'Daisy' moo: ->
walk: ->
race = new DuckRace duck, cow race.ducks # [ { name: 'Daffy', quack: [Function], walk: [Function] } ]





Listing 7.4 Competition

makeCompetition = ({max, sort}) ->
POINTS_FOR_WIN = 3 POINTS_FOR_DRAW = 1
GOALS_FOR_FORFEIT = 3
render = (team) -> find = (name) -> color = (element, color) -> insert = (teams...) -> highlight = (first, rest...) ->
rank = (unranked) ->
competitive = (team) ->
team?.players is 5 and team?.compete()?
blankTally = (name) -> name: name points: 0 goals: scored: 0
conceded: 0
roundRobin = (teams) -> results = {} for teamName, team of teams results[teamName] ?= blankTally teamName for opponentName, opponent of teams when opponent isnt team console.log "#{teamName} #{opponentName}" results[opponentName] ?= blankTally opponentName if competitive(team) and competitive(opponent) # omitted else if competitive team # omitted else if competitive opponent
# omitted
results
run = (teams) -> scored = (results for team, results of roundRobin(teams)) ranked = rank scored console.log ranked insert ranked... first = ranked.slice(0, 1)[0] rest = ranked.slice 1
highlight first, rest...
{ run }
sortOnPoints = (a, b) ->
a.points > b.points
class Team constructor: (@name) -> players: 5 compete: ->
Math.floor Math.random()*3
window.onload = ->
competition = makeCompetition(max:5, sort: sortOnPoints)
disqualified = new Team "Canaries"
disqualified.compete = null
bizarros = -> bizarros.players = 5
bizarros.compete = -> 9
competition.run { wolverines : new Team "Wolverines" penguins: { players: 5, compete: -> Math.floor Math.random()*3 } injured: injured sparrows: new Team "Sparrows" bizarros: bizarros }





Listing 7.5 The server

http = require 'http'
fs = require 'fs'
coffee = require 'coffee-script'
render = (res, head, body) -> res.writeHead 200, 'Content-Type': 'text/html' res.end """ <!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>Chapter 7</title> <style type='text/css'> * { font-family: helvetica, arial, sans-serif; } body { font-size: 120%; } .teams td { padding: 5px; } </style> #{head} </head> <body> #{body} </body> </html>
"""
listing = (id) -> markup = 1: """ <ul class='teams'> </ul>""" 2: """ <ul class='teams'> </ul>""" 3: """ <table class='teams'> <thead> <tr> <th>Team</th><th>Points</th><th>Scored</th><th>Conceded</th> <tr> </thead> </table>""" 4: """ <table class='teams'> <thead> <tr> <th>Team</th><th>Points</th><th>Scored</th><th>Conceded</th> <tr> </thead> </table>""" script = 1: "<script src='1.js'></script>" 2: "<script src='2.js'></script>" 3: "<script src='3.js'></script>"
4: "<script src='4.js'></script>"
head: script[id], body: markup[id]
routes = {}
for n in [1..6] do -> listingNumber = n routes["/#{listingNumber}"] = (res) -> render res, listing(listingNumber).head, listing(listingNumber).body routes["/#{listingNumber}.js"] = (res) ->
script res, listingNumber
server = http.createServer (req, res) -> handler = routes[req.url] or (res) -> render res, '', ''' <ul> <li><a href="/1">Listing 7.1</a></li> <li><a href="/2">Listing 7.2</a></li> <li><a href="/3">Listing 7.3</a></li> <li><a href="/4">Listing 7.4</a></li> </ul> '''
handler res
script = (res, listing) -> res.writeHead 200, 'Content-Type': 'application/javascript' fs.readFile "7.#{listing}.coffee", 'utf-8', (e, source) -> if e then res.end "/* #{e} */"
else res.end coffee.compile source
server.listen 8080, '127.0.0.1'





7.5 When to use comprehensions (and when not to)

evens = (num for num in [1..10] when num%2 == 0)    





numbers = [1,2,3,4,5,6,7,8,9,10]
evens = []
for (var i = 0; i !== numbers.length; i++) { if(numbers[i]%2 === 0) { evens.push(numbers[i]); }





evens = numbers.filter(function(item) {
  return item%2 == 0;
});





evens = numbers.filter (item) -> item%2 == 0





paid = [10, 50, 200]
taxes = (price*0.1 for price in paid)
# [1, 5, 20]





taxes = paid.map (item) -> item*0.1
# [1, 5, 20]





friends = [
  { name: 'bob', location: 'CoffeeVille' }, 
  { name: 'tom', location: 'JavaLand' },
  { name: 'sam', location: 'PythonTown' },
  { name: 'jenny', location: 'RubyCity' }
]





friends.filter (friend) -> friend.location is 'CoffeeVille'





friend for friend in friends when friend.location is 'CoffeeVille'





mine = ['Greg Machine', 'Bronwyn Peters', 'Sylvia Rogers']





common = (friend for friend in mine when friend in yours)





friends = [
  { name: 'bob', owes: 10 } 
  { name: 'sam', owes: 15 }
]
total = friends[0].owes + friends[1].owes # 25





owed = 0
for friend in friends
  owed += friend.owes





owing = (initial, friend) -> 
  if initial.owers then initial.owes + friend.owes
owed = friends.reduce owing





people = [ 'bill', 'ted' ]
greetings = {}
for person in people greetings[person] = ->
"My name is #{person}"
greetings.bill()





people = [ 'bill', 'ted' ]
greetings = {}
for person in people do -> name = person greetings[name] = ->
"My name is #{name}"
greetings.bill() # My name is bill





people.forEach (name) -> greetings[name] = "My name is #{name}"





7.6 Fluent interfaces

scruffy.eat().sleep().wake() 





class Turtle
  forward: (distance) ->
    # moves the turtle distance in the direction it is facing
    this                                               
  rotate: (degrees) ->
    # rotates the turtle 90 degrees clockwise
    this                                              
  move: ({direction, distance}) ->
    # moves the turtle in a given direction
    this
  stop: ->
    # stops the turtle





turtle = new Turtle
turtle.forward 10
turtle.rotate()
turtle.forward 10
turtle.rotate()
turtle.forward 10
turtle.rotate()
turtle.forward 10





Turtle::square = (size) ->
  @forward size             
  @rotate()                 
  @forward size             
  @rotate()                 
  @forward size             
  @rotate()                 
  @forward size    





Turtle::square = (size) -> 
  for side in [1..4]
    @forward size
    @rotate 90





turtle = new Turtle
turtle.square 4
turtle.forward 8
turtle.square 4





turtle = new Turtle();
with(turtle) { left(); forward(); }





address = '123 Turtle Beach Road';    
with (turtle) {                       
  rotate(90);                         
  address = '55 Dolphin Place';       
}





turtle = new Turtle
turtle                
.forward 2            
.rotate 90            
.forward 4   





turtle           
  .forward 2     
  .rotate 90     
  .forward 4  





turtle.forward(2..rotate(90..forward(4)));  





turtle = new Turtle
turtle               
.forward(2)          
.rotate(90)          
.forward(4)          





turtle = new Turtle
turtle
  .forward(2)
  .rotate(90)
  .forward(4)





NORTH = 0
turtle = new Turtle
turtle
.move                  
  direction: NORTH     
  distance: 10         
.stop()





makeCompetition
  max: 5
sort: ->
makeCompetition sort: -> max: 5





turtle          
.forward(3)     
.rotate(90)      
.forward(1)      





turtle          
.forward 3     
.rotate 90      
.forward 1      





turtle.forward(2).rotate(90).forward(4);   





turtle          
  .forward 3     
  .rotate 90      
  .forward 1      





wait = (duration, callback) -> 
  setTimeout callback, duration
wait 5, ->
turtle
.forward 10 .forward 3





wait(5, function() {                       
  return turtle.forward(1).rotate(90);     
});     





class Turtle
  rotate: (degrees) ->
    # rotate the turtle      
    @        





class Turtle
  rotate: (degrees) ->
    # rotate the turtle
    this 





turtle = new Turtle
turtle.rotate(90).rotate(90)





canvas = document.getElementById 'example'   
context = canvas.getContext '2d'              
context.fillRect 25, 25, 100, 100             
context.strokeRect 50, 50, 50, 50      





using = (object, fn) -> fn.apply object
using turtle, -> @forward 2 @rotate 90 @forward 4





chain(turtle)      
.forward(2)        
.rotate(90)        
.forward(4)
chain turtle .forward 2 .rotate 90 .forward 4





Listing 7.6 The chain function

chain = (receiver) ->
  wrapper = Object.create receiver         
  for key, value of wrapper        
    if value?.call                
      do ->               
        proxied = value            
        wrapper[key] = (args...) ->  
          proxied.call receiver, args...    
wrapper
wrapper
turtle = forward: (distance) -> console.log "moving forward by #{distance}" rotate: (degrees) ->
console.log "rotating #{degrees} degrees"
chain(turtle) .forward(5) .rotate(90)





7.7 Ambiguity

NORTH = 0
turtle .move direction: NORTH distance: 10 .stop()





makeTurtle = ->
  move: ->
    # move the turtle
    this
  stop: ->





friends = ->
    name: 'Bob'
    address: '12 Bob Street Bobville' 
  ,
    name: 'Ralph'
    address: '11 Ralph Parade Ralphtown'





friends = ->
  [{
    name: 'Bob'
    address: '12 Bob Street Bobville' 
  },
  {  
    name: 'Ralph'
    address: '11 Ralph Parade Ralphtown'
  }]





clarity = (important) ->    
clarity()             





(clarity 10)  
clarity 10   





clarity (10)     
clarity (x) -> x    





x y z 4





x(y(z(2)))





x = 5
do (x) -> x = 3
console.log x
console.log x
# 3 # 5





shadowing = do (x) -> (y) ->
  x = 3
x + y
shadowing 5 # 8





do (notPreviouslyDefined) -> notPreviouslyDefined = 9
# ReferenceError: notPreviouslyDefined is not defined
do (notPreviouslyDefined='') -> notPreviouslyDefined = 'It is now' # 'It is now'





notPreviouslyDefined 
# ReferenceError: notPreviouslyDefined is not defined





7.8 Summary