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

Variance misunderstanding [Question] #5252

Closed
klimick opened this issue Feb 19, 2021 · 7 comments
Closed

Variance misunderstanding [Question] #5252

klimick opened this issue Feb 19, 2021 · 7 comments

Comments

@klimick
Copy link
Contributor

klimick commented Feb 19, 2021

Could you please give an explanation why I cannot do this?

https://psalm.dev/r/9881859823

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/9881859823
<?php

/**
 * @template-covariant A
 * @template-covariant B
 * @psalm-immutable
 */
final class Type {
    /**
     * @template C
     *
     * @param Type<B, C> $type
     * @return Type<A, C>
     */
    public function compose(Type $type): Type
    {
        throw new \RuntimeException('???');
    }
}


/** @var Type<mixed, positive-int> $fromMixedToPositiveInt */
$_ = null;

/** @var Type<int, stdClass> $fromIntToStdClass */
$_ = null;

$fromMixedToPositiveInt->compose($fromIntToStdClass);
Psalm output (using commit e93e532):

ERROR: ArgumentTypeCoercion - 28:34 - Argument 1 of Type::compose expects Type<positive-int, mixed>, parent type Type<int, stdClass> provided

@muglug
Copy link
Collaborator

muglug commented Feb 24, 2021

Argument 1 of Type::compose expects Type<positive-int, mixed>, parent type Type<int, stdClass> provided

positive-int is a subtype of int. It's coercion in the same way passing object to something that expects Exception is coercion.

@muglug muglug closed this as completed Feb 24, 2021
@klimick
Copy link
Contributor Author

klimick commented Feb 24, 2021

@muglug It seems I asked an imprecise question and spent your time. Sorry.

I tried to implement the simple idea of function composition. But for types.
I think is not possible because psalm does not have contravariant templates:

/**
 * @template-contravariant A
 * @template-covariant B
 * @psalm-immutable
 */
final class Type {...}

Look to this snippet: https://psalm.dev/r/07db7bc8f9

Please, tell me. Why composition can be implemented with Closure (i treat it as workaround)
Because parameters of Closure is contravariant by default?
Why psalm does not have contravariant templates?

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/07db7bc8f9
<?php


/**
 * @template A
 * @template-covariant B
 * @psalm-immutable
 */
final class Type
{
    /**
     * @template C
     *
     * @param Closure(B): C $bc
     * @return Type<A, C>
     */
    public function composeWithClosure(Closure $bc): Type
    {
        throw new RuntimeException('???');
    }

    /**
     * @template C
     *
     * @param Type<B, C> $bc
     * @return Type<A, C>
     */
    public function composeWithFunc(Type $bc): Type
    {
        throw new RuntimeException('???');
    }
}

/** @var Type<mixed, positive-int> $func1 */
$func1 = null;

/** @var Type<int, string> $func2 */
$func2 = null;

$closure =
    /**
     * @param int $_
     * @return string
     */
    fn(int $_) => (string) $_;

// $closure AND $func2 semantically the same

// hacky-composition possible with Closure
$work = $func1->composeWithClosure($closure);

// But regular composition Func<A, B> and Func<B, C> impossible
$doesNotWork = $func1->composeWithFunc($func2);
Psalm output (using commit f8cbb22):

ERROR: ArgumentTypeCoercion - 53:40 - Argument 1 of Type::composeWithFunc expects Type<positive-int, mixed>, parent type Type<int, string> provided

@klimick
Copy link
Contributor Author

klimick commented Feb 24, 2021

Sorry. Snippet above may be confused. Use that https://psalm.dev/r/2e6727b92a

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/2e6727b92a
<?php


/**
 * @template A
 * @template-covariant B
 * @psalm-immutable
 */
final class Type
{
    /**
     * @template C
     *
     * @param Closure(B): C $bc
     * @return Type<A, C>
     */
    public function composeWithClosure(Closure $bc): Type
    {
        throw new RuntimeException('???');
    }

    /**
     * @template C
     *
     * @param Type<B, C> $bc
     * @return Type<A, C>
     */
    public function composeWithType(Type $bc): Type
    {
        throw new RuntimeException('???');
    }
}

/** @var Type<mixed, positive-int> $func1 */
$func1 = null;

/** @var Type<int, string> $func2 */
$func2 = null;

$closure =
    /**
     * @param int $_
     * @return string
     */
    fn(int $_) => (string) $_;

// $closure AND $func2 semantically the same

// hacky-composition possible with Closure
$work = $func1->composeWithClosure($closure);

// But regular composition Type<A, B> and Type<B, C> impossible
$doesNotWork = $func1->composeWithType($func2);
Psalm output (using commit f8cbb22):

ERROR: ArgumentTypeCoercion - 53:40 - Argument 1 of Type::composeWithType expects Type<positive-int, mixed>, parent type Type<int, string> provided

@muglug
Copy link
Collaborator

muglug commented Feb 24, 2021

Why psalm does not have contravariant templates?

Ah, because I don't think they're commonly-used. Feel free to create a PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants