[Haskell-cafe] pointer equality

Steffen Schuldenzucker sschuldenzucker at uni-bonn.de
Thu Jul 21 11:23:32 CEST 2011


On 07/21/2011 10:30 AM, Pedro Vasconcelos wrote:
> On Wed, 20 Jul 2011 12:48:48 -0300
> Thiago Negri<evohunz at gmail.com>  wrote:
>
>
>> Is it possible to implement (==) that first check these thunks before
>> evaluating it? (Considering both arguments has pure types).
>>
>>
>> E.g.,
>>
>> Equivalent thunks, evaluates to True, does not need to evaluate its
>> arguments: [1..] == [1..]
>>
>>
>
> Thunks are just expressions and equality of expressions is undecidable
> in any Turing-complete language (like any general-purpose programming
> language). Note that syntactical equality is not sufficient because
> (==) should be referentially transparent.

I think the following code pretty much models what Thiago meant for a 
small subset of haskell that constructs possibly infinite lists. Thunks 
are made explicit as syntax trees. 'Cycle' is the syntactic symbol for a 
function whose definition is given by the respective case in the 
definition of 'evalOne'.
(I chose cycle here instead of the evalFrom example above to because it 
doesn't need an Enum constraint).

The interesting part is the definition of 'smartEq'.

import Data.List (unfoldr)
import Data.Function (on)

-- let's say we have syntactic primitives like this
data ListExp a = Nil | Cons a (ListExp a) | Cycle (ListExp a)
     deriving (Eq, Ord, Read, Show)
-- derives syntactic equality

conss :: [a] -> ListExp a -> ListExp a
conss xs exp = foldr Cons exp xs

fromList :: [a] -> ListExp a
fromList xs = conss xs Nil

-- eval the next element, return an expression defining the tail
-- (if non-empty)
evalOne :: ListExp a -> Maybe (a, ListExp a)
evalOne Nil = Nothing
evalOne (Cons h t) = Just (h, t)
evalOne e@(Cycle exp) = case eval exp of
     [] -> Nothing
     (x:xs) -> Just (x, conss xs e)

eval :: ListExp a -> [a]
eval = unfoldr evalOne

-- semantic equality
evalEq :: (Eq a) => ListExp a -> ListExp a -> Bool
evalEq = (==) `on` eval

-- semantic equality, but check syntactic equality first.
-- In every next recursion step, assume the arguments of the current 
recursion
-- step to be equal. We can do that safely because two lists are equal iff
-- they cannot be proven different.
smartEq :: (Eq a) => ListExp a -> ListExp a -> Bool
smartEq a b = smartEq' a b []

smartEq' :: (Eq a) => ListExp a -> ListExp a -> [(ListExp a, ListExp a)] 
-> Bool
smartEq' a b eqPairs = if a == b || (a, b) `elem` eqPairs
         then True
         else case (evalOne a, evalOne b) of
     (Just _, Nothing)  -> False
     (Nothing, Just _)  -> False
     (Nothing, Nothing) -> True
     (Just (h1, t1), Just (h2, t2)) -> h1 == h2 && smartEq' t1 t2 ((a, 
b):eqPairs)

Examples:

*Main> smartEq (Cycle $ fromList [1]) (Cycle $ fromList [1,1])
True
*Main> smartEq (Cons 1 $ Cycle $ fromList [1]) (Cycle $ fromList [1])
True
*Main> smartEq (Cons 2 $ Cycle $ fromList [1]) (Cycle $ fromList [1])
False

Any examples for hangups of 'smartEq' are greatly appreciated, I 
couldn't produce any so far.

-- Steffen




More information about the Haskell-Cafe mailing list