Truth Tables from Anonymous Functions in Haskell

*爱你&永不变心* 提交于 2019-12-04 05:11:05

The problem here is that you're trying to call a function recursively with a different type for the recursive step. Consider the definition:

argsFromList f []     = f
argsFromList f (x:xs) = argsFromList (f x) xs

Let's try to infer the type ourselves. We can immediately see that the first argument f should be a function of at least one argument, the second argument (x:xs) is a list, and the list elements should be the same type as the first argument of f. In the first case the argument f is returned, so the final return type must be the same as the first argument. So we start with this:

argsFromList :: (a -> ?) -> [a] -> (a -> ?)

To find the unknown type ?, we can look at the second case, which consists of a recursive call. The argument xs is the same list type, and the argument (f x) has type ?. Since it's being used as the first argument in the recursive call, which has type (a -> ?), we can now conclude that ? is the same type as (a -> ?) which is therefore the same type as (a -> (a -> ?)) which is therefore the same type as (a -> (a -> (a -> ?))) which is... oops.

That would be the "infinite type", of course.

If you want to do this with functions that use a variable number of arguments of a single type, you'll probably want to use functions that take a list of values rather than individual arguments. Otherwise, you'll have to either write each version individually or use some arcane tricks involving advanced language features, neither of which is appealing in a simple case like this.

If you want to build a truth table for boolean functions with an arbitrary number of arguments, you're creating a function that must work for multiple types, so you'll have to use type classes:

{-# LANGUAGE FlexibleInstances #-}

class TruthTable a where
  truthTable :: a -> [([Bool], Bool)]

instance TruthTable Bool where
  truthTable b = [([], b)]

instance TruthTable a => TruthTable (Bool -> a) where
  truthTable f = [ (True  : inps, out) | (inps, out) <- truthTable (f True)] ++ 
                 [ (False : inps, out) | (inps, out) <- truthTable (f False)]

For example:

*Main> mapM_ print $ truthTable (&&)
([True,True],True)
([True,False],False)
([False,True],False)
([False,False],False)

What you're asking for is not at all trivial. Haskell doesn't make it easy to deal with functions that apply functions with variable numbers of arguments. For example, the zip functions from Data.List come in separate variants for different numbers of arguments (zip, zip3, zip4, ...). Likewise, in Control.Monad there's liftM, liftM2, liftM3, ...

Basically, the most general type you can assign to a function with an unknown number of arguments is a -> b; a one-place truth function is Bool -> Bool (a = Bool, b = Bool), a two-place truth function is Bool -> (Bool -> Bool) (a = Bool, b = Bool -> Bool), three-place is Bool -> (Bool -> (Bool -> Bool)) (a = Bool, b = Bool -> (Bool -> Bool)), and so on. But there is no easy way you can look at the function you've been passed in to know what's the type on the right of the initial arrow.

One type of solution that can be made to work involves using type classes to define separate instances of the truth-table maker function for each argument function type. Sjoerd Visscher's answer in this thread is doing that for all function sizes by using a recursive instance definition (notice the recursive TruthTable a => TruthTable (Bool -> a) declaration). There may be other solutions that could be constructed using the Applicative type class.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!