In addition to the @Souperman explanation I want to share my investigation on this topic
Let's take already known example from the fp-ts documentation.
We have an input variable of type IOEtiher
const input: IE.IOEither<Error, string> = IE.right('foo')
and function, which take a plain string and returns E.Either
function parse(s: string): E.Either<Error, number> {
// implentation
}
If we want to make this code works together in a fp-ts style, we need to introduce a pipe. pipe is a function which passes our data through the functions listed inside the pipe.
So, instead of doing this (imperative style)
const input: IE.IOEither<Error, string> = IE.right('foo')
const value = input()
let result: E.Either<Error, number>
if (value._tag === 'Right') {
result = parse(value.right) // where value.right is our 'foo'
}
We can do this
pipe(
input,
IE.chain(inputValue => parse(inputValue))
~~~~~~~~~~~~~~~~~ <- Error is shown
)
Error message
Type 'Either<Error, number>' is not assignable to type 'IOEither<Error, unknown>'.
Unfortunately, fp-ts cannot implicitly jump between types (e.g. from IOEither to Either) . In our example, we started with input variable which has IOEither (IE shortened) type and continue with a IE.chain method which tries to return Either value.
To make it work we can introduce a function which helps us to convert this types.
pipe(
input,
IE.chain(inputValue => IE.fromEitherK(parse)(inputValue))
)
Now our chain function explicitly know that parse function was converted from Either type to IOEither by using fromEitherK.
At this moment we can see that fromEitherK is a helper function that expects a Kleisli function in its arguments and return the new function with a new return type.
To make it more clear, we needn't to use a K suffix if, for example, our parse was a value (instead of function).
The code would look like
pipe(
input,
IE.chain(inputValue => IE.fromEither(parsed)) // I know this is useless code, but it shows its purpose
)
Returning back to our example. We can improve the code to make it more readable
Instead of this
pipe(
input,
IE.chain(inputValue => IE.fromEitherK(parse)(inputValue))
)
We can do this
pipe(
input,
IE.chain(IE.fromEitherK(parse))
)
And even more
pipe(
input,
IE.chainEitherK(parse)
)
Summary
As far I understand, a Kleisli arrows are the functions which take an argument and return a result wrapped in a container (like parse function).