The trick is to create a type class for which you will define an instance for functions, and an instance for the return type. The fact that he is a Bool not a problem at all.
We are trying to write a function that takes a variational argument and returns Bool , so we will define a type class with such a function.
class Stmt a where tautology :: a -> Bool
Next, we define an instance for the return type of the variational function. In this case, Bool .
-- A Bool is a tautology if it True. instance Stmt Bool where tautology = id
The key part is the following instance for functions that take a Bool argument, and the type of the return type is some type from our class. Thus, this instance will be applied several times if the function takes several arguments.
-- A function is a tautology if it always returns a tautology. instance Stmt b => Stmt (Bool -> b) where tautology f = tautology (f True) && tautology (f False)
Writing this method requires FlexibleInstances due to the Bool in the second instance. To do the same with pure Haskell 98, we will need to use a type variable with a limited constraint. For example, we can use Bounded and Enum (there are instances for Bool ), or you can create your own class that allows you to build the corresponding inputs.
instance (Enum a, Bounded a, Stmt b) => Stmt (a -> b) where tautology f = all (tautology . f) [minBound .. maxBound]
And you're done. Try:
> tautology $ \xy -> (not x && not y) == not (x && y) False > tautology $ \xy -> (not x && not y) == not (x || y) True
hammar Dec 02 2018-11-11T00: 00Z
source share