Haskell function application and currying

后端 未结 3 588
南方客
南方客 2021-01-30 08:22

I am always interested in learning new languages, a fact that keeps me on my toes and makes me (I believe) a better programmer. My attempts at conquering Haskell come and go - t

相关标签:
3条回答
  • 2021-01-30 08:54
    (f . g) x = f (g x)
    

    This is true. You concluded from that that

    (f . g) x y = f (g x y)
    

    must also be true, but that is not the case. In fact, the following is true:

    (f . g) x y = f (g x) y
    

    which is not the same.

    Why is this true? Well (f . g) x y is the same as ((f . g) x) y and since we know that (f . g) x = f (g x) we can reduce that to (f (g x)) y, which is again the same as f (g x) y.

    So (concatMap . ins) 1 [[2,3]] is equivalent to concatMap (ins 1) [[2,3]]. There is no magic going on here.

    Another way to approach this is via the types:

    . has the type (b -> c) -> (a -> b) -> a -> c, concatMap has the type (x -> [y]) -> [x] -> [y], ins has the type t -> [t] -> [[t]]. So if we use concatMap as the b -> c argument and ins as the a -> b argument, then a becomes t, b becomes [t] -> [[t]] and c becomes [[t]] -> [[t]] (with x = [t] and y = [t]).

    So the type of concatMap . ins is t -> [[t]] -> [[t]], which means a function taking a whatever and a list of lists (of whatevers) and returning a list of lists (of the same type).

    0 讨论(0)
  • 2021-01-30 09:03

    I'd like to add my two cents. The question and answer make it sound like . is some magical operator that does strange things with re-arranging function calls. That's not the case. . is just function composition. Here's an implementation in Python:

    def dot(f, g):
        def result(arg):
            return f(g(arg))
        return result
    

    It just creates a new function which applies g to an argument, applies f to the result, and returns the result of applying f.

    So (concatMap . ins) 1 [[2, 3]] is saying: create a function, concatMap . ins, and apply it to the arguments 1 and [[2, 3]]. When you do concatMap (ins 1 [[2,3]]) you're instead saying, apply the function concatMap to the result of applying ins to 1 and [[2, 3]] - completely different, as you figured out by Haskell's horrendous error message.

    UPDATE: To stress this even further. You said that (f . g) x was another syntax for f (g x). This is wrong! . is just a function, as functions can have non-alpha-numeric names (>><, .., etc., could also be function names).

    0 讨论(0)
  • 2021-01-30 09:11

    You're overthinking this problem. You can work it all out using simple equational reasoning. Let's try it from scratch:

    permute = foldr (concatMap . ins) [[]]
    

    This can be converted trivially to:

    permute lst = foldr (concatMap . ins) [[]] lst
    

    concatMap can be defined as:

    concatMap f lst = concat (map f lst)
    

    The way foldr works on a list is that (for instance):

    -- let lst = [x, y, z]
    foldr f init lst
    = foldr f init [x, y, z]
    = foldr f init (x : y : z : [])
    = f x (f y (f z init))
    

    So something like

    permute [1, 2, 3]
    

    becomes:

    foldr (concatMap . ins) [[]] [1, 2, 3]
    = (concatMap . ins) 1 
        ((concatMap . ins) 2
           ((concatMap . ins) 3 [[]]))
    

    Let's work through the first expression:

    (concatMap . ins) 3 [[]]
    = (\x -> concatMap (ins x)) 3 [[]]  -- definition of (.)
    = (concatMap (ins 3)) [[]]
    = concatMap (ins 3) [[]]     -- parens are unnecessary
    = concat (map (ins 3) [[]])  -- definition of concatMap
    

    Now ins 3 [] == [3], so

    map (ins 3) [[]] == (ins 3 []) : []  -- definition of map
    = [3] : []
    = [[3]]
    

    So our original expression becomes:

    foldr (concatMap . ins) [[]] [1, 2, 3]
    = (concatMap . ins) 1 
        ((concatMap . ins) 2
           ((concatMap . ins) 3 [[]]))
    = (concatMap . ins) 1 
        ((concatMap . ins) 2 [[3]]
    

    Let's work through

    (concatMap . ins) 2 [[3]]
    = (\x -> concatMap (ins x)) 2 [[3]]
    = (concatMap (ins 2)) [[3]]
    = concatMap (ins 2) [[3]]     -- parens are unnecessary
    = concat (map (ins 2) [[3]])  -- definition of concatMap
    = concat (ins 2 [3] : [])
    = concat ([[2, 3], [3, 2]] : [])
    = concat [[[2, 3], [3, 2]]]
    = [[2, 3], [3, 2]]
    

    So our original expression becomes:

    foldr (concatMap . ins) [[]] [1, 2, 3]
    = (concatMap . ins) 1 [[2, 3], [3, 2]]
    = (\x -> concatMap (ins x)) 1 [[2, 3], [3, 2]]
    = concatMap (ins 1) [[2, 3], [3, 2]]
    = concat (map (ins 1) [[2, 3], [3, 2]])
    = concat [ins 1 [2, 3], ins 1 [3, 2]] -- definition of map
    = concat [[[1, 2, 3], [2, 1, 3], [2, 3, 1]], 
              [[1, 3, 2], [3, 1, 2], [3, 2, 1]]]  -- defn of ins
    = [[1, 2, 3], [2, 1, 3], [2, 3, 1], 
       [1, 3, 2], [3, 1, 2], [3, 2, 1]]
    

    Nothing magical here. I think you may have been confused because it's easy to assume that concatMap = concat . map, but this is not the case. Similarly, it may seem like concatMap f = concat . (map f), but this isn't true either. Equational reasoning will show you why.

    0 讨论(0)
提交回复
热议问题