简体   繁体   中英

Inferring function type from function definition Haskell

So I was taking a test about Haskell and one question said:

let the function be

lolo g x = ys
      where ys = [x] ++ filter (curry g x) ys

then determine the type of the function called lolo. The options are:

a) (a,b) -> Bool -> b -> [(a,b)]
b) (b -> b -> b) -> Bool -> [b]
c) ((b,b) -> Bool) -> b -> [b]
d) (a -> b -> Bool) -> b -> [c]

Can somebody please explain which one it is and why? I'm really confused about this one... things I do not understand are:

1) the curry function can only be applied to functions right? not datatypes that may be tuples? then you can infer that g is a function in this context? what if g and x are both functions? is it possible to use curry with nth arguments? I've only seen curry used with 1 argument.

2) the other thing I don't understand very much is the recursion in the definition of ys. so ys is defined by ys, but I don't see the base case in this scenario. Will it ever end? maybe it's the filter function that makes the recursion end.

3) also in curry gx = curry (gx) right? (this is a question about precedence in application of functions)

Thanks a lot

1) The first argument to curry has to be a function, it is what is known as a higher order function , it takes a function and returns a new one. While its type is printed out in GHCi as

curry :: ((a, b) -> c) -> a -> b -> c

It is more clearly represented (IMO) as

curry :: ((a, b) -> c) -> (a -> b -> c)

Which makes it more obvious that it takes a function and returns a new function. Technically, you could say that curry takes 3 arguments, one of type (a, b) -> c , one of type a , and one of type b . It just takes a function that normally accepts a tuple of arguments and converts it into a function that takes 2 arguments.

2) The computation for ys will never end, don't bother trying to call length on it, you'll just run the computation forever. This isn't a problem, though, you can work with infinite lists and non-terminating lists just fine (non-terminating being a list where it takes forever to compute the next element, not just one that has infinite elements). You can still use functions like take and drop on it, though.

3) Does curry gx == curry (gx) ? No! When you see an expression like abcde , all of b , c , d , and e are arguments to a . If you instead saw abc (de) , then e is applied to d , and that result is applied to abc . Consider filter even [1..10] , this is certainly not the same as filter (even [1..10]) , since it wouldn't even compile! ( even :: Integral a => a -> Bool ).


When solving this sort of problem, first look at what functions are used in the expression that you already know the types of:

(++)   :: [a] -> [a] -> [a]
filter :: (b -> Bool) -> [b] -> [b]
curry  :: ((c, d) -> e) -> c -> d -> e

I've used different type variables in each so that there will be less confusion when trying to line up the types. You can get these types by loading up GHCi, then typing

> :type (++)
(++) :: [a] -> [a] -> [a]
> -- Or just use :t
> :t filter
filter :: (a -> Bool) -> [a] -> [a]
> :t curry
curry :: ((a, b) -> c) -> a -> b -> c

As you can see, I've changed filter to use b instead of a , and curry to use c , d , and e . This doesn't change the meaning any more than fx = x + 1 versus fy = y + 1 , it'll just make it easier to talk about.

Now that we've broken down our function into its subcomponents, we can work from the "top" down. By top, I mean the last function that gets called, namely (++) . You can picture this function by a tree like

    (++)
   /    \
[x]     filter
       /      \
    curry     ys
   /     \
  g       x

So we can clearly see that (++) is at the top. Using that, we can infer that [x] has the type [a] , which means that x ~ a (the tilde is the type equality symbol) and consequently ys ~ [a] , since ys = [x] ++ something . Now that we know the type of x , we can start filling out the rest of the expression. Next, we work down to filter (curry gx) ys . Since it is the second argument to (++) , we can infer that this subexpression also has the type [a] . If we look at the type of filter :

filter :: (b -> Bool) -> [b] -> [b]

The final result is a list of type [b] . Since it's being applied to [x] ++ , we can infer that filter (curry gx) ys :: [a] . This means that [b] ~ [a] => b ~ a . For reference, this makes filter 's type

filter :: (a -> Bool) -> [a] -> [a]

This now places a constraint on curry gx , it must fit into filter 's first argument which has the type a -> Bool . Looking at curry 's type again:

curry :: ((c, d) -> e) -> c -> d -> e

This means that e ~ Bool , and d ~ a . If we plug those back in

curry :: ((c, a) -> Bool) -> c -> a -> Bool

Ignoring g for now, we look at the type of x , which we figured out is a . Since x is the second argument to curry , that means that x matches with the argument of type c , implying that c ~ a . Substituting this into what we just computed we get

curry :: ((a, a) -> Bool) -> a -> a -> Bool

With

               curry g x     :: a -> Bool
       filter (curry g x)    :: [a] -> [a]
       filter (curry g x) ys :: [a]
[x] ++ filter (curry g x) ys :: [a]

From this we can directly infer that lolo 's type signature ends with [a] , so

lolo :: ??? -> [a]

I'll leave you to do the remaining few steps to figure out what ??? is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM