Re: Python-based monads essay part 2

2016-10-20 Thread Marko Rauhamaa
Gregory Ewing :

> Marko Rauhamaa wrote:
>
>> def main():
>> try:
>> guard_it()
>> except ValueError:
>> # I'm having none of it!
>> resume "CarryOn!"
>>
>> The problem is, how can the file f unclose itself when work resumes?
>
> The same thing could happen in Scheme, though. Re-entering a context
> via a continuation doesn't magically reset everything to the way it
> was at some earlier time. A data structure that has been mutated will
> still be mutated, and a file that has been closed will still be
> closed.

Obviously. However, Scheme continuations are (or can be) used to
implement all other single-threaded paradigms (coroutines, generators,
exceptions etc), so the execution context quite normally jumps around
"erratically". There is no automatic method to decide when final is
final.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-20 Thread Gregory Ewing

Marko Rauhamaa wrote:


def main():
try:
guard_it()
except ValueError:
# I'm having none of it!
resume "CarryOn!"

The problem is, how can the file f unclose itself when work resumes?


The same thing could happen in Scheme, though. Re-entering a
context via a continuation doesn't magically reset everything
to the way it was at some earlier time. A data structure that
has been mutated will still be mutated, and a file that has
been closed will still be closed.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-20 Thread Gregory Ewing

Chris Angelico wrote:

Okay. Now let's suppose that, instead of "73" in the first step, you
have "ask the user for an integer". Are you allowed to eliminate this
prompt, since the result of it cannot possibly affect anything?


That's an excellent question. The answer is no, you're
not allowed to eliminate it, and I'll try to explain why.

In Python, you're talking about something like this:

  def get_int():
  return int(input())

  print(0 * get_int())

But you can't write it that way in Haskell, because I/O
actions are not functions that return results. The Haskell
equivalent would be something like

  main =
  get_int >>= (\n ->
  print (0 * n))

Here's what happens. We've defined 'main' as a function that
returns an I/O instruction, "get an integer". That instruction
has a callback attached, namely the lambda expression
'\n -> print (0 * n)'.

The top level of the Haskell system sees this instruction,
carries it out, and then invokes the callback, passing the
number it got as an argument.

Inside the lambda, the number is bound to n. The lambda multiplies
it by 0 and then returns *another* I/O instruction, one that says
"print this number". The Haskell system carries out that instruction.
No further I/O instructions result, so the process is terminated.

Inside the lambda, the Haskell compiler is free to notice that
n is being multiplied by 0 and optimise it to just 'print 0'.
But the body of the lambda doesn't get evaluated until *after*
the "get integer" instruction has been carried out, and there is
no evaluation path that avoids that.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-20 Thread Steven D'Aprano
On Thursday 20 October 2016 04:57, Chris Angelico wrote:

> Short-circuiting depends only on what's known *prior* to that
> evaluation. Dead code elimination removes the notion of time:

Surely not. I understand "dead code" to mean the same as *unreachable* code: 
code which cannot be reached by any path through the program:

if False:
spam()  # dead code

not merely code that doesn't affect the return result:

x = spam()
x = 999

Calling spam is not dead code, even if the result is immediately thrown away!

In any case, whichever definition you use, for spam() to be dead code in the 
later example, it has to be known to have no side-effects.


> int(input("Enter something:")) * 0 + 5
> 
> This can simply return 5, because (barring an exception being thrown)
> the value entered cannot affect the result. Python does not do this.
> And any system that has optimization flags (PostgreSQL, Pike, etc),
> this would be marked "has side effects", and therefore is not
> considered dead code. But if you (maybe mistakenly) mark input() as a
> pure function, then yes, this could indeed be optimized out.

*Assuming* that the compiler can distinguish between functions with side-
effects and those that aren't, and *assuming* that the compiler knows that 
input() returns something which can be cast to an int with no side-effects or 
errors, then in principle the compiler could optimize that to just 5.

But I don't think that's terribly likely, even for an aggressive optimizing 
compiler. Maybe C, because there's nothing so terrible I wouldn't believe about 
C compilers *wink*

This is not the best example, because its obvious that the call to int() might 
fail, and hence the effect of this code may be to raise ValueError. A better 
example would be:

len(input("Enter something:")) * 0 + 5


Assuming the compiler knows that len() and input() have their standard meaning, 
is it justified to optimized that expression to just 5? I don't think so, 
because the side effect of input() displaying text on the screen and waiting 
for the user to type something is the whole reason for the function to exist.

Regardless of whether your language is imperative, or functional with monads, I 
think that expression has to write text to the screen and wait for the user to 
type something, or the compiler is buggy. But it doesn't have to happen 
straight away! Consider:

L = [0, 1, 2, 3, 4, len(input("A")) * 0 + 5, 6]
print("B")
print(L[5])

Does this program print "A B 5", or "B A 5"? In Python, it prints "A" first, 
but that may not be the case for all languages. My guess is that, like 
declarative, asynchronous, concurrent and parallel programming (did I miss any 
buzzwords?), functional programming doesn't guarantee the order in which code 
is executed.

Actually, neither does imperative programming, if the language supports "call 
by name" or similar parameter passing mechanisms:


def call_by_name(x):
print("B")
print(x)

call_by_name(len(input("A")) * 0 + 5)


If x is a thunk, then this too will print "B" first.


>> However, if we think of the I/O interaction (aka *the process*) to be
>> the return value of a function, every bead in the I/O chain counts.
> 
> And that's what I mean about making function purity meaningless. Here,
> look - this is a pure function!
> 
> magic_numbers = [1, 2, 4, 8]
> def do_magic(x):
> magic_numbers[x % 4] += x / 4
> return magic_numbers[(x + 1) % 4]
> 
> Since the previous state of magic_numbers can be considered an input,
> and the new state can be considered an output, this is a pure
> function! Right?

No, but you know that :-)

Inputs are explicitly arguments to the function, not external state. Here's how 
we can turn that into real input:

def do_magic(state, x):
state = calculate_new_state(state)  # work it out for yourself *wink*
value = state[(x + 1) % 4]
return state, value

and then use a monad to make the state invisible, so that you actually only 
need to call do_magic(x).

I'm hand-waving here because I don't remember all the fine details from Greg's 
essay. Call it magic if you want, but the compiler can do it, because the 
details of how state is stored is invisible to the caller, the compiler is free 
to do the optimization:


change the functional but inefficient idiom 
state = calculate_new_state(state)
into the imperative but efficient idiom:
modify_in_place(state)


That's allowed, because (1) nobody and nothing except the do_magic() function 
can access the state; (2) the use of a monad makes this completely transparent 
and mathematically vigorous, which means the compiler can do it without human 
intervention; and (3) we want the program to run in less than a billion 
petabytes of memory. Practicality beats purity.

All joking aside, the whole point of functional programming is to avoid the 
human programmer having to track external state. It's not actually to avoid 
external state: given our existing hardware, such a 

Re: Python-based monads essay part 2

2016-10-19 Thread Marko Rauhamaa
Chris Angelico :

> On Thu, Oct 20, 2016 at 3:51 AM, Marko Rauhamaa  wrote:
>>
>> def print(world, text, cont):
>> return cont(World(past=world, offset=text))
>>
>> def print_x_then_y(world, x, y, cont):
>> return print(world, x, lambda world2: print(world2, y, cont))
>>
> [...]
>>
>> The end result is an ordered sequence of events. The topological order
>> is a causal order (you need the past world to construct the future
>> world), and causal order generates a temporal order.
>
> Yeah, nice. You just made a mutable object with significant order of
> evaluation and then added enough external information to it that you
> can pretend it's immutable and can be evaluated in any order.

Please point out what is mutable in my code above. It doesn't have a
single mutable object. The world is recreated with each interaction.

(Parenthetically, that's how quantum mechanics explains life, universe
and everything. Nothing ever changes. Immutable particles/systems simply
get destroyed and created with every interaction. In fact, particles can
be abstracted out as figments of imagination -- the existence is a
network of interactions.)

> I could make that same transformation with literally any function,
> simply by taking "the world" as an input and an output. It makes the
> entire concept utterly meaningless.

Whether this mental gymnastics is useful, everybody can form their own
opinion.

A funny, semirelated anecdote about Scheme. Scheme does not have a
try/finally construct. It can't -- Scheme is too powerful. That's
because in Scheme, nothing is [guaranteed to be] final. After you leave
an execution context, a continuation may leak out that enables a
system-wide jump in the middle of the context.

Python could have similar continuations if it added a "resume" keyword:

def work(f):
...
message_from_grave = raise ValueError()
if message_from_grave == "CarryOn!":
...

def guard_it():
f = open(...)
try:
work(f)
finally:
f.close()

def main():
try:
guard_it()
except ValueError:
# I'm having none of it!
resume "CarryOn!"

The problem is, how can the file f unclose itself when work resumes?
Well, maybe we'd need another keyword:

def guard_it():
f = open(...)
try:
work(f)
finally:
f.close()
nevermind:
f.reopen()


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-19 Thread Chris Angelico
On Thu, Oct 20, 2016 at 3:51 AM, Marko Rauhamaa  wrote:
> Chris Angelico :
>> Okay. Now let's suppose that, instead of "73" in the first step, you
>> have "ask the user for an integer". Are you allowed to eliminate this
>> prompt, since the result of it cannot possibly affect anything? And if
>> not, why not?
>
> I would guess yes; that's how Python works as well:
>
> >>> 7 or input()
> 7
>

That's not quite the same. That's equivalent to:

if 7:
7
else:
input()

Short-circuiting depends only on what's known *prior* to that
evaluation. Dead code elimination removes the notion of time:

int(input("Enter something:")) * 0 + 5

This can simply return 5, because (barring an exception being thrown)
the value entered cannot affect the result. Python does not do this.
And any system that has optimization flags (PostgreSQL, Pike, etc),
this would be marked "has side effects", and therefore is not
considered dead code. But if you (maybe mistakenly) mark input() as a
pure function, then yes, this could indeed be optimized out.

> However, if we think of the I/O interaction (aka *the process*) to be
> the return value of a function, every bead in the I/O chain counts.

And that's what I mean about making function purity meaningless. Here,
look - this is a pure function!

magic_numbers = [1, 2, 4, 8]
def do_magic(x):
magic_numbers[x % 4] += x / 4
return magic_numbers[(x + 1) % 4]

Since the previous state of magic_numbers can be considered an input,
and the new state can be considered an output, this is a pure
function! Right?

>> If you consider that the world changes state as a result of asking the
>> user for input, then you've just eliminated all notion of functional
>> purity.
>
> Not necessarily. Nothing changes the world. Rather you have different
> worlds: the world of the past and the world of the future. The world of
> the past is in the past of the world of the future:
>
> def print(world, text, cont):
> return cont(World(past=world, offset=text))
>
> def print_x_then_y(world, x, y, cont):
> return print(world, x, lambda world2: print(world2, y, cont))
>
>> You have side effects, plain and simple, and you're using imperative
>> code.
>
> Technically, no. Thought-model-wise, yes.

Technical means nothing if you're destroying the meaning of functional
purity in order to squeeze I/O into it.

> My example above is purely functional as:
>
>  * Every object is immutable.
>
>  * The order of evaluation does not change the end result.
>
> The end result is an ordered sequence of events. The topological order
> is a causal order (you need the past world to construct the future
> world), and causal order generates a temporal order.

Yeah, nice. You just made a mutable object with significant order of
evaluation and then added enough external information to it that you
can pretend it's immutable and can be evaluated in any order. I could
make that same transformation with literally any function, simply by
taking "the world" as an input and an output. It makes the entire
concept utterly meaningless.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-19 Thread Marko Rauhamaa
Chris Angelico :
> Okay. Now let's suppose that, instead of "73" in the first step, you
> have "ask the user for an integer". Are you allowed to eliminate this
> prompt, since the result of it cannot possibly affect anything? And if
> not, why not?

I would guess yes; that's how Python works as well:

>>> 7 or input()
7


However, if we think of the I/O interaction (aka *the process*) to be
the return value of a function, every bead in the I/O chain counts.

> After all, eliminating terms that have been multiplied by zero is one
> form of algebra.

I wonder if the word "algebra" brings anything to this discussion. It
doesn't make Haskell any more or less functional.

> If you consider that the world changes state as a result of asking the
> user for input, then you've just eliminated all notion of functional
> purity.

Not necessarily. Nothing changes the world. Rather you have different
worlds: the world of the past and the world of the future. The world of
the past is in the past of the world of the future:

def print(world, text, cont):
return cont(World(past=world, offset=text))

def print_x_then_y(world, x, y, cont):
return print(world, x, lambda world2: print(world2, y, cont))

> You have side effects, plain and simple, and you're using imperative
> code.

Technically, no. Thought-model-wise, yes.

Of course, recursive functions can simulate Turing machines and vice
versa. You can write purely functional code in purely imperative style.

My example above is purely functional as:

 * Every object is immutable.

 * The order of evaluation does not change the end result.

The end result is an ordered sequence of events. The topological order
is a causal order (you need the past world to construct the future
world), and causal order generates a temporal order.

Now, *if* the Haskell VM chooses to *realize* I/O events *as soon as* it
becomes aware of them, you get traditional, imperative side effects.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-19 Thread Chris Angelico
On Wed, Oct 19, 2016 at 9:26 AM, Gregory Ewing
 wrote:
> The things being reasoned about -- the actions -- are
> not themselves functions, but that doesn't mean there's
> any cheating going on. Would you say it was cheating
> to perform algebraic manipulations on an expression
> involving numbers or strings? Why should it be any
> different when the things being manipulated represent
> actions that affect the world?

Okay. Let me pick up two of functional programming's favourite idioms,
and you can help me understand how I/O monads fit into them.

1) Lazy evaluation. In an imperative language, you can't say "make me
a list of prime numbers, then take the 73rd element"; but functional
languages are perfectly happy to have an infinite-length as an object,
as long as you never actually ask for the last element or anything.

2) Related to the above: Dead code elimination. If the 73rd prime ends
up being multiplied by zero, Haskell is entirely justified in not
calculating it.

Okay. Now let's suppose that, instead of "73" in the first step, you
have "ask the user for an integer". Are you allowed to eliminate this
prompt, since the result of it cannot possibly affect anything? And if
not, why not? After all, eliminating terms that have been multiplied
by zero is one form of algebra. If numbers and strings and I/O monads
are all the same, there's no difference between "length of this
string" (an operation yielding an integer) and "ask the user for an
integer".

If you consider that the world changes state as a result of asking the
user for input, then you've just eliminated all notion of functional
purity. You have side effects, plain and simple, and you're using
imperative code. At what point can you deem that the algebra is safe?
At what boundary can any of your code manipulations still be done?

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Gregory Ewing

Chris Angelico wrote:

You can take your
original set-builder monad and turn it into genuinely functional code;
show me that you can do the same with I/O. Otherwise, what you're
really saying is "we can cheat until we can do I/O", not "we can do
I/O in a functional way".


I don't think I'm saying either of those things.

What I'm saying is, monads provide a way of reasoning
algebraically about sequential programs.

The things being reasoned about -- the actions -- are
not themselves functions, but that doesn't mean there's
any cheating going on. Would you say it was cheating
to perform algebraic manipulations on an expression
involving numbers or strings? Why should it be any
different when the things being manipulated represent
actions that affect the world?

I may not have brought this point out as clearly as
I could have in the essay. Looking back, I can see
that the phrase "perform I/O in a functional way" is
open to misinterpretation. I didn't mean to say that
we can write a function (in the mathematical sense)
that performs I/O, only that we can apply functional
techniques if we represent I/O in a particular way.

If you still want to call that "cheating" I can't
stop you, but I think you're directing your scorn at
an imagined claim that functional programmers are not
actually making. In so doing you risk missing potential
benefits of what they actually are claiming.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Chris Angelico
On Tue, Oct 18, 2016 at 9:50 PM, Rustom Mody  wrote:
> Now we can functionalize all this by making 'the world' as a parameter to our 
> functions.
> Greg talks of this as do other writings on functional IO
> I personally find talking of 'world' as though its an object in my little
> world to be a sleight of hand.

There is some value in this concept. If you're trying to unit-test
something, you need to be able to pin down all the inputs and outputs
of a piece of code. Thus, when you document that code, you need to
acknowledge that "the world" is one of its inputs - and that unit
testing it requires either access to said world, or a mocked version
of the significant parts of it.

Doesn't magically make the function pure, except in the trivial and
useless sense that mutable globals just count as inputs and outputs,
and thus all functions are pure.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Rustom Mody
On Tuesday, October 18, 2016 at 4:07:53 PM UTC+5:30, Marko Rauhamaa wrote:
> Chris Angelico wrote:
> > If you want to prove to me that monads are still functional,
> 
> Ultimately, monads are simply the pipelining pattern. It is used
> extensively in Unix shells. (I don't know Microsoft's PowerShell, but it
> used to be called "Monad".)
> 
> > Otherwise, what you're really saying is "we can cheat until we can do
> > I/O", not "we can do I/O in a functional way".
> 
> I guess it's justified as follows: in functional programming, your
> program is a function that produces an output out of its input. IOW:
> 
> program(input)
> => output
> 
> Because of lazy evaluation, the program can start before the input is
> prepared and output can be returned gradually before the program
> finishes.

This would be an ok view if 'world' consisted of only stdin/stdout
As it happens it consists of files, filesystems, NICs talking to... uh a
more real and somewhat bigger world

Now we can functionalize all this by making 'the world' as a parameter to our 
functions.
Greg talks of this as do other writings on functional IO
I personally find talking of 'world' as though its an object in my little
world to be a sleight of hand.

OTOH I dont know any better...
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Marko Rauhamaa
Chris Angelico :
> I think, it is patently false to claim that Haskell is purely
> functional - but the I/O monads are a crucial feature in making the
> language useful.

As soon as you accept that values can exist in "a superposition," that
is, in a not-yet-fully-materialized form, you can see that functionality
is not broken.

Similarly you can imagine a function that returns the exact decimal
representation of π; the function returns its value, which is printed on
your screen by the REPL. Digits start whizzing by well before the
function has completed its computation.

No side effects needed, but digit ordering is, of course, crucial.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Marko Rauhamaa
Chris Angelico :
> If you want to prove to me that monads are still functional,

Ultimately, monads are simply the pipelining pattern. It is used
extensively in Unix shells. (I don't know Microsoft's PowerShell, but it
used to be called "Monad".)

> Otherwise, what you're really saying is "we can cheat until we can do
> I/O", not "we can do I/O in a functional way".

I guess it's justified as follows: in functional programming, your
program is a function that produces an output out of its input. IOW:

program(input)
=> output

Because of lazy evaluation, the program can start before the input is
prepared and output can be returned gradually before the program
finishes. This principle is analogous to Python's generators:

   def program(input):
   for data in input:
   whenever output is produced:
   yield output

Similarly, a Haskell program can take in lines, keypresses, mouse clicks
etc through its input stream and consume the stream gradually, and
generate lines, sounds, animation etc to its output stream gradually.

That gradual inputting and outputting can be carried out purely
functionally. Consider, for example, the task of finding a zero in an
infinite stream of numbers in Python:

def skip_over_zero(stream, cont):
if stream.head() == 0:
return cont(stream)
return skip_over_zero(stream.tail(), cont)


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Chris Angelico
On Tue, Oct 18, 2016 at 8:56 PM, Rustom Mody  wrote:
> One thing probably is correct in what you are saying (if you are saying!):
> The IO monad cannot be programmed at the vanilla user/programmer level but 
> must
> be hardwired into the system (for an FPL of course; for python its all demo 
> anyway)

That's about right. (And yeah, I'm fine with the Python demos being a
bit forced - it's a lot easier than all of us trying to learn Haskell
syntax. Though I do appreciate the examples of the Haskell
equivalents, where we get to see how clean it looks in actual usage.)

So what that really means is:

1) In actual Haskell code (as opposed to standard library code), there
are certain restrictions, including that your functions must be pure.
2) Standard library code is allowed to play fast and loose with the rules.
3) This makes Haskell actually useful, instead of being an interesting
research project.

For comparison, Python has similar rules:

1) In actual Python code, there are certain restrictions, including
that your object references must be perfectly maintained.
2) Standard library code is allowed to play fast and loose (eg making
weakrefs that don't count as actual references, or releasing the GIL
while doing heavy computation, etc).
3) This makes Python actually perform fast, instead of being a pure
and naive byte-code interpreter.

In each case, I absolutely agree with the third point. There is
nothing wrong with sacrificing a bit of purity in order to gain a ton
of practicality, especially if you can bury the sacrifice away in some
deep corner of the language. However, it is patently false to claim
that all of CPython could be implemented using equivalent Python code;
for instance, you would have no way of ever creating the top-of-tree
loop wherein 'object' is an instance of 'type', and 'type' is a
subclass of 'object'. In the same way, I think, it is patently false
to claim that Haskell is purely functional - but the I/O monads are a
crucial feature in making the language useful.

Just please, stop trying to claim that it's purely functional. It
isn't. It's imperative with functional paint.

(Unless I'm flat wrong in my analysis, of course, in which case I'm
happy to be corrected in my understanding.)

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Rustom Mody
On Tuesday, October 18, 2016 at 2:55:07 PM UTC+5:30, Chris Angelico wrote:
> On Fri, Oct 14, 2016 at 9:52 AM, Gregory Ewing wrote:
> > A bit more on SMFs, and then some I/O.
> >
> > http://www.cosc.canterbury.ac.nz/greg.ewing/essays/monads/DemystifyingMonads2.html
> 
> Finally finished reading this - it's been up in a tab in Chrome for
> the past few days.
> 
> So here's how I summarize your explanation of monads:
> 
> 1) Functional programming can describe states and transitions.
> 2) We can cheat with those by hiding the states and implementing
> things imperatively.
> 3) Once we're already implementing things imperatively, we can do I/O.

Seems like a reasonable summary (to me; would like to hear Greg tho')

> 
> If you want to prove to me that monads are still functional, *REVERSE*
> your transformation in the original article. You can take your
> original set-builder monad and turn it into genuinely functional code;
> show me that you can do the same with I/O. Otherwise, what you're
> really saying is "we can cheat until we can do I/O",

Dont understand the 'until'

> not "we can do I/O in a functional way".

Not sure what you mean by 'cheat' and 'functional'
I have a feeling you are assuming a definition of 'functional that is
unusable in practice such as "programming without any effects"

People who actually use FP to do serious work probably need to use a more
nuanced definition such as
- being very explicit about effects (disallowing side-effects is quite 
different from disallowing all effects)
- making sure the effects are well documented
  'Documented' could be quite different: eg in scheme its an informal naming
   convention — names containing a '!' and pronouned SHRIEK are non-functional
   In haskell its more formal: effectful things must have monadic types

One thing probably is correct in what you are saying (if you are saying!):
The IO monad cannot be programmed at the vanilla user/programmer level but must 
be hardwired into the system (for an FPL of course; for python its all demo 
anyway)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-18 Thread Chris Angelico
On Fri, Oct 14, 2016 at 9:52 AM, Gregory Ewing
 wrote:
> A bit more on SMFs, and then some I/O.
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/essays/monads/DemystifyingMonads2.html

Finally finished reading this - it's been up in a tab in Chrome for
the past few days.

So here's how I summarize your explanation of monads:

1) Functional programming can describe states and transitions.
2) We can cheat with those by hiding the states and implementing
things imperatively.
3) Once we're already implementing things imperatively, we can do I/O.

If you want to prove to me that monads are still functional, *REVERSE*
your transformation in the original article. You can take your
original set-builder monad and turn it into genuinely functional code;
show me that you can do the same with I/O. Otherwise, what you're
really saying is "we can cheat until we can do I/O", not "we can do
I/O in a functional way".

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python-based monads essay part 2

2016-10-13 Thread Jussi Piitulainen
Gregory Ewing writes:

> A bit more on SMFs, and then some I/O.
>
> http://www.cosc.canterbury.ac.nz/greg.ewing/essays/monads/DemystifyingMonads2.html

Thanks.

It would be good to spell out SMF at the start of the page.

"The definition of / above" (__truediv__ method) was not given "above"
(in the definition of SMF).
-- 
https://mail.python.org/mailman/listinfo/python-list