Kevin Meredith
Kevin Meredith

Reputation: 41909

Hiding Internal State of CoffeeScript Object

Looking at the following from CoffeeScript Ristretto:

QueueMaker = ->
  do (queue = undefined) ->
    array: []
    head: 0
    tail: -1
    pushTail: (value) ->
      queue.array[tail += 1] = value
    pullHead: ->
      unless queue.isEmpty()
        do (value = queue.array[queue.head]) ->
          queue.array[queue.head] = undefined
          queue.head += 1
          value
      isEmpty: ->
        queue.tail < queue.head

It's possible to mutate queue.head - http://jsfiddle.net/VQLNG/.

queue = QueueMaker()
queue.head = 666
console.log queue

How can I write the above function so that head isn't public?

Upvotes: 0

Views: 100

Answers (3)

hpaulj
hpaulj

Reputation: 231385

QueueMaker = ->
  do (array = [], head = 0, tail = -1) ->
    pushTail: (value) ->
      array[tail += 1] = value
    pullHead: ->
      if tail >= head
        do (value = array[head]) ->
          array[head] = undefined
          head += 1
          value
    isEmpty: ->
      tail < head

With this version, array, head and tail are hidden. They are initialed when the queue is created, and remain in existence only as long as it exists.

coffee> queue = QueueMaker()
{ pushTail: [Function],
  pullHead: [Function],
  isEmpty: [Function] }

coffee> queue.head
undefined

But to be honest, this is the first version of QueueMaker on the Ristretto link. What you gave us was the “de-encapsulate” version, rewritten purposely to make these variables visible (in order to extend its behavior).

For reference, the "de-encapuslated" version is:

QueueMaker = ->
  do (queue = undefined) ->
    queue = 
      array: []
      head: 0
      tail: -1
      pushTail: ...
      pullHead: ...

Your question omitted the queue= line. Now the purpose of the do()-> should be clearer.

Upvotes: 0

mu is too short
mu is too short

Reputation: 434665

JavaScript doesn't have private properties so CoffeeScript doesn't have them either.

However, you can simulate private properties in many cases by using function scopes to hide things and closures to access the hidden things.

A simple stack implementation should demonstrate the technique:

Stack = ->
    stack = [ ]
    push: (e) -> stack.push(e)
    pop:      -> stack.pop()
    toArray:  -> stack.slice()

stack is a local variable in the Stack function so it cannot be accessed or seen from outside Stack. The push and pop functions simply proxy to the stack array and the toArray function is the only way to see what stack looks like. Only those three functions have access to stack so it is effectively private and each time you call Stack, you get a new local stack.

Demo: http://jsfiddle.net/ambiguous/C8V5R/

Adapting your queue to use this technique to hide array, head and tail is left as an exercise.

Upvotes: 1

Douglas F Shearer
Douglas F Shearer

Reputation: 26488

The return value of QueueMaker is a JavaScript object, with head being one of it's fields. Object fields are mutable, with no option for protected status.

Even with QueueMaker rewritten as a CoffeeScript class, and head being an instance variable, it would still be mutable from outside the object scope.

CoffeeScript can only support the language level features of JavaScript, which does not support private/protected keywords. Sadly.

Upvotes: 0

Related Questions