LINUX.ORG.RU

Замыкания по-русски

 ,


0

1

Часто, адепты ФП промывают мозги по-поводу того, что лексическое связывание позволяет легко строить «объекты и классы», вроде этого

(define f (lambda(fu) (lambda(x) (fu x x))))
(define sum (f +))
(define mult (f *))
А я че-то подумал щас, собственно при динамическом биндинге это сделать так же легко
(set 'f (fn() (fu n n)))
(set 'sum (fn(n) (let(fu +) (f))))
(set 'mult (fn(n) (let(fu *) (f))))
Никакого преимущества у калош тут нет, фактически.


Ответ на: комментарий от anonymous

У этой «старой», как ты ее называешь, никакого значения вообще нет

Как это нет? Куда оно делось?

anonymous
()
Ответ на: комментарий от anonymous

собственно, если ты так хочешь чтобы переменная «исчезала» при выходе из скопа:

#lang racket
(require syntax/parse/define
         racket/undefined)

(define-simple-macro (dlet ([id:id e:expr] ...+) body:expr ...+)
  #:with (pre-tmp ...) (generate-temporaries #'(id ...))
  #:with (tmp ...) (generate-temporaries #'(id ...))
  (let ([pre-tmp (if (eq? undefined (namespace-variable-value 'id #f (λ () undefined)))
                     (begin (namespace-set-variable-value! 'id undefined)
                            undefined)
                     id)] ... 
        [tmp e] ...)
    (set! id tmp) ...
    body ...
    (set! id pre-tmp) ...
    (when (eq? undefined id) (namespace-undefine-variable! 'id)) ...))

(define-simple-macro (dlambda (arg:id ...) body:expr ...)
  #:with (tmp ...) (generate-temporaries #'(arg ...))
  (lambda (tmp ...)
    (dlet ([arg tmp] ...)
          body ...)))

(define (set arg e)
    (eval `(namespace-set-variable-value! ',arg ',e)))
->
Добро пожаловать в DrRacket, версия 6.0.1 [3m].
Язык: racket [выбранный].
> (set 'def-val (dlambda (arg) (set arg val)))
> (dlet ([val 'foo]) 
        (def-val 'a))
> a
'foo
> val
val: undefined;
 cannot reference an identifier before its definition
> 

anonymous
()
Ответ на: комментарий от anonymous

Ну ты напиши, что не будет работать. Все твои примеры работают. На самом деле т.к. все переменные глобальные, то это снимает проблемы с невозможностью евала во внутреннем скопе - то есть без проблем все можно переписать не ехал евал через евал без макросов. Но незачем.

anonymous
()
Ответ на: комментарий от anonymous

А вот так сможет твоя форма?

Конечно, ведь arg глобальные. Нет никаких проблем заевалить:

Добро пожаловать в DrRacket, версия 6.0.1 [3m].
Язык: racket [выбранный].
> (set 'def-var (fn(arg) (eval arg)))
> (let ([val 'foo]) (def-var '(set 'a val)))
> a
'foo
> 
немного код поправил (из-за when в конце dlet ничего не возвращал, что приводило к ошибке) и причесал:
#lang racket
(require syntax/parse/define
         racket/undefined)

(define-simple-macro (dlet ([id:id e:expr] ...) body:expr ...+)
  #:with (pre-tmp ...) (generate-temporaries #'(id ...))
  #:with (tmp ...) (generate-temporaries #'(id ...))
  (letrec ([pre-tmp (namespace-variable-value 'id #f (λ () (set 'id undefined) id))] ... 
           [tmp e] ...)
    (set! id tmp) ...
    (begin0 (begin body ...)
            (if (eq? pre-tmp undefined)
                (namespace-undefine-variable! 'id)
                (set! id pre-tmp)) ...)))

(define-simple-macro (fn (arg:id ...) body:expr ...)
  #:with (tmp ...) (generate-temporaries #'(arg ...))
  (lambda (tmp ...)
    (dlet ([arg tmp] ...)
          body ...)))

(define (set arg e)
    (namespace-set-variable-value! arg e))

(define-syntax let (syntax-local-value #'dlet))

anonymous
()
Ответ на: комментарий от anonymous

Ну, если вот это сможет

(set 'def-val (fn(arg) (set arg val)))
(let (val 'foo) (map def-val '(a b c)))
то я почти поверил в схему

anonymous
()
Ответ на: комментарий от anonymous

Ну особой разницы с предыдущими примерами нет, офк сможет:

Добро пожаловать в DrRacket, версия 6.0.1 [3m].
Язык: racket [выбранный].
> (set 'def-val (fn(arg) (set arg val)))
> (let ([val 'foo]) (map def-val '(a b c)))
'(#<void> #<void> #<void>)
> a
'foo
> b
'foo
> c
'foo
> 

anonymous
()
Ответ на: комментарий от anonymous

Да не, я ошибся, извиняюсь, думал, что ты придуриваешься. Но я пока не понял, как все работает, надо разобраться.

anonymous
()
Ответ на: комментарий от anonymous

Чтобы не исправлять на dlet каждый раз в копипасте.

anonymous
()
Ответ на: комментарий от anonymous

Но я пока не понял, как все работает, надо разобраться.

Все просто - если переменная с нужным именем есть, то мы делаем на нее set! в начале let и возвращаем старое значение в конце let. Если переменной еще нет - то добавляем ее в глобальный неймспейс в начале let (и удаляем в конце). Поскольку все переменные оказываются глобальными, то eval just works.

anonymous
()
Ответ на: комментарий от anonymous

Ну, так, получается, что ты реализовал на базе слабого ЯП более мощный язык. У тебя получился лисп с динамическим биндингом. Весь вопрос в том, что если ты будешь использовать в программах лексические замыкания, ты опять откатишься, а если не будешь — будешь писать на нормальном лиспе, вот и все.

super-hack-new-user
()
Ответ на: комментарий от super-hack-new-user

Ну, так, получается, что ты реализовал на базе слабого ЯП более мощный язык.

Нет, наоборот. На базе более слабого языка более мощный реализовать нельзя (как нельзя сделать лексический скоп из динамического).

anonymous
()
Ответ на: комментарий от anonymous

Написать метациклический интерпретатор. Это гораздо проще, чем макросы, имхо.

anonymous
()
Ответ на: комментарий от anonymous

Нет, наоборот. На базе более слабого языка более мощный реализовать нельзя (как нельзя сделать лексический скоп из динамического).

Это неправда. Ассемблер — это более мощный язык, чем машкод, но первый асм реализован на машкоде.

anonymous
()
Ответ на: комментарий от anonymous

Это неправда. Ассемблер — это более мощный язык, чем машкод, но первый асм реализован на машкоде.

Реализация написана на машкоде, но асм не реализован на базе машкода.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.