Haskell from SML - referrential Transparency?!

I am pretty new to Haskell, so need some clarification. I am porting some code from SML, and getting a result that surprises me. I basically have some functions which work like this: f1 = fa fb fc test1 = do print "test1:" f1 But I ran a few tests, and got odd results - so I ran the same test function twice, and got different results - that was my surprise. I did this: f1 = fa fb fc f2 = fa fb fc test2 = do print "test1:" f1 f2 and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results. Seems to me that by referential transparency, I should always get the same result from the function(s). So, I added some Debug.trace to the argument functions which are used, and I get a trace from the first call(s), but none from the second one(s), although I do get the result from each. It is as if because of the laziness, it someone cached some of the intermediate results, so did not re-invoke the functions. Anyway, totally confused. I must be missing something significant here. Thanks for any clarification! (The original code is a bit long, so I did not include here...)

On Tuesday 19 April 2011 21:10:09, Gregory Guthrie wrote:
I am pretty new to Haskell, so need some clarification. I am porting some code from SML, and getting a result that surprises me.
I basically have some functions which work like this: f1 = fa fb fc test1 = do print "test1:" f1
So f1 :: IO something Being an IO-action, f1 can return different things in different invocations since the world in which it runs has changed (it might read a file which was modified between the first and the second invocation, for example).
But I ran a few tests, and got odd results - so I ran the same test function twice, and got different results - that was my surprise. I did this: f1 = fa fb fc f2 = fa fb fc test2 = do print "test1:" f1 f2
and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results.
Depending on what f1 does, that may be perfectly normal or a serious bug. We'd need to see more of the code to determine which.
Seems to me that by referential transparency, I should always get the same result from the function(s).
So, I added some Debug.trace to the argument functions which are used, and I get a trace from the first call(s), but none from the second one(s), although I do get the result from each.
Did you do it in the form fa = trace ("fa") realFa ? Then the trace is only evaluated the first time fa is evaluated, even if fa is called later again.
It is as if because of the laziness, it someone cached some of the intermediate results, so did not re-invoke the functions.
Anyway, totally confused. I must be missing something significant here. Thanks for any clarification! (The original code is a bit long, so I did not include here...)
http://hpaste.org/ perhaps?

Oops; No - my (further) mistake. It is not IO() - f1 returns a data structure which implements Show, and the test is really: Test1 = do print "Test1:" print f1 (etc..) Thanks for the alert on trace. I used it like this: allocate :: Store -> (Store, Location) allocate ( Store(bot,top,sto) ) | trace("allocate "++ show bot ++ show top) False = undefined allocate ( Store(bot,top,sto) ) = let newtop = top+1 and it does seem to show every allocation on the first run of f1, but then nothing on the second. SO it is not just a first call to allocate, but all calls under an invocation of f1 that don't show. Makes me wonder if f1 is even being re-evaluated. I did post the code - but don't expect anyone to really wade through and debug for me! :-) (The issues that I am asking about are a9b, a9bb at line 435, 438) http://hpaste.org/45851/haskell_from_sml_question thanks for the help. -------------------------------------------
-----Original Message----- From: Daniel Fischer [mailto:daniel.is.fischer@googlemail.com] Sent: Tuesday, April 19, 2011 2:16 PM To: haskell-cafe@haskell.org Cc: Gregory Guthrie Subject: Re: [Haskell-cafe] Haskell from SML - referrential Transparency?!
On Tuesday 19 April 2011 21:10:09, Gregory Guthrie wrote:
I am pretty new to Haskell, so need some clarification. I am porting some code from SML, and getting a result that surprises me.
I basically have some functions which work like this: f1 = fa fb fc test1 = do print "test1:" f1
So f1 :: IO something
Being an IO-action, f1 can return different things in different invocations since the world in which it runs has changed (it might read a file which was modified between the first and the second invocation, for example).
But I ran a few tests, and got odd results - so I ran the same test function twice, and got different results - that was my surprise. I did this: f1 = fa fb fc f2 = fa fb fc test2 = do print "test1:" f1 f2
and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results.
Depending on what f1 does, that may be perfectly normal or a serious bug. We'd need to see more of the code to determine which.
Seems to me that by referential transparency, I should always get the same result from the function(s).
So, I added some Debug.trace to the argument functions which are used, and I get a trace from the first call(s), but none from the second one(s), although I do get the result from each.
Did you do it in the form
fa = trace ("fa") realFa
?
Then the trace is only evaluated the first time fa is evaluated, even if fa is called later again.
It is as if because of the laziness, it someone cached some of the intermediate results, so did not re-invoke the functions.
Anyway, totally confused. I must be missing something significant here. Thanks for any clarification! (The original code is a bit long, so I did not include here...)
perhaps?

On Tuesday 19 April 2011 21:38:18, Gregory Guthrie wrote:
I did post the code - but don't expect anyone to really wade through and debug for me! :-)
Oh, wow. Haskell looking like Lisp :(
(The issues that I am asking about are a9b, a9bb at line 435, 438) http://hpaste.org/45851/haskell_from_sml_question
Can't reproduce, I get the same result from all invocations. But see below
thanks for the help.
<moved from above>
and it does seem to show every allocation on the first run of f1, but then nothing on the second. SO it is not just a first call to allocate, but all calls under an invocation of f1 that don't show. Makes me wonder if f1 is even being re-evaluated.
Interpreted or compiled without optimisations, the first invocation of a9b produces debug-output, the second not, both yield the same result, Output (IntValue 11,Halt). The invocation of a9bb produces the same, with the debug output, like the first invocation of a9b. Since a9b is a simple monomorphic value, it is evaluated only once, hence further uses refer simply to the result, not the computation made to achieve it. If you compile with optimisations, a9bb doesn't produce debug output *because it doesn't exist*. The compiler sees that it's the same as a9b and unites them, all references to a9bb are rewritten to references to a9b (that's what referential transparency gives you :). Hence, when that value is asked for, it is already evaluated, no computation needed, no debug- output.

Thanks. It was the "no computation needed" difference that I was missing, and was including (falsely) in my expectations for "same result", i.e. including the same traces. ------------------------------------------- Dr. Gregory Guthrie Computer Science Department School of Computer Science & Mathematics Maharishi University of Management -------------------------------------------
-----Original Message----- From: Daniel Fischer [mailto:daniel.is.fischer@googlemail.com] Sent: Tuesday, April 19, 2011 3:28 PM To: Gregory Guthrie Cc: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] Haskell from SML - referrential Transparency?!
On Tuesday 19 April 2011 21:38:18, Gregory Guthrie wrote:
I did post the code - but don't expect anyone to really wade through and debug for me! :-)
Oh, wow. Haskell looking like Lisp :(
(The issues that I am asking about are a9b, a9bb at line 435, 438) http://hpaste.org/45851/haskell_from_sml_question
Can't reproduce, I get the same result from all invocations. But see below
thanks for the help.
<moved from above>
and it does seem to show every allocation on the first run of f1, but then nothing on the second. SO it is not just a first call to allocate, but all calls under an invocation of f1 that don't show. Makes me wonder if f1 is even being re-evaluated.
Interpreted or compiled without optimisations, the first invocation of a9b produces debug- output, the second not, both yield the same result, Output (IntValue 11,Halt). The invocation of a9bb produces the same, with the debug output, like the first invocation of a9b. Since a9b is a simple monomorphic value, it is evaluated only once, hence further uses refer simply to the result, not the computation made to achieve it. If you compile with optimisations, a9bb doesn't produce debug output *because it doesn't exist*. The compiler sees that it's the same as a9b and unites them, all references to a9bb are rewritten to references to a9b (that's what referential transparency gives you :). Hence, when that value is asked for, it is already evaluated, no computation needed, no debug- output.

Thanks.
It was the "no computation needed" difference that I was missing, and was including (falsely) in my expectations for "same result", i.e. including the same traces.
To be clear, Debug.trace is not referentially transparent. It's only supposed to be used for debugging. Brandon

I've been working on Haskell for quite a while and it's not too often that a
beginner shows me a new trick--this trick with trace seems really cool and I
hadn't seen it before.
f x | trace ("f " ++ show x) False = undefined
f ... -- rest of regular definition
Makes it really easy to add the trace and then later comment it out. One
problem I always have with traces the usual way is that adding/removing them
is kind of annoying--it can change the indentation of your code, etc. So
this trick is really useful!
Thanks Gregory!
-- ryan
On Tue, Apr 19, 2011 at 12:38 PM, Gregory Guthrie
Oops; No - my (further) mistake. It is not IO() - f1 returns a data structure which implements Show, and the test is really:
Test1 = do print "Test1:" print f1 (etc..)
Thanks for the alert on trace.
I used it like this: allocate :: Store -> (Store, Location) allocate ( Store(bot,top,sto) ) | trace("allocate "++ show bot ++ show top) False = undefined allocate ( Store(bot,top,sto) ) = let newtop = top+1
and it does seem to show every allocation on the first run of f1, but then nothing on the second. SO it is not just a first call to allocate, but all calls under an invocation of f1 that don't show. Makes me wonder if f1 is even being re-evaluated.
I did post the code - but don't expect anyone to really wade through and debug for me! :-) (The issues that I am asking about are a9b, a9bb at line 435, 438) http://hpaste.org/45851/haskell_from_sml_question
thanks for the help. -------------------------------------------
-----Original Message----- From: Daniel Fischer [mailto:daniel.is.fischer@googlemail.com] Sent: Tuesday, April 19, 2011 2:16 PM To: haskell-cafe@haskell.org Cc: Gregory Guthrie Subject: Re: [Haskell-cafe] Haskell from SML - referrential Transparency?!
On Tuesday 19 April 2011 21:10:09, Gregory Guthrie wrote:
I am pretty new to Haskell, so need some clarification. I am porting some code from SML, and getting a result that surprises me.
I basically have some functions which work like this: f1 = fa fb fc test1 = do print "test1:" f1
So f1 :: IO something
Being an IO-action, f1 can return different things in different invocations since the world in which it runs has changed (it might read a file which was modified between the first and the second invocation, for example).
But I ran a few tests, and got odd results - so I ran the same test function twice, and got different results - that was my surprise. I did this: f1 = fa fb fc f2 = fa fb fc test2 = do print "test1:" f1 f2
and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and
in the
same original order as f2;f1). Even doing (f1;f1) gives two different results.
Depending on what f1 does, that may be perfectly normal or a serious bug. We'd need to see more of the code to determine which.
Seems to me that by referential transparency, I should always get the same result from the function(s).
So, I added some Debug.trace to the argument functions which are used, and I get a trace from the first call(s), but none from the second one(s), although I do get the result from each.
Did you do it in the form
fa = trace ("fa") realFa
?
Then the trace is only evaluated the first time fa is evaluated, even if fa is called later again.
It is as if because of the laziness, it someone cached some of the intermediate results, so did not re-invoke the functions.
Anyway, totally confused. I must be missing something significant here. Thanks for any clarification! (The original code is a bit long, so I did not include here...)
perhaps?
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 26 April 2011 00:53, Ryan Ingram
I've been working on Haskell for quite a while and it's not too often that a beginner shows me a new trick--this trick with trace seems really cool and I hadn't seen it before.
f x | trace ("f " ++ show x) False = undefined f ... -- rest of regular definition
Makes it really easy to add the trace and then later comment it out. One problem I always have with traces the usual way is that adding/removing them is kind of annoying--it can change the indentation of your code, etc. So this trick is really useful!
Yes. It would be much better if trace were a primitive, whose semantics were to ignore the first argument and yield the second argument as a value. But these semantics would be changed by supplying an argument to the runtime (so you could turn tracing on-and-off between runs). Actually, this still isn't very convenient. Better would be to have another String argument - the trace key. Then the runtime argument would list those trace keys for which tracing should be performed. This way you never have to edit the code to add and remove tracing (though you may want to remove many trace statements when you are convinced the code is thoroughly debugged). -- Colin Adams Preston, Lancashire, ENGLAND () ascii ribbon campaign - against html e-mail /\ www.asciiribbon.org - against proprietary attachments

On Tue, Apr 19, 2011 at 4:10 PM, Gregory Guthrie
and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results.
This shows that referential transparency is working nicely. -- Felipe.

Perhaps the description was unclear; F1;f1 gives result r1;r2 (not the same) F1;f2 gives r1;r2 F2,f1 gives r1;r2 -------------------------------------------
-----Original Message----- From: Felipe Almeida Lessa [mailto:felipe.lessa@gmail.com] Sent: Tuesday, April 19, 2011 2:26 PM To: Gregory Guthrie Cc: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] Haskell from SML - referrential Transparency?!
On Tue, Apr 19, 2011 at 4:10 PM, Gregory Guthrie
wrote: and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results.
This shows that referential transparency is working nicely.
-- Felipe.

On Tue, Apr 19, 2011 at 1:41 PM, Gregory Guthrie
Perhaps the description was unclear;
F1;f1 gives result r1;r2 (not the same) F1;f2 gives r1;r2 F2,f1 gives r1;r2
No, you were clear, and Felipe's answer still makes sense. Since f1 and f2 have the same definition, substituting one for the other should not change anything. Maybe do notation is what is confusing you. Try getting rid of the do notation and writing everything in terms of the more basic (>>) and (>>=) combinators. If (>>) could be any operator, should we expect that f1 = f1
f1?
Luke
-------------------------------------------
-----Original Message----- From: Felipe Almeida Lessa [mailto:felipe.lessa@gmail.com] Sent: Tuesday, April 19, 2011 2:26 PM To: Gregory Guthrie Cc: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] Haskell from SML - referrential Transparency?!
On Tue, Apr 19, 2011 at 4:10 PM, Gregory Guthrie
wrote: and I get different results from the two executions (f1,f2), even though they have exactly the same definition. Reversing their order, gives the exact same results (i.e. the results are still different, and in the same original order as f2;f1). Even doing (f1;f1) gives two different results.
This shows that referential transparency is working nicely.
-- Felipe.
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

You can think of IO actions as values (which don't change) representing imperative programs to execute (which may have different results each time you execute them). So, `fa fb fc` represents the exact same value as `fa fb fc`, but if you execute that value multiple times you may get different results. Here are a couple safe ways to execute IO actions: * From your `main` function * In GHCi (if the value you give it is an action, it *executes* it, otherwise it *prints* it) - Jake
participants (8)
-
Brandon Moore
-
Colin Adams
-
Daniel Fischer
-
Felipe Almeida Lessa
-
Gregory Guthrie
-
Jake McArthur
-
Luke Palmer
-
Ryan Ingram