I'll give a brief answer here, but a more complete one is spread across two answers to a similar question, which in turn was taken from the Ramda wiki page. (Disclaimer: I'm the author of that page and one of the principals in Ramda itself.)
This is broken into two parts:
Functor f => (a → b) → f a → f b
Before the fat arrow (=>
) we have constraints on the remainder. The single constraint in this example is that the variable f
must be a Functor
. A Functor is a type whose members have a map
method which obeys certain laws. And the declaration is parameterized over another type, so we don't write just f
but f String
, f Number
, or more generically, f a
for some unknown type a
.
The skinny arrow (->
) is an abbreviation for the type Function. So instead of writing
Function x y
we can instead write
x -> y
or when needed to avoid ambiguity.
(x -> y)
Putting these together, we can note that in R.map(double, [1, 2, 3])
, we have a function (double
) from Number
to Number
, which means that our a
and b
are both Number
. And our functor is Array
. So specializing the definitions with these types, we have map
accepting a function from Number
to Number
, and returning a function that takes an array of Number
s and returns a new array of Number
s. (That's because in this system, ->
binds to the right, so (a -> b -> c)
is equivalent to (a -> (b -> c))
. In Ramda, all functions are curried in such a way that you can call them with any initial set of parameters, and until all the terms have been supplied, you continue to get back functions. Thus with Ramda functions there is no real difference between R.map(double)([1, 2, 3])
and R.map(double, [1, 2, 3])
.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…