I've revised my thinking about printing (total, finite) functions: I should have respected the notion that a printed representation can be cut-and-pasted back in at the prompt for evaluation to something equal to the original. I've also revised my implementation to this effect, so that functions (over suitable classes of types) are now instances of the classes Bounded, Enum, Eq, Ord and Show, with the desired printing, as demonstrated by this session transcript:

> not == not
True
> not == id
False
> id == (not . not)
True
> fromEnum not
1
> not == toEnum 1
True
> not
(\x -> case x of False -> True; True -> False)
> not == (\x -> case x of False -> True; True -> False)
True
> id :: Bool -> Bool
(\x -> case x of False -> False; True -> True)
> const True :: Bool -> Bool
(\x -> case x of False -> True; True -> True)
> (&&) == (&&)
True
> (&&) == (||)
False
> fromEnum (&&)
8
> (&&) == toEnum 8
True
> (&&)
(\x -> case x of False -> (\x -> case x of False -> False; True -> False); True -> (\x -> case x of False -> False; True -> True))

The printing is a bit ugly, but it would be somewhat improved in Haskell' if the LambdaCase suggestion is accepted (see <http://hackage.haskell.org/trac/haskell-prime/wiki/LambdaCase>), i.e., without the arbitrary choice of variable, and slightly shorter representations. Printing of properly higher-order functions doesn't have the nice read-back property, but only because Haskell doesn't support patterns over (suitably finite, total) functions. (One can paste back in the printed rep. for (&&), for example, and successfully compare it to the original, but not something of type (Bool->Bool)->Bool.)

I can't imagine I'm the first to play around with this sort of thing, but I wonder why instances like these ought not be included in the Prelude. The functions are treated in a fully extensional manner, so I think it's all quite kosher. The ideas break down for partial functions, of course, but then so do the usual instances for enumerated data types (we can't successfully compare True with undefined, for example). And although the Ord and Enum instances are somewhat arbitrary, they are coherent with each other, generated in a principled fashion and agree with common practices (Ord and Enum for enumerated data types are themselves somewhat arbitrary). I suppose there's an argument from an efficiency perspective, but we don't prevent derivation of Eq for recursive types on the grounds that someone might compare huge trees.

Here are the instances used above (well, the headers anyway), including products for good measure:

instance (Enum a, Bounded a, Enum b, Bounded b) => Bounded (a->b) where ... instance (Enum a, Bounded a, Enum b, Bounded b) => Enum (a -> b) where ...
instance (Enum a, Bounded a, Eq b) => Eq (a->b) where ...
instance (Enum a, Bounded a, Enum b, Bounded b, Eq b) => Ord (a -> b) where ...
instance (Enum a, Bounded a, Show a, Show b) => Show (a->b) where ...
instance (Bounded a, Bounded b) => Bounded (a,b) where ...
instance (Enum a, Bounded a, Enum b, Bounded b) => Enum (a,b) where ...

  --  Fritz


On Apr 1, 2006, at 2:01 AM, Fritz Ruehr wrote:

You can use type classes to implement this for *finite* functions, i.e., total functions over types which are Enum and Bounded (and Show-able), using for example a tabular format for the show:

        > putStr (show (uncurry (&&)))
        (False,False)   False
        (False,True)    False
        (True,False)    False
        (True,True)     True

...

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to