Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti
L
> On 10 Feb 2024, at 13:30, Peter Bex  wrote:
> 
> On Sat, Feb 10, 2024 at 12:24:36PM +, Pietro Cerutti wrote:
>> This is not how I reason about referential transparency. It is a property of 
>> functions applied to values, not variables.
>> The fact that you can define x to different values or even rebind it in the 
>> scope of a let binding doesn't make (lambda (x) (+ x 1)) less referentially 
>> transparent.
>> If you change the value of a slot of a vector, then the vector has a 
>> different value than before, even if the same name binds to the value.
> 
> This might be true in an abstract sense, but it isn't true for the
> CHICKEN type system.  The same vector (e.g. eq? returns #t) is always
> the same "value", for purposes of the type system.
> 
> It can have different values inside its slots, which is why we have
> to differentiate between pure and clean functions (see my other mail).

Oh, I've missed it! I thought it was just some mismatch on the naming in 
scrutinizer, I had not noticed the different semantics. Thanks!

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread Al

On 2024-02-10 14:28, Peter Bex wrote:


CHICKEN's type system differentiates between "pure" and "clean". A "pure"
Hope this clears things up a bit!



Ah, that's what I was looking for. So I shouldn't declare procedures 
using vector-ref as pure, but as clean. Now how do I declare a scheme 
procedure as "clean" -- I don't see that in the manual? I'll check out 
the source, though grep'ing for vector-ref is obviously not an option.



-- Al




Re: (declare (pure ...))

2024-02-10 Thread Peter Bex
On Sat, Feb 10, 2024 at 12:24:36PM +, Pietro Cerutti wrote:
> This is not how I reason about referential transparency. It is a property of 
> functions applied to values, not variables.
> The fact that you can define x to different values or even rebind it in the 
> scope of a let binding doesn't make (lambda (x) (+ x 1)) less referentially 
> transparent.
> If you change the value of a slot of a vector, then the vector has a 
> different value than before, even if the same name binds to the value.

This might be true in an abstract sense, but it isn't true for the
CHICKEN type system.  The same vector (e.g. eq? returns #t) is always
the same "value", for purposes of the type system.

It can have different values inside its slots, which is why we have
to differentiate between pure and clean functions (see my other mail).

Cheers,
Peter


signature.asc
Description: PGP signature


Re: (declare (pure ...))

2024-02-10 Thread Peter Bex
On Sat, Feb 10, 2024 at 11:12:04AM +, siiky via wrote:
> Hi Al,
> 
> > On a practical level, I would be sad if vector-ref, for example, was
> > "impure", and thus compiling a vector-ref invalidated all previously-
> > checked globals for the current scope. Likewise, I would prefer to
> > declare a procedure using vector-ref as pure, to let csc know that it
> > does not modify globals (or the filesystem etc).
> 
> The function vector-ref doesn't stop being pure (i.e. referentially
> transparent) depending how you use it. The function is always referentially
> transparent -- the expression (vector-ref some-global-vec) isn't. Quoting
> from "Functional Programming in C++" by Ivan Čukić:

CHICKEN's type system differentiates between "pure" and "clean".  A "pure"
function has no side effects and will always return the same value given
the same input (excluding the environment).

A "clean" function does not modify any state, but may return different
values at different times depending on the value.

That's why vector-ref is marked as "clean" and not "pure" in types.db.
A procedure like "not" and all type predicates are marked as "pure".

Hope this clears things up a bit!

Cheers,
Peter


signature.asc
Description: PGP signature


Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 10 Feb 2024, at 11:10, Al  wrote:
> 
> On 2024-02-10 11:53, Pietro Cerutti wrote:
> 
>> I don't see why vector-ref would be any less pure than, say, a let binding. 
>> Or do you mean vector-set! ?
>> 
> vector-ref, applied to a global, could return different values even when 
> called with the same arguments. Between calls, some other code could modify 
> the contents of the vector. So according to referential transparency, 
> vector-ref could not be pure. Likewise a function that calls vector-ref could 
> not be pure.
> 
> Further, according to referential transparency no procedure that *reads* a 
> global (not just vectors -- even imediate values like numbers, booleans) can 
> be pure. It might return different values if impure code modifies the global 
> between calls to the procedure in question.

This is not how I reason about referential transparency. It is a property of 
functions applied to values, not variables.
The fact that you can define x to different values or even rebind it in the 
scope of a let binding doesn't make (lambda (x) (+ x 1)) less referentially 
transparent.
If you change the value of a slot of a vector, then the vector has a different 
value than before, even if the same name binds to the value.

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 10 Feb 2024, at 11:10, Al  wrote:
> 
> On 2024-02-10 11:53, Pietro Cerutti wrote:
> 
>> I don't see why vector-ref would be any less pure than, say, a let binding. 
>> Or do you mean vector-set! ?
>> 
> vector-ref, applied to a global, could return different values even when 
> called with the same arguments. Between calls, some other code could modify 
> the contents of the vector. So according to referential transparency, 
> vector-ref could not be pure. Likewise a function that calls vector-ref could 
> not be pure.
> 
> Further, according to referential transparency no procedure that *reads* a 
> global (not just vectors -- even imediate values like numbers, booleans) can 
> be pure. It might return different values if impure code modifies the global 
> between calls to the procedure in question.

This is not how I reason about referential transparency. It is a property of 
functions applied to values, not variables.
The fact that you can define x to different values or even rebind it in the 
scope of a let binding doesn't make (lambda (x) (+ x 1)) less referentially 
transparent.
If you change the value of a slot of a vector, then the vector has a different 
value than before, even if the same name binds to the value.

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread siiky via

Hi Al,

On a practical level, I would be sad if vector-ref, for example, was 
"impure", and thus compiling a vector-ref invalidated all previously- 
checked globals for the current scope. Likewise, I would prefer to 
declare a procedure using vector-ref as pure, to let csc know that it 
does not modify globals (or the filesystem etc).


The function vector-ref doesn't stop being pure (i.e. referentially 
transparent) depending how you use it. The function is always 
referentially transparent -- the expression (vector-ref some-global-vec) 
isn't. Quoting from "Functional Programming in C++" by Ivan Čukić:


> We’re going to define purity more precisely now, through a concept 
called referential transparency. Referential transparency is a 
characteristic of expressions, not just functions. Let’s say an 
expression is anything that specifies a computation and returns a 
result. We’ll say an expression is referentially transparent if the 
program wouldn’t behave any differently if we replaced the entire 
expression with just its return value. If an expression is referentially 
transparent, it has no observable side effects, and therefore all 
functions used in that expression are pure.


(p.104)

I think that part just decides whether the procedure is "pure", for 
whatever purity means. That doesn't say anything about what compiling a 
call to such a "pure" procedure means (can memoize the value or not, 
need to re-check globals or closures afterwards or not etc).


Both the Declarations and Types manual pages say clearly it means 
"referencial transparency."


And RE local state, that you asked in another email: my understanding is 
that that refers to variables belonging to the closure, that survive 
across calls. E.g.:


(define foo
  (let ((x 0))
(lambda (y)
  (set! x (add1 x))
  (* 2 y

siiky





Re: (declare (pure ...))

2024-02-10 Thread Al

On 2024-02-10 11:53, Pietro Cerutti wrote:

I don't see why vector-ref would be any less pure than, say, a let 
binding. Or do you mean vector-set! ?


vector-ref, applied to a global, could return different values even when 
called with the same arguments. Between calls, some other code could 
modify the contents of the vector. So according to referential 
transparency, vector-ref could not be pure. Likewise a function that 
calls vector-ref could not be pure.



Further, according to referential transparency no procedure that *reads* 
a global (not just vectors -- even imediate values like numbers, 
booleans) can be pure. It might return different values if impure code 
modifies the global between calls to the procedure in question.



In contrast, a let binding can only be modified by the code in the 
current scope.



There should still be a way to communicate to the optimizer that 
vector-ref, or some procedure that uses vector-ref on a global 
identifier (but does not call set! / vector-set! on globals) does not, 
uh, modify any globals.



-- Al




Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 10 Feb 2024, at 10:45, Al  wrote:
> 
> On 2024-02-10 11:20, Pietro Cerutti wrote:
> 
>> Both Haskell and CHICKEN ultimately compile to obiect code. That is not 
>> important: the important thing is the abstract machine you're programming 
>> against. This is why I specified "observable" in my previous reply.
> 
> I agree. And if we step out of the monadic framework for just a bit, you'll 
> see that there's room for an abstract machine in which purity just means 
> "does not set! globals". This would still let the optimizer know that globals 
> do not need to be re-checked after such a procedure is invoked, for example. 
> I'm not sure if csc really uses the full "referentially transparent" 
> definition (i.e. memoize-able), or just the "does not modify globals" one.
> 
> 
> On a practical level, I would be sad if vector-ref, for example, was 
> "impure", and thus compiling a vector-ref invalidated all previously-checked 
> globals for the current scope. Likewise, I would prefer to declare a 
> procedure using vector-ref as pure, to let csc know that it does not modify 
> globals (or the filesystem etc).

I don't see why vector-ref would be any less pure than, say, a let binding. Or 
do you mean vector-set! ?

If the latter, well.. wouldn't your reasoning prevent you from using 
vector-set! on a global variable?

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 9 Feb 2024, at 21:06, Al  wrote:
> Hi,
> 
> 
> what does (declare (pure ..)) mean to csc? Is the function supposed to be
> 
> * only side-effect free, or ...
> 
> * also return the same value when called with the same arguments?


Back to your original question, to make sure I understand: can you show an 
example of a function which respects only one of those two points?

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread Al

On 2024-02-10 11:20, Pietro Cerutti wrote:

Both Haskell and CHICKEN ultimately compile to obiect code. That is 
not important: the important thing is the abstract machine you're 
programming against. This is why I specified "observable" in my 
previous reply.


I agree. And if we step out of the monadic framework for just a bit, 
you'll see that there's room for an abstract machine in which purity 
just means "does not set! globals". This would still let the optimizer 
know that globals do not need to be re-checked after such a procedure is 
invoked, for example. I'm not sure if csc really uses the full 
"referentially transparent" definition (i.e. memoize-able), or just the 
"does not modify globals" one.



On a practical level, I would be sad if vector-ref, for example, was 
"impure", and thus compiling a vector-ref invalidated all 
previously-checked globals for the current scope. Likewise, I would 
prefer to declare a procedure using vector-ref as pure, to let csc know 
that it does not modify globals (or the filesystem etc).





Yeah their wording is different but the meaning is the same, i.e., a 
--> function will be marked as pure (for the implementation, see the 
last value returned by validate-type in scrutinizer.scm).


I think that part just decides whether the procedure is "pure", for 
whatever purity means. That doesn't say anything about what compiling a 
call to such a "pure" procedure means (can memoize the value or not, 
need to re-check globals or closures afterwards or not etc).



-- Al




Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 10 Feb 2024, at 09:27, Al  wrote:
> 
> On 2024-02-10 10:13, Pietro Cerutti wrote:
> 
>> I don't get your question: those two things are the same thing :)
>> 
>> Referential transparency means you can substitute an expression with its 
>> expansion down to a value. If anything happening in between causes 
>> (observable *) changes, you can't do it anymore.
>> 
>> (*) modifying the program counter is not an observable change, for example.
>> 
> Those two things are the same thing in Haskell and languages that have a 
> mathematical model of the program, yes. Scheme is... not that, much of the 
> time. Chicken is implemented on top of C, so it's even less clear.

Both Haskell and CHICKEN ultimately compile to obiect code. That is not 
important: the important thing is the abstract machine you're programming 
against. This is why I specified "observable" in my previous reply.

> But let's not get theoretical, yes, my question is if csc can (does?) 
> "memoize" the results of "pure" function.
> 
> 
> In any case, the definitions I quoted above
> 
> * https://wiki.call-cc.org/man/5/Declarations#pure
> 
> * https://wiki.call-cc.org/man/5/Types#purity
> 
> are not the same thing, so either the one or the other should stand. Though 
> I'm not sure what "local variables or data contained in local variables" 
> actually means. I think it means you can't "set!" globals.

Yeah their wording is different but the meaning is the same, i.e., a --> 
function will be marked as pure (for the implementation, see the last value 
returned by validate-type in scrutinizer.scm).

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-10 Thread Al

On 2024-02-10 10:13, Pietro Cerutti wrote:


I don't get your question: those two things are the same thing :)

Referential transparency means you can substitute an expression with 
its expansion down to a value. If anything happening in between causes 
(observable *) changes, you can't do it anymore.


(*) modifying the program counter is not an observable change, for 
example.


Those two things are the same thing in Haskell and languages that have a 
mathematical model of the program, yes. Scheme is... not that, much of 
the time. Chicken is implemented on top of C, so it's even less clear.



But let's not get theoretical, yes, my question is if csc can (does?) 
"memoize" the results of "pure" function.



In any case, the definitions I quoted above

* https://wiki.call-cc.org/man/5/Declarations#pure

* https://wiki.call-cc.org/man/5/Types#purity

are not the same thing, so either the one or the other should stand. 
Though I'm not sure what "local variables or data contained in local 
variables" actually means. I think it means you can't "set!" globals.



-- Al




Re: (declare (pure ...))

2024-02-10 Thread Pietro Cerutti

> On 10 Feb 2024, at 09:06, Al  wrote:
> 
> 
>>> * only side-effect free, or ...
>>> 
>>> * also return the same value when called with the same arguments?
>> 
>> The first implies the second: to be able to choose from a set of return 
>> values for the same given argument, you do need to have side-effects, e.g., 
>> interact with a RNG which maintains state, read from a file, maintain an 
>> index into a circular vector of results, etc..
>> 
>> Here's what the docs say:
>> http://wiki.call-cc.org/man/5/Declarations#pure
>> 
> "referentially transparent, that is, as not having any side effects". You can 
> read "the environment" (including all globals) without actually modifying the 
> environment. Between two applications of the procedure, other impure 
> procedures may modify the environment. So it's not clear if it means Haskell 
> monad purity or immutable purity, hence my question.

I don't get your question: those two things are the same thing :)

Referential transparency means you can substitute an expression with its 
expansion down to a value. If anything happening in between causes (observable 
*) changes, you can't do it anymore.

(*) modifying the program counter is not an observable change, for example.


-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.


Re: (declare (pure ...))

2024-02-10 Thread Al




* only side-effect free, or ...

* also return the same value when called with the same arguments?


The first implies the second: to be able to choose from a set of 
return values for the same given argument, you do need to have 
side-effects, e.g., interact with a RNG which maintains state, read 
from a file, maintain an index into a circular vector of results, etc..


Here's what the docs say:
http://wiki.call-cc.org/man/5/Declarations#pure

"referentially transparent, that is, as not having any side effects". 
You can read "the environment" (including all globals) without actually 
modifying the environment. Between two applications of the procedure, 
other impure procedures may modify the environment. So it's not clear if 
it means Haskell monad purity or immutable purity, hence my question.



Furthermore, https://wiki.call-cc.org/man/5/Types#purity says: "Using 
the (... --> ...) syntax, you can declare a procedure to not modify 
local state, i.e. not causing any side-effects on local variables or 
data contained in local variables".



-- Al




Re: (declare (pure ...))

2024-02-09 Thread Pietro Cerutti

> On 9 Feb 2024, at 21:06, Al  wrote:
> 
> Hi,
> 
> 
> what does (declare (pure ..)) mean to csc? Is the function supposed to be
> 
> * only side-effect free, or ...
> 
> * also return the same value when called with the same arguments?

The first implies the second: to be able to choose from a set of return values 
for the same given argument, you do need to have side-effects, e.g., interact 
with a RNG which maintains state, read from a file, maintain an index into a 
circular vector of results, etc..

Here's what the docs say:
http://wiki.call-cc.org/man/5/Declarations#pure

-- 
Pietro Cerutti
I've pledged to give 10% of income to effective charities and invite you to 
join me.
https://givingwhatwecan.org

Sent from a small device - please excuse brevity and typos.

Re: (declare (pure ...))

2024-02-09 Thread siiky via

Hi Al,


what does (declare (pure ..)) mean to csc? Is the function supposed to be

* only side-effect free, or ...

* also return the same value when called with the same arguments?


I can't remember where `(declare)` is documented, but I believe this [0] 
is also relevant.


[0] https://wiki.call-cc.org/man/5/Types#purity

Good luck!
siiky





(declare (pure ...))

2024-02-09 Thread Al

Hi,


what does (declare (pure ..)) mean to csc? Is the function supposed to be

* only side-effect free, or ...

* also return the same value when called with the same arguments?


Thanks,

Al