Pregunta

I recently started writing non-trivial programs in Logo (non-trivial in the sense no turtle graphics). One of the major hurdles I ran into was dynamic scoping. For example consider the following program:

to foldl :f :acc :list [:index 1]
    output ifelse empty? :list [:acc] [
        (foldl :f (invoke :f :acc first :list :index) butfirst :list :index + 1)
    ]
end

to permute :list
    output ifelse empty? :list [[[]]] [
        foldl [[permutations item index]
            sentence :permutations map [[list]
                fput :item :list
            ] permute delete :index :list
        ] [] :list
    ]
end

The permute function works for the empty list [] for which it produces the output [[]] and lists with a single item [a] for which it produces the output [[a]]. However it fails for lists with two or more elements.

Guess why it fails? The lambda function passed to foldl from permute accesses the free variable list and because foldl also has a local variable named list it accesses the wrong variable. Because foldl is defined recursively the list variable keeps shrinking with each iteration.

I solved this problem by saving a copy of the original list in the foldl function as follows:

to foldl :f :acc :list [:index 1] [:original :list]
    output ifelse empty? :list [:acc] [
        (foldl :f (invoke :f :acc first :list :index :original)
            butfirst :list :index + 1 :original)
    ]
end

to permute :list
    output ifelse empty? :list [[[]]] [
        foldl [[permutations item index list]
            sentence :permutations map [[list]
                fput :item :list
            ] permute delete :index :list
        ] [] :list
    ]
end

However it took me the better part of the evening to figure out what was causing this strange error. I had never programmed in a language with dynamic scoping before (save small snippets of bash scripting).

Hence my question is as follows: what should you keep in mind when writing functions in languages which have dynamic scoping? What are the best practices? How do you avoid common pitfalls?

¿Fue útil?

Solución 2

Minimize the use of free variables.

Otros consejos

I would keep in mind that these languages don't have closures.

Maybe they have, but with an extra construct (like some Lisp languages a few decades ago). Even worse, sometimes the Interpreter and the Compiler had different semantics - like in some old Lisp dialects decades ago.

There is a reason Lisp mostly switched to lexical binding (Scheme explored it in the mid 70s, Common Lisp got it the mid 80s, Emacs Lisp just recently got support for it.).

Basically if you want to do advanced functional programming, stay away from dynamically scoped languages.

Use SML, Scheme, CL, Haskell, Racket, OCAML, ... instead.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top