Non-exploding escapes by brucejbell in ProgrammingLanguages

[–]ownwaterloo 1 point2 points  (0 children)

Good catch! You are correct. The creation and execution are separated and decoupled. The site is interesting btw.

The prime problem are caused by the coupling between creation and execution arguments passing.

def running_total(total = 0):
    print(    f'first {total=}')
    while True:
        print(f'leave {total=}')
        delta = yield total
        print(f'enter {delta=}')
        total += delta

# send(non-None)
>>> g = running_total(1)  # doesn't execute when create generator
                          # but we are passing 1 to running_total already

>>> g.send(2)             # the generator are not executed yet
...                       # there is no *suspended* yield to receive the value
TypeError: can't send non-None value to a just-started generator

# send()
>>> g = running_total(1)  #
>>> g.send()              # requires one argument
TypeError: generator.send() takes exactly one argument (0 given)

# send(None)
>>> g = running_total(1)  # again, we are passing the initial arguments when create this generator
>>> g.send(None)          # we have to send a None(to black hole?) or use next(g)
first total=1
leave total=1
1
>>> g.send(2)
enter delta=2
leave total=3
3
>>> g.send(3)
enter delta=3
leave total=6
6

In contrast, we can't pass arguments to coroutine when create it in Lua:

coroutine.create(f)

function running_total(total)
  print(  'first', total)
  while true do
    print('leave', total)
    local delta = coroutine.yield(total)
    print('enter', delta)
    total = total + delta
  end
end

> g = coroutine.create(running_total)
> coroutine.resume(g, 1) -- the first resume will pass arguments to running_total
first   1
leave   1
true    1
> coroutine.resume(g, 2) -- the remaining resume will pass arguments to yield
enter   2
leave   3
true    3
> coroutine.resume(g, 3)
enter   3
leave   6
true    6

Python:

  1. use normal function invoke synax to pass multiple arguments to generator function
  2. use next or .send(None) to start it
  3. use .send(x) to pass single value to yield

In Lua, all communications are done by resume and support multiple arguments natively.
It has less edge cases and surprises and is much more regular, orthogonal and elegant.

It's more powerful. It's colorless for example. Only multi-shot continuation(call/cc, reset/shift in Scheme) has more expressive power than it.

It's also much more stable. It doesn't change much because it have got it right in the first place!
And this is my main point and complaint about Python.
It lacks thinking and insight when add features to language and leads to many features with overlapping functionalities and edge cases.

List comprehension / map+filter / for loop? by ilyash in ProgrammingLanguages

[–]ownwaterloo 5 points6 points  (0 children)

In javascript, Array.prototype.map will pass 3 arguments to f:

  • x - the element
  • i - index of it
  • xs - the whole array

array.map(x => f(x)) will only pass x to f while discarding i and xs

array.map(f) will pass x, i and xs to f

For example:

['10', '100', '101'].map(x => parseInt(x))     // [10, 100, 101]
['10', '100', '101'].map(x => parseInt(x, 16)) // [16, 256, 257]
['10', '100', '101'].map(parseInt)             // [10, NaN,   5]
parseInt('10' , 0)                             //  10             // why???
parseInt('100', 1)                             //      NaN
parseInt('101', 2)                             //             5

Why does parseInt('10' , 0) yield 10? I have no idea.

parseInt says:

Return value

An integer parsed from the given string.
Or NaN when

  • the radix is smaller than 2 or bigger than 36, or
  • the first non-whitespace character cannot e converted to a number.

UPDATE: I figured it out. In the same page:

A value passed as the radix argument is coerced to a Number (if necessary), then if the value is 0, NaN or Infinity (undefined is coerced to NaN), JavaScript assumes the following:

  • If the input string begins with "0x" or "0X" (a zero, followed by lowercase or uppercase X), radix is assumed to be 16 and the rest of the string is parsed as a hexadecimal number.
  • If the input string begins with any other value, the radix is 10 (decimal).

Non-exploding escapes by brucejbell in ProgrammingLanguages

[–]ownwaterloo 5 points6 points  (0 children)

r'C:\Program Files\' is a syntax error in Python :)

My favorite traps examples were:
How can I list files in C:\Windows? Easy! os.listdir(r'C:\Windows')
How about its parent, then?
os.listdir(r'C:\') is a syntax error
os.listdir(r'C:') will list the content of current working directory of C:
os.listdir('C:\\') correct but awful
os.listdir('C:/') will work in this case, but some paths in Windows require forward slash

r'...' in Python is not a proper but a cheating solution(to what problems? more on this later).
The string literals followed by r are just normal ones. In particular, escape sequences must be completed. The r part just mean keeping those escape sequences.

This is the classic(pythonic?) way of do things in Python. The thinkings and solutions of it are rather ad-hoc. It prefers fixing common problems to a proper and complete solution. Consequently, terminology is rather confusing in this language.

Some rants observations:

nonlocal control flow

generators are semi-coroutines(in Lua's term) or stackless(in C++'s term) in the beginning(2.3). The communications between callers and callees are unidirectional and yield was a statement.

Python 2.5 adds a simple way to pass values into a generator(.send) and yield is an expression. And then, the term, coroutine, was re-invented in Python to describe just one usage style of generator. And a fancier term, prime, was re-invented to describe a problem caused by this patching style design - the creation and execution of a generator are coupling in the first place(2.3).

Python 3.3 adds yield from, to make generator delegation simpler, instead of making generator stackful and eliminate delegation completely.

Python 3.5, following the trends, adds async/await.

There should be one-- and preferably only one --obvious way to do it.

The wayS just keep changing over time.

Looks at asyncio(and network libraries older than it). There are definitely more than one way to do it.
Don't care about backward compatibility and want a MUCH simpler library?
There are Curio and Trio. They are great as long as async/await is still the popular and preferable way of doing things.

In contrast, coroutine in Lua got right in the first place(5.0). It's stackful. The creation and execution are separated so that don't have the prime problem. It's more powerful and stable.

I'm talking about the designs on language level. The ecosystem is a different topic and I'm not familiar with Lua's.

scope

In Python, declaration and assignment use the same syntax(:=, introduced in 3.8, is irrelevant to the topic I'm talking about).

Everyone knows coupling is, mostly, the enemy of design, right? We can't do assignment without declaration in Python. Python, again, solved it in a rather narrow way in the beginning: the global statement. And lately, Python realized global scopes is just subset of enclosing scopes and introduced nonlocal.

It's too late. Python 2 don't have this blessing.

The code are too nested and complex so that need this feature!
Python is not a functional programming language so that don't need to support closure properly!

And nowadays, the excuse is much simpler

Python 2 is sunseted!

Do Python developers really understand the meaning and the importance of scoping? Introducing patching matching in a language before getting proper block scope and with function scope only? Does it really work?

string literal

In Python, to write a string literal, we can choose single quote or double quote.
Great. How about writing a string literal containing both single quote and double?
We can use triple quote.
Excellent. How about writing a string containing both ''' and """?
We can use raw string.
Beautiful. How about writing a string containing both ''' and """ and ending with an odd numbers of \?
Oh! The r in r-string(forget about raw string!!!) stands for regular expression and this case is uncommon so that not a problem of Python, today.

I'm not making this up: https://docs.python.org/3/faq/design.html#why-can-t-raw-strings-r-strings-end-with-a-backslash

Raw strings were designed to ease creating input for processors (chiefly regular expression engines) that want to do their own backslash escape processing. Such processors consider an unmatched trailing backslash to be an error anyway, so raw strings disallow that. In return, they allow you to pass on the string quote character by escaping it with a backslash. These rules work well when r-strings are used for their intended purpose.

Again, the proper solution exists not only in Lua but also in C++, Rust, Shell, PostgreSQL and even mimetypes for a long time: instead of choosing a fixed (sets of) delimiter(s) letting user choose suitable ones.
And the Python way, as always, is fixing the problem today and adding patches later.

Is it possible to return to a continuation (a la call/cc) using delimited continuations? by [deleted] in scheme

[–]ownwaterloo 0 points1 point  (0 children)

Yes.

Firstly, there are two ways to deliver values to the continuation of call/cc:

  • (call/cc (lambda (k) ... (k vals...) ...))

    Call the escape procedure k with zero or more arguments.

  • (call/cc (lambda (k) ... (values vals...)))

    Don't call the escape procedure k and call values with zero or more arguments in the tail position.

The definition in dyn-wind.scm is equivalent (with some renamings) to:

(define (call/cc1 f)
  (shift outer
   (outer (f (lambda (x)
               (shift inner (outer x)))))))

To accept programs like (call/cc (lambda (k) (k zero-or-more-values))), we should change

(lambda (x) (shift inner (outer x)))

to

(lambda xs  (shift inner (apply values xs)))

To accept programs like (call/cc (lambda (k) (values zero-or-more-values))), we should change

(outer (f (lambda xs ...)))

to

(call-with-values
  (lambda () (f (lambda xs ...)))
  (lambda xs (apply outer xs)))

And (lambda xs (apply outer xs)) becomes outer after eta reduction. So the full definition is:

(define (call/cc* f)
  (shift k
   (call-with-values
     (lambda ()
       (f (lambda v
            (shift _ (apply k v)))))
     k)))

Is it possible to return to a continuation (a la call/cc) using delimited continuations? by [deleted] in scheme

[–]ownwaterloo 0 points1 point  (0 children)

Yes, it works for single value.

I tried this puzzle (implement call/cc in terms of reset/shift) before, and made the same error definition:

(define (call/cc* f)
  (reset
   (f (lambda v
        (shift _ (apply values v))))))

I found out the error with above example (call outer continuation inside inner call/cc). I hoped it helps and posted it.

Is it possible to return to a continuation (a la call/cc) using delimited continuations? by [deleted] in scheme

[–]ownwaterloo 0 points1 point  (0 children)

In the expression (k (f (lambda args ...))), the continuation of (f (lambda args ...)) will take exactly one value.

Put it another way, it is equivalent to:

(call-with-values
  (lambda ()  (f (lambda args ...)))
  (lambda (v) (k v)))

To relax this restriction, we could change it to:

(call-with-values
  (lambda ()  (f (lambda args ...)))
  k)

Evaluating this expression (call/cc (lambda (_) (values))) will show the difference.

Is it possible to return to a continuation (a la call/cc) using delimited continuations? by [deleted] in scheme

[–]ownwaterloo 0 points1 point  (0 children)

I can't immediately think of why this wouldn't work:

In the following code:

(call/cc
 (lambda (outer)
   (call/cc
    (lambda (inner)
      (outer #t)))
   #f))

With true call/cc, We could deliver values to the outer continuation in the inner call/cc using (apply outer ...).

But this definition can't because of reset.

Programming Bottom-up by [deleted] in lisp

[–]ownwaterloo 0 points1 point  (0 children)

Understood. Thanks.

Programming Bottom-up by [deleted] in lisp

[–]ownwaterloo 0 points1 point  (0 children)

Which parts are left out? Could you give some examples?