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

[RFC] runtime match with type #1348

Open
sunrabbit123 opened this issue Nov 4, 2024 · 5 comments
Open

[RFC] runtime match with type #1348

sunrabbit123 opened this issue Nov 4, 2024 · 5 comments
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@sunrabbit123
Copy link
Contributor

sunrabbit123 commented Nov 4, 2024

Feature Request

typia has been used so far to verify types or to give random values.

And now I think I can manage the work after the verification, not the verification.

ts-pattern and pattycake resemble a pattern match of rust or c#, for which we take the pattern as a function factor value.
In the case of typia, you can take the type as a factor and convert it into an if statement in advance at compilation time.

You just have to write the verification syntax into a type and give it to the user how to handle the verified type

How to use?

just write match

type ValidateMatchFunction<T, F> = F extends (arg: infer P) => any
  ? P extends T
    ? F
    : never
  : never;

function match<
  const T,
  MatchFns extends ((arg: any) => any)[],
  ReturnTypes extends MatchFns[number] extends (...args: any[]) => infer R
    ? R
    : never,
>(
  input: T,
  ...fns: {
    [K in keyof MatchFns]: ValidateMatchFunction<T, MatchFns[K]>;
  }
): ReturnTypes | undefined;


const result = match(1 as 1 | 2 | 3 | 4,
	(src: 1) => "one",
	(src: 2 | 3) => "two or three"
);

result;
// ^? const result: "one" | "two or three" | undefined

const result2 = matchAssert(4 as 1 | 2 | 3 | 4,
	(src: 1) => "one",
	(src: 2 | 3) => "two or three"
);
// expected assertsion error!

After compilation, it changes to a native conditional statement.

Similar library?

  1. Effect.match

    import { Match } from "effect"
    
    // Create a Matcher for objects with properties 'a' or 'b'
    //
    //      ┌─── (u: { a: number; } | { b: string; }) => string | number
    //      ▼
    const match = Match.type<{ a: number } | { b: string }>().pipe(
      // Match an object with a numeric property 'a'
      Match.when({ a: Match.number }, (_) => _.a),
      // Match an object with a string property 'b'
      Match.when({ b: Match.string }, (_) => _.b),
      // Ensure all cases are covered
      Match.exhaustive
    )
    
    console.log(match({ a: 0 }))
    // Output: 0
    
    console.log(match({ b: "hello" }))
    // Output: "hello"
  2. ts-pattern.match

    import { match, P } from 'ts-pattern';
    
    type Data =
      | { type: 'text'; content: string }
      | { type: 'img'; src: string };
    
    type Result =
      | { type: 'ok'; data: Data }
      | { type: 'error'; error: Error };
    
    const result: Result = ...;
    
    const html = match(result)
      .with({ type: 'error' }, () => <p>Oups! An error occured</p>)
      .with({ type: 'ok', data: { type: 'text' } }, (res) => <p>{res.data.content}</p>)
      .with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => <img src={src} />)
      .exhaustive();
  3. pattycake.match

    let html = match(result)
      .with(
        { type: 'error', error: { foo: [1, 2] }, nice: '' },
        () => '<p>Oups! An error occured</p>',
      )
      .with({ type: 'ok', data: { type: 'text' } }, function (data) {
        return '<p>420</p>';
      })
      .with(
        { type: 'ok', data: { type: 'img', src: 'hi' } },
        (src) => `<img src=${src} />`,
      )
      .otherwise(() => 'idk bro');
      
    // compiled
    let html;
    out: {
      if (
        result.type === 'error' &&
        Array.isArray(result.error.foo) &&
        result.error.foo.length >= 2 &&
        result.error.foo[0] === 1 &&
        result.error.foo[1] === 2
      ) {
        html = '<p>Oups! An error occured</p>';
        break out;
      }
      if (result.type === 'ok' && result.data.type === 'text') {
        let data = result;
        html = '<p>420</p>';
        break out;
      }
      if (
        result.type === 'ok' &&
        result.data.type === 'img' &&
        result.data.src === 'hi'
      ) {
        let src = result;
        html = `<img src=${src} />`;
        break out;
      }
      html = 'idk bro';
      break out;
    }

Of the following, only patty-cake is changes native at compilation time.

but the form will be very different from typia.match.

This is because typia can use pre-declared types so that you don't have to rewrite the match case.

@sunrabbit123
Copy link
Contributor Author

If I have a chance, I'd like to work on it.
I don't know how long it will take

@samchon
Copy link
Owner

samchon commented Nov 5, 2024

You can start from the v7.0 branch, and typia.functional module would be helpful.

Also, instead of making both matches and asserMatches functions at the same time, I think the last callback function should be the error handler. If the last function be skipped, it throws error.

If your contribution comes after v7 release, this feature would come in v7.1 minor update.

https://github.com/samchon/typia/tree/v7.0/src/programmers/functional

@samchon samchon added enhancement New feature or request good first issue Good for newcomers labels Nov 5, 2024
@sunrabbit123
Copy link
Contributor Author

sunrabbit123 commented Nov 11, 2024

oh.. thanks!
Are you talking about this interface?

const result = match(1 as 1 | 2 | 3 | 4,
	(src: 1) => "one",
	(src: 2 | 3) => "two or three",
        (err: UnmatchedError) => e.toString()
);

result;
// ^? const result: "one" | "two or three" | string

const result2 = matchAssert(4 as 1 | 2 | 3 | 4,
	(src: 1) => "one",
	(src: 2 | 3) => "two or three"
);
// expected assertsion error!

@samchon
Copy link
Owner

samchon commented Nov 15, 2024

Yes it is. I think IValidation.IFailure would be proper.

@kakasoo
Copy link
Contributor

kakasoo commented Nov 24, 2024

It's a feature that looks like a lot of fun! I will use this function very often if it is added. This feature is also larger than other libraries. ( But it can also confuse a lot of people when it's an object type. If there are people who can't distinguish subtypes. )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
Status: No status
Development

No branches or pull requests

3 participants