[Haskell-cafe] "show" for functional types

Fritz Ruehr fruehr at willamette.edu
Mon Apr 3 06:41:37 EDT 2006


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
>
> ...



More information about the Haskell-Cafe mailing list