Haskell: how to infer the type of an expression manually

后端 未结 5 886
难免孤独
难免孤独 2020-12-05 15:58

Given ist the Haskell function:

head . filter fst

The question is now how to find the type \"manually\" by hand. If I let Haskell tell me t

5条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-05 16:11

    Let's start with (.). It's type signature is

    (.) :: (b -> c) -> (a -> b) -> a -> c
    

    which says "given a function from b to c, and a function from a to b, and an a, I can give you a b". We want to use that with head and filter fst, so`:

    (.) :: (b -> c) -> (a -> b) -> a -> c
           ^^^^^^^^    ^^^^^^^^
             head     filter fst
    

    Now head, which is a function from an array of something to a single something. So now we know that b is going to be an array, and c is going to be an element of that array. So for the purpose of our expression, we can think of (.) as having the signature:

    (.) :: ([d] -> d) -> (a -> [d]) -> a -> d -- Equation (1)
                         ^^^^^^^^^^
                         filter fst
    

    The signature for filter is:

    filter :: (e -> Bool) -> [e] -> [e] -- Equation (2)
              ^^^^^^^^^^^
                  fst
    

    (Note that I've changed the name of the type variable to avoid confusion with the as that we already have!) This says "Given a function from e to a Bool, and a list of es, I can give you a list of es". The function fst has the signature:

    fst :: (f, g) -> f
    

    says, "given a pair containing an f and a g, I can give you an f". Comparing this with Equation 2, we know that e is going to be a pair of values, the first element of which must be a Bool. So in our expression, we can think of filter as having the signature:

    filter :: ((Bool, g) -> Bool) -> [(Bool, g)] -> [(Bool, g)]
    

    (All I've done here is to replace e with (Bool, g) in Equation 2.) And the expression filter fst has the type:

    filter fst :: [(Bool, g)] -> [(Bool, g)]
    

    Going back to Equation 1, we can see that (a -> [d]) must now be [(Bool, g)] -> [(Bool, g)], so a must be [(Bool, g)] and d must be (Bool, g). So in our expression, we can think of (.) as having the signature:

    (.) :: ([(Bool, g)] -> (Bool, g)) -> ([(Bool, g)] -> [(Bool, g)]) -> [(Bool, g)] -> (Bool, g)
    

    To summarise:

    (.) :: ([(Bool, g)] -> (Bool, g)) -> ([(Bool, g)] -> [(Bool, g)]) -> [(Bool, g)] -> (Bool, g)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    head                         filter fst
    head :: [(Bool, g)] -> (Bool, g)
    filter fst :: [(Bool, g)] -> [(Bool, g)]
    

    Putting it all together:

    head . filter fst :: [(Bool, g)] -> (Bool, g)
    

    Which is equivalent to what you had, except that I've used g as the type variable rather than b.

    This probably all sounds very complicated, because I described it in gory detail. However, this sort of reasoning quickly becomes second nature and you can do it in your head.

提交回复
热议问题