简体   繁体   中英

A Haskell List Involving Recursion and High Order Functions

Now I have this code that takes a list and does something to the first element and then to every other one. And it returns a list of the transformed elements. The problem I am having is that I want to have a list that contains the untransformed and the transformed elements. This is what I have so far:

applyToEveryOther :: (a -> b) -> [a] -> [b]
applyToEveryOther _ [] = []
applyToEveryOther f [x] = [f x]
applyToEveryOther f (x:y:xs) = f x : y : applyToEveryOther f xs

The error it is giving me says there is a problem with the : y part of the function

When I try your code, I get the following (admittedly somewhat lengthy and confusing) error message:

EveryOther.hs:4:42: error:
    • Couldn't match type ‘b’ with ‘a’
      ‘b’ is a rigid type variable bound by
        the type signature for:
          applyToEveryOther :: forall a b. (a -> b) -> [a] -> [b]
        at EveryOther.hs:1:22
      ‘a’ is a rigid type variable bound by
        the type signature for:
          applyToEveryOther :: forall a b. (a -> b) -> [a] -> [b]
        at EveryOther.hs:1:22
      Expected type: [a]
        Actual type: [b]
    • In the second argument of ‘(:)’, namely ‘applyToEveryOther f xs’
      In the second argument of ‘(:)’, namely
        ‘y : applyToEveryOther f xs’
      In the expression: f x : y : applyToEveryOther f xs
    • Relevant bindings include
        xs :: [a] (bound at EveryOther.hs:4:26)
        y :: a (bound at EveryOther.hs:4:24)
        x :: a (bound at EveryOther.hs:4:22)
        f :: a -> b (bound at EveryOther.hs:4:19)
        applyToEveryOther :: (a -> b) -> [a] -> [b]
           (bound at EveryOther.hs:2:1)

However, it's worth trying to figure out what GHC is saying here. As per the second bullet point, GHC was processing the subexpression y : applyToEveryOther f xs , and specifically looking at the second argument to the : operator in that expression (namely applyToEveryOther f xs . It expected that expression to have type [a] , but the actual type of the expression was type [b] .

Here, a and b are both "rigid" types, meaning simply that they were specified explicitly by the programmer. GHC also notes, in the relevant bindings, that y had type a .

So, to sum up, you asked GHC to evaluate the expression:

y : applyToEveryOther f xs

where you already specified that y had type a and applyToEveryOther f xs had type [b] , and GHC refused to do this because lists in Haskell can't mix two different types.

And that's kind of the key to the whole problem. You want to transform some of the elements of your [a] list from a to b , but then you want to return a mixed list of a s and b s. Haskell can't do that!

The only way your function can work is if you change the signature so that a and b are the same type:

applyToEveryOther :: (a -> a) -> [a] -> [a]

and your code will work fine.

Another way you could "discover" the correct signature is to leave it out and have Haskell infer the most general possible signature. If you load the code (without the explicit signature) into GHCi and ask for the type, you get:

> :t applyToEveryOther
applyToEveryOther :: (a -> a) -> [a] -> [a]
>

which is the most general possible type for this function.

If I understood correctly you wanted both, the original and transformed values.

However evaluating applyToEveryOther (+3) [0,1,2] returns [3,1,5]. If you want [0,3,1,4,2,5] as a result, try

applyToEveryOther _ [] = []
applyToEveryOther f [x] = [x,f x]
applyToEveryOther f (x:xs) = x: f x : applyToEveryOther f xs

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