Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Experiment / document generic lambda types further with HKTs. (e.g. <X>(x: X) => X) #78

Open
daniel-nagy opened this issue Dec 18, 2023 · 2 comments
Labels

Comments

@daniel-nagy
Copy link

Hi,

This is not an issue, so feel free to close, but I had a question about mapping over a function's input and output at the type level, specifically for generic functions.

It seems that it is not possible to map over a function's input and output types while propagating type parameters, using this trick, and I was wondering if you had thought about this use case at all. For example, consider a HKT that takes a function and returns the same function except the output is wrapped in a Promise.

import { $, Function, Kind, Type } from "hkt-toolbelt";

interface ReturnsPromise extends Kind.Kind {
  f(
    x: Type._$cast<this[Kind._], Function.Function>
  ): typeof x extends (...a: infer A) => infer R
    ? (...a: A) => Promise<Awaited<R>>
    : never;
}

type Test = $<ReturnsPromise, <U>(a: string, b: U) => 12>

In this example the type parameters will be inferred as unknown. This is possible at the value level.

declare function returnsPromise<A extends unknown[], R>(x: (...a: A) => R): (...a: A) => Promise<Awaited<R>>

const test = returnsPromise(<U>(a: string, b: U) => 12);

Thanks for taking the time to entertain my question, this is a very interesting concept!

@poteat
Copy link
Owner

poteat commented Dec 21, 2023

I totally don't mind answering this. It seems you are making the assertion that a certain HKT cannot be constructed. The HKT you are interested in is an HKT which has the following behavior:

$<Promisify, () => void> // () => Promise<void>
$<Promisify, (x: number) => number> // (x: number) => Promise<number>

To my knowledge this is trivially accomplished via first defining _$promisify, and then currying it with the standard HKT approach. I'll try implementing it myself and respond separately on the result.

@poteat
Copy link
Owner

poteat commented Dec 21, 2023

This is indeed trivial as implemented here: https://tsplay.dev/wOXL7N

Posting the actual block as well to avoid bit rot.

import * as H from "hkt-toolbelt";

type _$promisify<T> = T extends (...args: infer Args) => infer R ? (...args: Args) => Promise<R> : never;
interface Promisify extends H.Kind.Kind { f(x: this[H.Kind._]): _$promisify<typeof x> }

You should see that this representation works fine. You don't even need a separate _$promisify, this is just something I do in this repo to keep things organized and efficient.

This also works:

import * as H from "hkt-toolbelt";

interface Promisify extends H.Kind.Kind {
    f(x: this[H.Kind._]): typeof x extends (...args: infer Args) => infer R ? (...args: Args) => Promise<R> : never;
}

I realize this doesn't implement the full complexity of your code above; indeed, the reason why your code doesn't work is because you are passing in a generic function type <U>(a: string, b: U) => 12. There are indeed limitations when working with these types. To my knowledge, as soon as you "loop" over these parameters, the U will be collapsed to unknown. I would say that we should have tests for these types and also document what happens when you pass them in to certain utilities.

@poteat poteat closed this as completed Dec 21, 2023
@poteat poteat changed the title [Question] HKT that Maps Function Input and Output [Suggestion]: Experiment / document generic lambda types further with HKTs. (e.g. <X>(x: X) => X) Dec 21, 2023
@poteat poteat reopened this Dec 21, 2023
@poteat poteat changed the title [Suggestion]: Experiment / document generic lambda types further with HKTs. (e.g. <X>(x: X) => X) Idea: Experiment / document generic lambda types further with HKTs. (e.g. <X>(x: X) => X) Dec 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants