Может быть функцией, а не специальной формой

Наконец-то я начал изучать функциональные языки (emacs lisp), и он делает явное различие между функциями и специальными формами, такими как управление потоком, например if.

Существует ли фундаментальная/теоретическая причина, почему особые формы отличаются от функций? любые языки предоставляют функциональные if?

Спасибо

Ответы

Ответ 1

С нетерпеливой оценкой требуется различие, языки с ленивой оценкой (т.е. Haskell), если et al. могут быть функциями.

Простая оценка: аргументы функции вычисляются перед вызовом функции, и только результаты передаются функции.

Ленивая оценка: аргументы функции оцениваются тогда и только тогда, когда они доступны.

Ответ 2

Если if была нормальной функцией, тогда оба ее аргумента - форма затем и форма else - были бы оценены ранее вызывая функцию if, потому что правило оценка функции: оценить все аргументы для создания значений, затем предоставить эту последовательность значений в качестве аргументов к функции, обозначенной первым символом в списке.

Вместо этого с if то, что вы хотите сделать, - это точно определить одну из форм then и else, а не и то, и другое. Чтобы подавить оценку того или другого, вам нужен макрос или специальная форма.

Ответ 3

В таких языках, как Emacs Lisp и Common Lisp, специальные формы являются встроенными языковыми конструкциями. У них есть разные правила оценки, которые вызовут обычные функции. Для обычных вызовов функций все аргументы оцениваются. Таким образом, вы не можете записать IF как нормальную функцию - условие определяет, какое условие оценивается. Также обычно вы не можете писать свои собственные специальные формы - в Common Lisp нет языковой конструкции для определения специальной формы (хотя отдельные реализации должны каким-то образом реализовать существующие. Это приводит к макросам. С макросами вы можете написать синтаксическое преобразование, которое преобразует одно выражение в другое. Чтобы иметь возможность писать IF как макрос, вам нужно иметь другую условную форму, которую вы можете использовать для преобразованного кода. Lisp предоставляет условные обозначения в качестве базовых конструкций. Предположим, что COND is такая базовая конструкция, то вы могли бы расширить IF в использование COND.

MY-IF как макрос в Common Lisp:

(defmacro my-if (condition true-clause false-clause)
   `(cond (,condition ,true-clause)
          (t ,false-clause)))

Итак,

(my-if (foo-p) 'one 'two)

расширяется в

(cond ((foo-p) 'one)
      (t 'two))

Ответ 4

Короткий ответ: Нет.

Long (er) answer: (if...) требует, чтобы вы контролировали порядок оценки аргументов. Lisp, будучи нетерпеливым языком, не может сделать это в функции.

Временное решение: сделайте это в макросе:

(defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))

Например:

[[email protected]:~]$ clisp -q
[1]> (defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))
_IF
[2]> (_if (= 1 1) (+ 2 3) "bar")
5
[3]> (_if (= 1 2) (+ 2 3) "bar")
"bar"
[4]> 

Ответ 5

Для полноты: нет специальных форм в Pico, например, и if является примитивной функцией, а Pico вдохновлен Scheme и по умолчанию оценивает оценку.

В схеме вы можете написать

(define (true t f)
  (t))

(define (false t f)
  (f))

(define (function_if c t e)
  (c t e))

а затем

(function_if true (lambda () 'true) (lambda () 'false))
==> true

Что делает это управляемым в Pico, так это то, что вы можете определить функциональные параметры, которые автоматически заменяют функциональные аргументы. Это означает, что вам не нужно делать обертывание внутри лямбда. Поэтому Пико имеет нетерпеливую оценку, но с ленивой оценкой по требованию, минуя необходимость в специальных формах.

Итак, в синтаксисе Scheme с функциональными параметрами вы можете кодировать логические значения как:

(define (true (t) (f))
  (t))

(define (false (t) (f))
  (f))

Тогда функция if становится:

(define (function_if c (t) (e))
  (c (t) (e)))

и

(function_if true 'true 'false)
==> true

В качестве другого примера определение функции and составляет (define (and p (q)) (p (q) false)).

Аналогичным образом вы можете определить or, not, while, for,... как функции, используя приведенную выше кодировку булевых.

Ответ 6

В Scala можно моделировать if с правильной оценкой побочных эффектов, используя аргументы по имени.

def If[A](cond : Boolean, truePart : => A, falsePart : => A) = if (cond) truePart else falsePart

Эти функции могут использоваться для моделирования большого количества новых структур управления.

Ответ 7

IF может быть функцией на функциональном языке, имеющем семантику по имени (ленивая оценка), как в Lambda Calculus или Algol. На самом деле это, я думаю, в основе отношений между Turing Machines и Lambda Calculus как эквивалентные основы для вычислений. Однако в языках, имеющих побочные эффекты (например, присвоения переменным), это не очень полезно, потому что, когда все происходит, важно.