| Name | # | Haskell | Ramda | Sanctuary | Signature |
|---|---|---|---|---|---|
| identity | I | id |
identity |
I |
a → a |
| constant | K | const |
always |
K |
a → b → a |
| apply | A | ($) |
call |
I¹ |
(a → b) → a → b |
| thrush | T | (&) |
applyTo |
T |
a → (a → b) → b |
| duplication | W | join² |
unnest² |
join² |
(a → a → b) → a → b |
| flip | C | flip |
flip |
flip |
(a → b → c) → b → a → c |
| compose | B | (.), fmap² |
map² |
compose, map² |
(b → c) → (a → b) → a → c |
| substitution | S | (<*>)² |
ap² |
ap² |
(a → b → c) → (a → b) → a → c |
| chain | S_³ | (=<<)² |
chain² |
chain² |
(a → b → c) → (b → a) → b → c |
| converge | S2³ | apply2way, liftA2², liftM2² |
lift2² |
(b → c → d) → (a → b) → (a → c) → a → d |
|
| psi | P | on |
on |
on |
(b → b → c) → (a → b) → a → a → c |
| fix-point⁴ | Y | fix |
(a → a) → a |
¹) The A-combinator can be implemented as an alias of the I-combinator. Its implementation in Haskell exists because the infix nature gives it some utility. Its implementation in Ramda exists because it is overloaded with additional functionality.
²) Algebras like ap have different implementations for different types.
They work like Function combinators only for Function inputs.
³) I could not find a consistent name for these combinators, but they are common enough in the JavaScript ecosystem to justify their inclusion. I named them myself in order to refer to their implementation.
⁴) In JavaScript and other non-lazy languages, it is impossible to
implement the Y-combinator. Instead a variant known as the applicative or
strict fix-point combinator is implemented. This variant is sometimes
rererred to as the Z-combinator. The implementation found in combinators.js
is the strictly evaluated "Z" combinator, which needs the extra wrapper
around g (g) on the right hand side.
I'm going to assume you meant
(f -> a -> a) -> a -> a. You missed an extraa -> ...for input you're giving inYFact (4). Under that assumption, you are almost correct!There's a little thing missing though. You specified
fin that signature, but what isf? You're implying it's a function by using the letterf, but you haven't given that function a signature. What is the signature off? In your factorial example it's thefactfunction, so let's go with that. You calledfactonn - 1(which is a member ofa) and it produced anothera(we know that, because you're multiplying its result). So the signature offis actuallya -> a! Let's update our whole signature:Interestingly, we see a pattern arise:
(a -> a)occurs three times! We can see that more clearly if I add the implicit braces explicitly. I'll also give the three functions some names so we can talk about them.I'll add names to your example as well, to make it easier to talk about:
The
recurfunction is what you call to trigger the recursion. But recursion into what? Well, into theoutputfunction. And then thefixedfunction is what's returned from the whole thing.Now why did
recurandfixedhave the shapea -> a? It's becauseoutputhas that shape! Therecurfunction just calls back intooutput, so it has the same shape asoutput. Andfixedhas the same shape asoutputtoo, becauseYis just returning youroutput.You already discovered that you can change the shape of
outputwith your definition ofYPlus, and thatrecurandfixedjust followed:That's your second example, updated to include the names. You updated
outputto take two arguments, and consequentlyrecurandfixedalso took two arguments. TheYfunction now has the following signature:Where did that third
-> acome from?! From your ownoutputfunction! Can we also reduce the number of-> as by not returning a function at all?Yep! 😄 In this last example, the signature of
Yis:And that signature actually works for all the other examples too, because
a -> a,a -> a -> a, or any other Function or value are all members ofa, so long as they are consistent throughout the type signature. So the least restrictive signature for theYfunction is(a -> a) -> a🎉The signature
(a -> a) -> abasically captures the pattern we saw, that whateverayou're returning froma -> a(even if it's a function) is the sameathat you get as input, and the sameathat is returned as final output.