Share via


Subtipo e variação

Q# suporta apenas alguns mecanismos de conversão. As conversões implícitas só podem ocorrer ao aplicar operadores binários, avaliar expressões condicionais ou construir um literal de matriz. Nestes casos, é determinado um supertipo comum e as conversões necessárias são executadas automaticamente. Além destas conversões implícitas, as conversões explícitas através de chamadas de função são possíveis e muitas vezes necessárias.

Atualmente, a única relação de subtipagem que existe aplica-se às operações. Intuitivamente, faz sentido que se possa substituir uma operação que suporte mais do que o conjunto de functores necessário. Concretamente, para qualquer dois tipos de TIn betão e TOut, a relação de subtipagem é

    (TIn => TOut) :>
    (TIn => TOut is Adj), (TIn => TOut is Ctl) :>
    (TIn => TOut is Adj + Ctl)

indica A :> B que B é um subtipo de A. Formulado de forma diferente, B é mais restritivo do A que tal que um valor de tipo B pode ser utilizado onde quer que seja necessário um valor de tipo A . Se um callable depender de um argumento (item) do tipo A, um argumento do tipo B pode ser substituído com segurança, uma vez que, se fornecer todas as capacidades necessárias.

Este tipo de polimorfismo estende-se a cadeias de identificação na medida em que uma cadeia de identificação do tipo B é um subtipo de um tipo A de cadeia de identificação se contiver o mesmo número de itens e o tipo de cada item for um subtipo do tipo de item correspondente em A. Isto é conhecido como subttipagem de profundidade. Atualmente, não existe suporte para subtipagem de largura, ou seja, não existe qualquer relação de subtipo entre dois tipos definidos pelo utilizador ou um tipo definido pelo utilizador e qualquer tipo incorporado. A existência do unwrap operador, que lhe permite extrair uma cadeia de identificação que contém todos os itens nomeados e anónimos, impede esta situação.

Nota

No que diz respeito aos callables, se um callable processar um argumento do tipo A, também é capaz de processar um argumento do tipo B. Se um callable for transmitido como um argumento para outro callable, tem de ser capaz de processar tudo o que a assinatura de tipo possa exigir. Isto significa que, se o callable precisar de ser capaz de processar um argumento do tipo B, qualquer callable capaz de processar um argumento de tipo A mais geral pode ser transmitido com segurança. Por outro lado, esperamos que, se exigirmos que o callable transmitido devolva um valor do tipo A, a promessa de devolver um valor do tipo B é suficiente, uma vez que esse valor irá fornecer todas as capacidades necessárias.

O tipo de função ou operação é contravariant no respetivo tipo de argumento e covarianto no tipo de retorno. A :> B portanto, implica que para qualquer tipo T1de concreto ,

    (B → T1) :> (A → T1), and
    (T1 → A) :> (T1 → B) 

aqui pode significar uma função ou operação e omitimos todas as anotações para características. A(B → T2) Substituir por e(T2 → A), respectivamente, e substituir (A → T2)B por e (T2 → B) respectivamente, leva à conclusão de que, para qualquer tipo T2concreto,

    ((A → T2) → T1) :> ((B → T2) → T1), and
    ((T2 → B) → T1) :> ((T2 → A) → T1), and
    (T1 → (B → T2)) :> (T1 → (A → T2)), and
    (T1 → (T2 → A)) :> (T1 → (T2 → B)) 

Por indução, cada indirecção adicional inverte a variância do tipo de argumento e deixa a variância do tipo de retorno inalterada.

Nota

Isto também deixa claro qual é o comportamento de variância das matrizes; A obtenção de itens através de um operador de acesso de item corresponde à invocação de uma função do tipo (Int -> TItem), em que TItem é o tipo de elementos na matriz. Uma vez que esta função é transmitida implicitamente ao transmitir uma matriz, é necessário covarianar as matrizes no tipo de item. As mesmas considerações também se mantêm para cadeias de identificação, que são imutáveis e, portanto, covariantas em relação a cada tipo de item. Se as matrizes não fossem imutáveis, a existência de uma construção que lhe permitiria definir itens numa matriz e, portanto, utilizar um argumento do tipo TItem, implicaria que as matrizes também precisam de ser contravariadas. A única opção para tipos de dados que suportam a obtenção e definição de itens é, portanto, ser invariável, o que significa que não existe qualquer relação de subtipagem; B[]não é um subtipo de A[] mesmo que B seja um subtipo de A. Apesar de as matrizes em Q# serem imutáveis, são invariáveis em vez de covariantas. Isto significa, por exemplo, que um valor do tipo (Qubit => Unit is Adj)[] não pode ser transmitido para um callable que requer um argumento do tipo (Qubit => Unit)[]. Manter matrizes invariáveis permite uma maior flexibilidade relacionada com a forma como as matrizes são processadas e otimizadas no runtime, mas poderá ser possível revê-la no futuro.