0

I'm wondering how to store a single variable and to have specific functions on that variable. I'm wondering if there are alternatives to creating a class.

Specifically, I am creating an application where I store a value for time that represents the number of seconds elapsed from a base time (e.g., Jan 1, 2000, 00:00:00). I want to perform operations on this value such as convert it from seconds from to a specific time or date, or from a date to the specific seconds.

I have done this using a class, however it seems wasteful. Specifically, every time I access the stored value for seconds elapsed it would look similar to (time-time time) where time-time is the accessor for the time instance time.

Is there a better way to design this, perhaps without classes?

audrow
  • 143
  • 1
  • 9
  • 2
    Why not just store it as an integer? – jkiiski Aug 22 '16 at 18:36
  • 2
    To be clear, you don't want to use the [universal time functions](http://www.lispworks.com/documentation/HyperSpec/Body/f_get_un.htm) that are part of the standard? – Joshua Taylor Aug 22 '16 at 21:08
  • In order of frequency what do you do with it? Is it a specific time or do you get an elapsed time since your offset? – Sylwester Aug 22 '16 at 21:37
  • @JoshuaTaylor, I didn't know about the universal time functions. It is very similar to something that I've written. Thanks for pointing it out. – audrow Aug 23 '16 at 21:36

3 Answers3

5

Accessor names

You can name accessors in CLOS any way you like. The accessor function could be called seconds:

CL-USER 23 > (defclass mytime ()
               ((seconds :accessor seconds :initarg :seconds)))
#<STANDARD-CLASS MYTIME 422015CDD3>

CL-USER 24 > (let ((mt (make-instance 'mytime :seconds 100)))
               (values (seconds mt)
                       (truncate (seconds mt) 60)))
100
1

Shorter access to slots via accessor functions

Common Lisp also has a form WITH-ACCESSORS. It allows us to use a symbol instead of an accessor form in the code - for a certain CLOS object. In the following example we can use secs and it looks like a variable in the code, but Common Lisp will make sure that it actually calls the accessor seconds. We can write secs instead of (seconds mt). Thus it helps to make the enclosed code shorter. Compare the next example with the code above.

CL-USER 25 > (let ((mt (make-instance 'mytime :seconds 200)))
               (with-accessors ((secs seconds))
                   mt
                 (values secs (truncate secs 60))))
200
3

Shorter access to slots via SLOT-VALUE

CLOS also has WITH-SLOTS for slot access via slot names, here the slot named seconds of the mytime instance can be accessed via the name secs:

CL-USER 26 > (let ((mt (make-instance 'mytime :seconds 200)))
               (with-slots ((secs seconds))
                   mt
                 (values secs (truncate secs 60))))
200
3
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
3

If you have a class which is just wrapping a single object, and that object has a known type, then you can always just write methods for the class of that object instead:

(defmethod time-as-unix-time ((tm integer))
  (- tm (load-time-value (encode-universal-time 0 0 0 1 1 1970 0))))

For instance.

Of course object-oriented zealots will throw you into a pit full of spikes if they catch you doing this sort of thing: it no doubt violates encapsulation or some other rule of the cult.

  • 1
    Can I make the method more restrictive than by type without using classes? – audrow Aug 23 '16 at 21:33
  • @audrow Well, you *are* using a class, just a [built in one](http://www.lispworks.com/documentation/HyperSpec/Body/t_built_.htm#built-in-class). You can define any kind of method for a `built-in-class` that you can for any other class: what you can't do is subclass it, and it doesn't have any slots or other attributes other than what the standard defines for it. –  Aug 23 '16 at 22:09
  • I see. Thank you. – audrow Aug 23 '16 at 22:40
2

You could try a closure over a lexical scope like this:

(let ((time (get-universal-time)))
  (defun set-time(tm)
    (setf time tm))

  (defun get-time()
    time)

  (defun get-time-in-some-other-format()
    ;; calculate return value based on 'time'
    )
)
Claus Brod
  • 131
  • 4
  • You probably wanted that to be `let ((time (get-universal-time))) ...)`? This is an alternative, but it also makes it so that you only have *one* instance of the time structure. That might be inconvenient... – Joshua Taylor Aug 22 '16 at 21:12
  • Sorry for the typo, now fixed. – Claus Brod Aug 22 '16 at 21:15
  • This is beautiful. I didn't understand how to use closure over a lexical scope before this example. Thank you. – audrow Aug 23 '16 at 21:35