If first parameter is taken by the function first as function application is left-associative, for example:
drop 2 [1,2,3,4]
result: [3,4]
is equivalent to
(drop 2) [1,2,3,4]
same result: [3,4]
Here my question is, if the type signature is right-associative, which means, things in right side evaluate first, in this case, it's gonna be as follows, as the first parameter is first taken by the function:
drop :: [1,2,3,4] -> (2 -> [3,4])
It shouldn't have been as follows, right?
drop :: Int -> ([a] -> [a])
drop :: 2 -> ([1,2,3,4] -> [3,4])
So, why does it take the second parameter first in type signature of the function instead of the first parameter?
In additon, if the second parameter is evaluated prior to first parameter, then why is the following usage invalid?
(drop [1,2,3,4]) 2
CodePudding user response:
It's nonsensical to write things like drop :: [1,2,3,4] -> (2 -> [3,4])
or 2 -> ([1,2,3,4] -> [3,4])
– you're mixing type-level and value-level notations there. What you should instead do is look at local types of subexpressions:
drop 2 [1,2,3,4]
└─┬┘ ┊ └──┬────┘
┌─┴───────────────┐ ┌─┴─┐ ┌──┴──┐
│Int->[Int]->[Int]│ │Int│ │[Int]│
└─────────────────┘ └───┘ └─────┘
Add the implied parentheses
(drop 2) [1,2,3,4]
└┬─┘ ┊ └──┬────┘
┌─┴─────────────────┐ ┌─┴─┐ ┌──┴──┐
│Int->([Int]->[Int])│ │Int│ │[Int]│
└───────────────────┘ └───┘ └─────┘
Now the subexpression drop 2
means you're applying the argument 2
as the first argument of the function drop
, i.e. as the Int
in its signature. For the whole drop 2
expression, this argument has therefore vanished:
( drop 2 ) [1,2,3,4]
┊ ┌──────────┴────────┐ ┌─┴─┐ ┊ └──┬────┘
┊ │Int->([Int]->[Int])│ │Int│ ┊ ┊
┊ └───────────────────┘ └───┘ ┊ ┊
└────────────┬─────────────────┘ ┌──┴──┐
┌────┴───────┐ │[Int]│
│[Int]->[Int]│ └─────┘
└────────────┘
This is analogous to applying the single-argument function length :: [Bool] -> Int
to the single argument [False,True] :: [Bool]
to get the result length [False,True] ≡ (2::Int)
. The fact that for drop
the result has type ([Int]->[Int])
instead of something “atomic” like Int
is irrelevant at this stage.
Then on the outer level, you're applying the function of type [Int]->[Int]
to the argument of type [Int]
, which is perfectly sensible. The whole thing then has simply result type [Int]
.
( ( drop 2 ) [1,2,3,4] )
┊ ┊ ┌──────────┴────────┐ ┌─┴─┐ ┊ └──┬────┘ ┊
┊ ┊ │Int->([Int]->[Int])│ │Int│ ┊ ┊ ┊
┊ ┊ └───────────────────┘ └───┘ ┊ ┊ ┊
┊ └────────────┬─────────────────┘ ┊ ┊
┊ ┌────┴───────┐ ┌──┴──┐ ┊
┊ │[Int]->[Int]│ │[Int]│ ┊
┊ └────────────┘ └─────┘ ┊
└────────────────────────┬────────────────────┘
┌──┴──┐
│[Int]│
└─────┘
CodePudding user response:
I think you misunderstand what right associative means. It indeed means that:
drop :: Int -> [a] -> [a]
is equivalent to:
drop :: Int -> ([a] -> [a])
This thus means that drop
is a function that takes a parameter of type Int
, and then returns a function of type [a] -> [a]
.
But function application itself is left-associative. Indeed:
drop 2 [1,2,3,4]
is short for:
(drop 2) [1,2,3,4]
Here drop 2
will thus return a function of type [a] -> [a]
that will drop the first two items of the list. We then apply [1,2,3,4]
to that function, and thus obtain [3,4]
.