1

Is there a way in lisp-family (EDIT: lisp-1) languages to differentiate symbol evaluation with regard to its position as as function or as an argument (i.e. override eval of this symbol in regard to when it is evaluated)?
As an example (I don't need this functionality, this is an example), I wanted to implement some sort of infix operation on a set of objects, which can be called by an object itself

(my-obj some-operator arg1 ...)  

which will actually apply function some-operator to my-obj and arguments.
But when this object is used anywhere else in code as an argument, like:

(some-function my-obj &args...) 

it will evaluate to a value of my-obj.
Thank you.

artemonster
  • 744
  • 4
  • 27
  • Thanks, forgot to make a remark, that I was talking about lisp-1 languages. – artemonster Dec 15 '15 at 10:48
  • 1
    It seems to me that what your are searching is a way of breaking the [Referential transparency rule](https://en.wikipedia.org/wiki/Referential_transparency) that is a very important principle in programming (and more so in functional programming), and that could be simplified in this way: “each thing should have the same meaning in any context”. This helps greatly in program readability, provability, etc. Just my 2¢. – Renzo Dec 15 '15 at 17:56

4 Answers4

5

In Racket it is possible to do a couple things in this spirit:

  1. You can define a struct and give it a prop:procedure. When an instance of the struct is supplied in an application, the procedure will be called.

  2. You can override the default #%app with your own function, to redefine application generally, and including things that aren't structs. For example you can do things like emulate Clojure's (key map) syntax, so that ('symbol dict) is actually (hash-ref dict 'symbol).

Greg Hendershott
  • 16,100
  • 6
  • 36
  • 53
  • Overriding `#%app` and perhaps even `#%datum` (depending on what OP needs) is definitely the way to go. – soegaard Dec 15 '15 at 18:17
  • The clojure's interesting application of a hashmap to key, or vice-versa is exactly what motivated me to ask this question. Do you happen to know how it is realized in clojure? – artemonster Dec 16 '15 at 09:44
  • In Clojure on the JVM, I _think_ it's handled in Java not in Clojure "per se". Clojure values correspond to various Java classes that implement `IFn` and its `invoke` method, and that's where the magic happens. At least that's my recollection from [writing about looking at this a year ago](http://www.greghendershott.com/2014/10/applicable-symbols.html). – Greg Hendershott Dec 17 '15 at 04:57
1

Being a lisp-1 basically means that you do not evaluate the first slot of a combination any differently than any other slots. To get such behavior for code you write anyway, you need to transform it to code that does what you want under the rules of a lisp-1. Thus you will need to implement a macro that performs this transformation.

For example if you want infix operators you need to write some macro infix and then perhaps you could write:

(infix (+ - * /) (1 + 2 * 5 - 3) / 4)

and have it evaluate to 2.

hkBst
  • 2,818
  • 10
  • 29
1

I have been playing around with the idea of a default procedure in a OO CLOS-like Scheme. eg. that writing

(obj 5 10)

Would validate obj and apply it with arguments if obj is a procedure or method, but if it isn't it would be the same as the default dispatcher eg.

(default-dispatcher obj 5 10)

In such Scheme one could make vector accessors:

(define-method default-dispatcher 
  "Default dispatcher for vectors"
  ([obj %vector] [index %number])       -> (vector-ref obj index)
  ([obj %vector] [index %number] value) -> (vector-set! obj index value)
  (args ...)                            -> (error "No such method"))

; example usage
(define vec (vector 4 5 6 7))
[vec 1]     ; => 5
[vec 1 10] 
[vec 1]     ; => 10

In Racket this is possible by changing the languages #%app syntax.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
1

In the TXR Lisp dialect, the problem is approached from the other end. Starting with Lisp-2 dialect as a basis, can we robe ourselves with some of the expressivity advantages of a Lisp-1 dialect, like eliminating the (function ...), #' and funcall noise from programs that make extensive use of higher order functions?

The design is centered around a special operator called dwim, which stands for either "Do What I Mean" or "Dispatch, in a Way that is Intelligent and Meaningful".

Invocations of the dwim operator are sugared over using square brackets, which are called "DWIM Brackets"

The dwim operator isn't just a macro over Lisp-2; it actually changes the name lookup rules. When we have

(dwim a b c (d e f) g)

Or equivalently:

[a b c (d e f) g]

all of the argument forms which are symbolic (a, b, c and g) are resolved using a special rule which conflates together the function and variable namespaces. This is built into the heart of the language. The operator has direct access to the environment to make this possible.

The special treatment does not recurse into (d e f), which is an ordinary Lisp-2 form. You have to put the DWIM Brackets on that if you want the semantics.

Also, the dwim operator is properly handled by macro expansion. For instance, given:

(symacrolet ((localfun whatever))
  (flet ((localfun () ...)))
    [a b c localfun]    ;; refers to the flet as a function object!
    (a b c localfun)))  ;; refers to the symbol macro!

The macro expander knows about dwim and its semantics, and so it considers the possibility that localfun refers to the function and variable namespace. The closest lexical binding in either namespace is the flet and so the symbol macro expansion is suppressed (shadowed).

The dwim semantics is implicitly used in the partial evaluating op macro and its "cousins" derived from it.

Range Extraction task from Rosetta Code:

(defun range-extract (numbers)
  `@{(mapcar [iff [callf > length (ret 2)]
                  (ret `@[@1 0]-@[@1 -1]`)
                  (ret `@{@1 ","}`)]
             (mapcar (op mapcar car)
                     (split [window-map 1 :reflect
                                        (op list @2 (- @2 @1))
                                        (sort (uniq numbers))]
                            (op where [chain second (op < 1)])))) ","}`)

Y Combinator:

;; The Y combinator:
(defun y (f)
  [(op @1 @1)
   (op f (op [@@1 @@1]))])

;; The Y-combinator-based factorial:
(defun fac (f)
  (do if (zerop @1)
         1
         (* @1 [f (- @1 1)])))

;; Test:
(format t "~s\n" [[y fac] 4])

Moreover, various useful things are function callable in TXR Lisp. For instance, every sequence (list, vector or character string) is regarded as a function which maps numeric indices to elements. Thus we can do:

(mapcar "abc" #(2 1 0))  -> #(#\c #\b #\a)

The accepted answer describes a Racket mechanism for treating structures as functions. TXR has this in the form of lambda methods. This is demonstrated in the "OOP-Based" solution to the Accumulator Factory task in Rosetta:

(defstruct (accum count) nil
  (count 0))

(defmeth accum lambda (self : (delta 1))
  (inc self.count delta))

We can instantiate a (new (accum 9)) which will produce the values 10, 11, 12, ... when invoked as a function. An optional delta argument can be supplied for an increment other than 1:

(let ((acc (new (accum 0))))
  (list [acc 5] [acc 5] [acc])) -> (5 10 11)
Kaz
  • 55,781
  • 9
  • 100
  • 149